/* Crafted with love by Fueled on Bacon https://fueledonbacon.com */ //SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import '@opengsn/contracts/src/BasePaymaster.sol'; import '@opengsn/contracts/src/utils/GsnTypes.sol'; import '@openzeppelin/contracts/utils/math/Math.sol'; import './interfaces/IVenueSBT.sol'; import './ERC721A.sol'; import './interfaces/IVenueRegistar.sol'; import './interfaces/IEventReviewPaymaster.sol'; contract EventReviewPaymaster is IEventReviewPaymaster, BasePaymaster { uint256 public gasUsedByPost = 60000; uint256 public minGas = 300000; address public target; bool private _pause; mapping(address => uint256) private _balanceByVenue; mapping(address => mapping(uint256 => bool)) private _isTokenUsedByEvent; IVenueRegistar private _venueRegistar; constructor( address venueRegistar, address gsnForwarder, address _target, IRelayHub hub ) { _venueRegistar = IVenueRegistar(venueRegistar); setTrustedForwarder(gsnForwarder); target = _target; setRelayHub(hub); } /// @inheritdoc IPaymaster function versionPaymaster() external view virtual override returns (string memory) { return '3.0.0-beta.1+opengsn.review.ipaymaster'; } /// @inheritdoc IEventReviewPaymaster function setVenueRegistar(address venueRegistar) external virtual override onlyOwner { if (venueRegistar == address(0)) revert ReviewPaymasterWrongVenueRegistar(); _venueRegistar = IVenueRegistar(venueRegistar); } /// @inheritdoc IEventReviewPaymaster function setGasUsedByPost(uint256 _gasUsedByPost) external virtual override onlyOwner { gasUsedByPost = _gasUsedByPost; } /// @inheritdoc IEventReviewPaymaster function setMinGas(uint256 _minGas) external virtual override onlyOwner { minGas = _minGas; } /// @inheritdoc IEventReviewPaymaster function setTarget(address _target) external virtual override onlyOwner { target = _target; } /// @inheritdoc IEventReviewPaymaster function fundVenue(address venue) external virtual override payable { _balanceByVenue[venue] += msg.value; relayHub.depositFor{value:msg.value}(address(this)); } // @inheritdoc IEventReviewPaymaster function withdraw(address payable receiver, uint256 amount) external virtual override { if( _balanceByVenue[_msgSender()] < amount) revert ReviewPaymasterNotEnoughFunds(); _balanceByVenue[_msgSender()] -= amount; relayHub.withdraw(receiver, amount); } /// @inheritdoc IEventReviewPaymaster function getBalance() external view virtual override returns (uint256) { return _balanceByVenue[_msgSender()]; } function _preRelayedCall( GsnTypes.RelayRequest calldata relayRequest, bytes calldata, /*signature*/ bytes calldata, /*approvalData*/ uint256 maxPossibleGas ) internal virtual override returns (bytes memory context, bool revertOnRecipientRevert) { IForwarder.ForwardRequest calldata request = relayRequest.request; if (request.gas < minGas) revert ReviewPaymasterNotEnoughGas(); if (request.to != target) revert ReviewPaymasterUnknownTarget(); (address venue, address _event, uint256 tokenId, uint8 stars) = abi.decode( request.data[4:], (address, address, uint256, uint8) ); if (_isTokenUsedByEvent[_event][tokenId]) revert ReviewPaymasterTokenAlreadyUsed(); if (!_venueRegistar.isVenue(venue)) revert ReviewPaymasterVenueNotRegistered(); if (_balanceByVenue[venue] < _venueRegistar.getMinBalance()) revert ReviewPaymasterVenueNotBalance(); IVenueSBT venueSBT = IVenueSBT(venue); if (!venueSBT.isEvent(_event)) revert ReviewPaymasterEventNotRegistered(); ERC721A nft = ERC721A(_event); if (nft.ownerOf(tokenId) != request.from) revert ReviewPaymasterSenderIsNotTokenOwner(); if(stars > 50) revert ReviewPaymasterWrongNumberOfStars(); GsnTypes.RelayData memory relayData = relayRequest.relayData; uint256 ethPrecharge = relayHub.calculateCharge(maxPossibleGas, relayData); //reverts on underflow _balanceByVenue[venue] - ethPrecharge; return (abi.encode(venue, _event, tokenId), true); } function _postRelayedCall( bytes calldata context, bool success, uint256 gasUseWithoutPost, GsnTypes.RelayData calldata relayData ) internal virtual override { require(success, 'No success'); (address venue, address _event, uint256 tokenId) = abi.decode(context, (address, address, uint256)); IRelayHub _relayHub = relayHub; uint256 ethActualCharge = _relayHub.calculateCharge(gasUseWithoutPost + gasUsedByPost, relayData); _isTokenUsedByEvent[_event][tokenId] = true; _balanceByVenue[venue] -= ethActualCharge; } }