Transaction Hash:
Block:
13357701 at Oct-05-2021 07:28:34 AM +UTC
Transaction Fee:
0.004547184662621304 ETH
$10.66
Gas Used:
56,214 Gas / 80.890608436 Gwei
Emitted Events:
160 |
MagicProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x00000000000000000000000007a1f6fc89223c5ebd4e4ddae89ac97629856a0f, 00000000000000000000000000000000000000000000011393bbfc76723db092 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x07a1f6fc...629856A0f |
3.463250370078575906 Eth
Nonce: 1905
|
3.458703185415954602 Eth
Nonce: 1906
| 0.004547184662621304 | ||
0x99C85bb6...993Cb89E3
Miner
| (BeePool) | 1,006.936248797381040142 Eth | 1,006.936333118381040142 Eth | 0.000084321 | |
0xB0c7a3Ba...91070Ac9A | |||||
0xdCf809BB...cF2555418 |
Execution Trace
ERC721Farm.claimRewards( tokenIds=[6895] )
MagicProxy.40c10f19( )
-
MagicMintReentrancyFix.mint( account=0x07a1f6fc89223c5ebD4e4ddaE89Ac97629856A0f, amount=5083499999999999979666 )
-
claimRewards[ERC721Farm (ln:62)]
min[ERC721Farm (ln:64)]
calculateRewards[ERC721Farm (ln:65)]
contains[ERC721Farm (ln:57)]
min[ERC721Farm (ln:58)]
mint[ERC721Farm (ln:71)]
File 1 of 3: ERC721Farm
File 2 of 3: MagicProxy
File 3 of 3: MagicMintReentrancyFix
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import '@openzeppelin/contracts/utils/math/Math.sol'; import '@solidstate/contracts/token/ERC721/IERC721.sol'; import '@solidstate/contracts/token/ERC721/IERC721Receiver.sol'; import '@solidstate/contracts/utils/EnumerableSet.sol'; import '../token/IMagic.sol'; contract ERC721Farm is IERC721Receiver { using EnumerableSet for EnumerableSet.UintSet; address private immutable MAGIC; address private immutable ERC721_CONTRACT; uint256 public immutable EXPIRATION; uint256 private immutable RATE; mapping(address => EnumerableSet.UintSet) private _deposits; mapping(address => mapping(uint256 => uint256)) public depositBlocks; constructor( address magic, address erc721, uint256 rate, uint256 expiration ) { MAGIC = magic; ERC721_CONTRACT = erc721; RATE = rate; EXPIRATION = block.number + expiration; } function onERC721Received( address, address, uint256, bytes calldata ) external pure override returns (bytes4) { return IERC721Receiver.onERC721Received.selector; } function depositsOf(address account) external view returns (uint256[] memory) { EnumerableSet.UintSet storage depositSet = _deposits[account]; uint256[] memory tokenIds = new uint256[](depositSet.length()); for (uint256 i; i < depositSet.length(); i++) { tokenIds[i] = depositSet.at(i); } return tokenIds; } function calculateRewards(address account, uint256[] memory tokenIds) public view returns (uint256[] memory rewards) { rewards = new uint256[](tokenIds.length); for (uint256 i; i < tokenIds.length; i++) { uint256 tokenId = tokenIds[i]; rewards[i] = RATE * (_deposits[account].contains(tokenId) ? 1 : 0) * (Math.min(block.number, EXPIRATION) - depositBlocks[account][tokenId]); } } function claimRewards(uint256[] calldata tokenIds) public { uint256 reward; uint256 block = Math.min(block.number, EXPIRATION); uint256[] memory rewards = calculateRewards(msg.sender, tokenIds); for (uint256 i; i < tokenIds.length; i++) { reward += rewards[i]; depositBlocks[msg.sender][tokenIds[i]] = block; } if (reward > 0) { IMagic(MAGIC).mint(msg.sender, reward); } } function deposit(uint256[] calldata tokenIds) external { claimRewards(tokenIds); for (uint256 i; i < tokenIds.length; i++) { IERC721(ERC721_CONTRACT).safeTransferFrom( msg.sender, address(this), tokenIds[i], '' ); _deposits[msg.sender].add(tokenIds[i]); } } function withdraw(uint256[] calldata tokenIds) external { claimRewards(tokenIds); for (uint256 i; i < tokenIds.length; i++) { require( _deposits[msg.sender].contains(tokenIds[i]), 'ERC721Farm: token not deposited' ); _deposits[msg.sender].remove(tokenIds[i]); IERC721(ERC721_CONTRACT).safeTransferFrom( address(this), msg.sender, tokenIds[i], '' ); } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a / b + (a % b == 0 ? 0 : 1); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IERC165} from '../../introspection/IERC165.sol'; import {IERC721Internal} from './IERC721Internal.sol'; /** * @notice ERC721 interface * @dev see https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721 is IERC721Internal, IERC165 { /** * @notice query the balance of given address * @return balance quantity of tokens held */ function balanceOf ( address account ) external view returns (uint256 balance); /** * @notice query the owner of given token * @param tokenId token to query * @return owner token owner */ function ownerOf ( uint256 tokenId ) external view returns (address owner); /** * @notice transfer token between given addresses, checking for ERC721Receiver implementation if applicable * @param from sender of token * @param to receiver of token * @param tokenId token id */ function safeTransferFrom ( address from, address to, uint256 tokenId ) external payable; /** * @notice transfer token between given addresses, checking for ERC721Receiver implementation if applicable * @param from sender of token * @param to receiver of token * @param tokenId token id * @param data data payload */ function safeTransferFrom ( address from, address to, uint256 tokenId, bytes calldata data ) external payable; /** * @notice transfer token between given addresses, without checking for ERC721Receiver implementation if applicable * @param from sender of token * @param to receiver of token * @param tokenId token id */ function transferFrom ( address from, address to, uint256 tokenId ) external payable; /** * @notice grant approval to given account to spend token * @param operator address to be approved * @param tokenId token to approve */ function approve ( address operator, uint256 tokenId ) external payable; /** * @notice get approval status for given token * @param tokenId token to query * @return operator address approved to spend token */ function getApproved ( uint256 tokenId ) external view returns (address operator); /** * @notice grant approval to or revoke approval from given account to spend all tokens held by sender * @param operator address to be approved * @param status approval status */ function setApprovalForAll ( address operator, bool status ) external; /** * @notice query approval status of given operator with respect to given address * @param account address to query for approval granted * @param operator address to query for approval received * @return status whether operator is approved to spend tokens held by account */ function isApprovedForAll ( address account, address operator ) external view returns (bool status); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IERC721Receiver { function onERC721Received ( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title Set implementation with enumeration functions * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license) */ library EnumerableSet { struct Set { bytes32[] _values; // 1-indexed to allow 0 to signify nonexistence mapping (bytes32 => uint) _indexes; } struct Bytes32Set { Set _inner; } struct AddressSet { Set _inner; } struct UintSet { Set _inner; } function at ( Bytes32Set storage set, uint index ) internal view returns (bytes32) { return _at(set._inner, index); } function at ( AddressSet storage set, uint index ) internal view returns (address) { return address(uint160(uint(_at(set._inner, index)))); } function at ( UintSet storage set, uint index ) internal view returns (uint) { return uint(_at(set._inner, index)); } function contains ( Bytes32Set storage set, bytes32 value ) internal view returns (bool) { return _contains(set._inner, value); } function contains ( AddressSet storage set, address value ) internal view returns (bool) { return _contains(set._inner, bytes32(uint(uint160(value)))); } function contains ( UintSet storage set, uint value ) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } function indexOf ( Bytes32Set storage set, bytes32 value ) internal view returns (uint) { return _indexOf(set._inner, value); } function indexOf ( AddressSet storage set, address value ) internal view returns (uint) { return _indexOf(set._inner, bytes32(uint(uint160(value)))); } function indexOf ( UintSet storage set, uint value ) internal view returns (uint) { return _indexOf(set._inner, bytes32(value)); } function length ( Bytes32Set storage set ) internal view returns (uint) { return _length(set._inner); } function length ( AddressSet storage set ) internal view returns (uint) { return _length(set._inner); } function length ( UintSet storage set ) internal view returns (uint) { return _length(set._inner); } function add ( Bytes32Set storage set, bytes32 value ) internal returns (bool) { return _add(set._inner, value); } function add ( AddressSet storage set, address value ) internal returns (bool) { return _add(set._inner, bytes32(uint(uint160(value)))); } function add ( UintSet storage set, uint value ) internal returns (bool) { return _add(set._inner, bytes32(value)); } function remove ( Bytes32Set storage set, bytes32 value ) internal returns (bool) { return _remove(set._inner, value); } function remove ( AddressSet storage set, address value ) internal returns (bool) { return _remove(set._inner, bytes32(uint(uint160(value)))); } function remove ( UintSet storage set, uint value ) internal returns (bool) { return _remove(set._inner, bytes32(value)); } function _at ( Set storage set, uint index ) private view returns (bytes32) { require(set._values.length > index, 'EnumerableSet: index out of bounds'); return set._values[index]; } function _contains ( Set storage set, bytes32 value ) private view returns (bool) { return set._indexes[value] != 0; } function _indexOf ( Set storage set, bytes32 value ) private view returns (uint) { unchecked { return set._indexes[value] - 1; } } function _length ( Set storage set ) private view returns (uint) { return set._values.length; } function _add ( Set storage set, bytes32 value ) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); set._indexes[value] = set._values.length; return true; } else { return false; } } function _remove ( Set storage set, bytes32 value ) private returns (bool) { uint valueIndex = set._indexes[value]; if (valueIndex != 0) { uint index = valueIndex - 1; bytes32 last = set._values[set._values.length - 1]; // move last value to now-vacant index set._values[index] = last; set._indexes[last] = index + 1; // clear last index set._values.pop(); delete set._indexes[value]; return true; } else { return false; } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import '@solidstate/contracts/token/ERC20/IERC20.sol'; interface IMagic is IERC20 { function mint(address account, uint256 amount) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title ERC165 interface registration interface * @dev see https://eips.ethereum.org/EIPS/eip-165 */ interface IERC165 { /** * @notice query whether contract has registered support for given interface * @param interfaceId interface id * @return bool whether interface is supported */ function supportsInterface ( bytes4 interfaceId ) external view returns (bool); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @notice Partial ERC721 interface needed by internal functions */ interface IERC721Internal { event Transfer ( address indexed from, address indexed to, uint256 indexed tokenId ); event Approval ( address indexed owner, address indexed operator, uint256 indexed tokenId ); event ApprovalForAll ( address indexed owner, address indexed operator, bool approved ); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IERC20Internal} from './IERC20Internal.sol'; /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ interface IERC20 is IERC20Internal { /** * @notice query the total minted token supply * @return token supply */ function totalSupply () external view returns (uint256); /** * @notice query the token balance of given account * @param account address to query * @return token balance */ function balanceOf ( address account ) external view returns (uint256); /** * @notice query the allowance granted from given holder to given spender * @param holder approver of allowance * @param spender recipient of allowance * @return token allowance */ function allowance ( address holder, address spender ) external view returns (uint256); /** * @notice grant approval to spender to spend tokens * @dev prefer ERC20Extended functions to avoid transaction-ordering vulnerability (see https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729) * @param spender recipient of allowance * @param amount quantity of tokens approved for spending * @return success status (always true; otherwise function should revert) */ function approve ( address spender, uint256 amount ) external returns (bool); /** * @notice transfer tokens to given recipient * @param recipient beneficiary of token transfer * @param amount quantity of tokens to transfer * @return success status (always true; otherwise function should revert) */ function transfer ( address recipient, uint256 amount ) external returns (bool); /** * @notice transfer tokens to given recipient on behalf of given holder * @param holder holder of tokens prior to transfer * @param recipient beneficiary of token transfer * @param amount quantity of tokens to transfer * @return success status (always true; otherwise function should revert) */ function transferFrom ( address holder, address recipient, uint256 amount ) external returns (bool); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title Partial ERC20 interface needed by internal functions */ interface IERC20Internal { event Transfer( address indexed from, address indexed to, uint256 value ); event Approval( address indexed owner, address indexed spender, uint256 value ); }
File 2 of 3: MagicProxy
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import '@solidstate/contracts/proxy/diamond/Diamond.sol'; import '@solidstate/contracts/token/ERC20/metadata/ERC20MetadataStorage.sol'; contract MagicProxy is Diamond { constructor() { ERC20MetadataStorage.Layout storage l = ERC20MetadataStorage.layout(); l.name = 'MAGIC'; l.symbol = 'MAGIC'; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {SafeOwnable, OwnableStorage, Ownable} from '../../access/SafeOwnable.sol'; import {IERC173} from '../../access/IERC173.sol'; import {ERC165, IERC165, ERC165Storage} from '../../introspection/ERC165.sol'; import {DiamondBase, DiamondBaseStorage} from './DiamondBase.sol'; import {DiamondCuttable, IDiamondCuttable} from './DiamondCuttable.sol'; import {DiamondLoupe, IDiamondLoupe} from './DiamondLoupe.sol'; /** * @notice SolidState "Diamond" proxy reference implementation */ abstract contract Diamond is DiamondBase, DiamondCuttable, DiamondLoupe, SafeOwnable, ERC165 { using DiamondBaseStorage for DiamondBaseStorage.Layout; using ERC165Storage for ERC165Storage.Layout; using OwnableStorage for OwnableStorage.Layout; constructor () { ERC165Storage.Layout storage erc165 = ERC165Storage.layout(); bytes4[] memory selectors = new bytes4[](12); // register DiamondCuttable selectors[0] = IDiamondCuttable.diamondCut.selector; erc165.setSupportedInterface(type(IDiamondCuttable).interfaceId, true); // register DiamondLoupe selectors[1] = IDiamondLoupe.facets.selector; selectors[2] = IDiamondLoupe.facetFunctionSelectors.selector; selectors[3] = IDiamondLoupe.facetAddresses.selector; selectors[4] = IDiamondLoupe.facetAddress.selector; erc165.setSupportedInterface(type(IDiamondLoupe).interfaceId, true); // register ERC165 selectors[5] = IERC165.supportsInterface.selector; erc165.setSupportedInterface(type(IERC165).interfaceId, true); // register SafeOwnable selectors[6] = Ownable.owner.selector; selectors[7] = SafeOwnable.nomineeOwner.selector; selectors[8] = SafeOwnable.transferOwnership.selector; selectors[9] = SafeOwnable.acceptOwnership.selector; erc165.setSupportedInterface(type(IERC173).interfaceId, true); // register Diamond selectors[10] = Diamond.getFallbackAddress.selector; selectors[11] = Diamond.setFallbackAddress.selector; // diamond cut FacetCut[] memory facetCuts = new FacetCut[](1); facetCuts[0] = FacetCut({ target: address(this), action: IDiamondCuttable.FacetCutAction.ADD, selectors: selectors }); DiamondBaseStorage.layout().diamondCut(facetCuts, address(0), ''); // set owner OwnableStorage.layout().setOwner(msg.sender); } receive () external payable {} /** * @notice get the address of the fallback contract * @return fallback address */ function getFallbackAddress () external view returns (address) { return DiamondBaseStorage.layout().fallbackAddress; } /** * @notice set the address of the fallback contract * @param fallbackAddress fallback address */ function setFallbackAddress ( address fallbackAddress ) external onlyOwner { DiamondBaseStorage.layout().fallbackAddress = fallbackAddress; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library ERC20MetadataStorage { struct Layout { string name; string symbol; uint8 decimals; } bytes32 internal constant STORAGE_SLOT = keccak256( 'solidstate.contracts.storage.ERC20Metadata' ); function layout () internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } function setName ( Layout storage l, string memory name ) internal { l.name = name; } function setSymbol ( Layout storage l, string memory symbol ) internal { l.symbol = symbol; } function setDecimals ( Layout storage l, uint8 decimals ) internal { l.decimals = decimals; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {Ownable, OwnableStorage} from './Ownable.sol'; import {SafeOwnableInternal} from './SafeOwnableInternal.sol'; import {SafeOwnableStorage} from './SafeOwnableStorage.sol'; /** * @title Ownership access control based on ERC173 with ownership transfer safety check */ abstract contract SafeOwnable is Ownable, SafeOwnableInternal { using OwnableStorage for OwnableStorage.Layout; using SafeOwnableStorage for SafeOwnableStorage.Layout; function nomineeOwner () virtual public view returns (address) { return SafeOwnableStorage.layout().nomineeOwner; } /** * @inheritdoc Ownable * @dev ownership transfer must be accepted by beneficiary before transfer is complete */ function transferOwnership ( address account ) virtual override public onlyOwner { SafeOwnableStorage.layout().setNomineeOwner(account); } /** * @notice accept transfer of contract ownership */ function acceptOwnership () virtual public onlyNomineeOwner { OwnableStorage.Layout storage l = OwnableStorage.layout(); emit OwnershipTransferred(l.owner, msg.sender); l.setOwner(msg.sender); SafeOwnableStorage.layout().setNomineeOwner(address(0)); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title Contract ownership standard interface * @dev see https://eips.ethereum.org/EIPS/eip-173 */ interface IERC173 { event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @notice get the ERC173 contract owner * @return conract owner */ function owner () external view returns (address); /** * @notice transfer contract ownership to new account * @param account address of new owner */ function transferOwnership (address account) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IERC165} from './IERC165.sol'; import {ERC165Storage} from './ERC165Storage.sol'; /** * @title ERC165 implementation */ abstract contract ERC165 is IERC165 { using ERC165Storage for ERC165Storage.Layout; /** * @inheritdoc IERC165 */ function supportsInterface (bytes4 interfaceId) override public view returns (bool) { return ERC165Storage.layout().isSupportedInterface(interfaceId); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {Proxy} from '../Proxy.sol'; import {DiamondBaseStorage} from './DiamondBaseStorage.sol'; import {IDiamondLoupe} from './IDiamondLoupe.sol'; import {IDiamondCuttable} from './IDiamondCuttable.sol'; /** * @title EIP-2535 "Diamond" proxy base contract * @dev see https://eips.ethereum.org/EIPS/eip-2535 */ abstract contract DiamondBase is Proxy { /** * @inheritdoc Proxy */ function _getImplementation () override internal view returns (address) { // inline storage layout retrieval uses less gas DiamondBaseStorage.Layout storage l; bytes32 slot = DiamondBaseStorage.STORAGE_SLOT; assembly { l.slot := slot } address implementation = address(bytes20(l.facets[msg.sig])); if (implementation == address(0)) { implementation = l.fallbackAddress; require( implementation != address(0), 'DiamondBase: no facet found for function signature' ); } return implementation; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {OwnableInternal} from '../../access/OwnableInternal.sol'; import {IDiamondCuttable} from './IDiamondCuttable.sol'; import {DiamondBaseStorage} from './DiamondBaseStorage.sol'; /** * @title EIP-2535 "Diamond" proxy update contract */ abstract contract DiamondCuttable is IDiamondCuttable, OwnableInternal { using DiamondBaseStorage for DiamondBaseStorage.Layout; /** * @notice update functions callable on Diamond proxy * @param facetCuts array of structured Diamond facet update data * @param target optional recipient of initialization delegatecall * @param data optional initialization call data */ function diamondCut ( FacetCut[] calldata facetCuts, address target, bytes calldata data ) external override onlyOwner { DiamondBaseStorage.layout().diamondCut(facetCuts, target, data); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {DiamondBaseStorage} from './DiamondBaseStorage.sol'; import {IDiamondLoupe} from './IDiamondLoupe.sol'; /** * @title EIP-2535 "Diamond" proxy introspection contract * @dev derived from https://github.com/mudgen/diamond-2 (MIT license) */ abstract contract DiamondLoupe is IDiamondLoupe { /** * @inheritdoc IDiamondLoupe */ function facets () external override view returns (Facet[] memory diamondFacets) { DiamondBaseStorage.Layout storage l = DiamondBaseStorage.layout(); diamondFacets = new Facet[](l.selectorCount); uint8[] memory numFacetSelectors = new uint8[](l.selectorCount); uint256 numFacets; uint256 selectorIndex; // loop through function selectors for (uint256 slotIndex; selectorIndex < l.selectorCount; slotIndex++) { bytes32 slot = l.selectorSlots[slotIndex]; for (uint256 selectorSlotIndex; selectorSlotIndex < 8; selectorSlotIndex++) { selectorIndex++; if (selectorIndex > l.selectorCount) { break; } bytes4 selector = bytes4(slot << (selectorSlotIndex << 5)); address facet = address(bytes20(l.facets[selector])); bool continueLoop; for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) { if (diamondFacets[facetIndex].target == facet) { diamondFacets[facetIndex].selectors[numFacetSelectors[facetIndex]] = selector; // probably will never have more than 256 functions from one facet contract require(numFacetSelectors[facetIndex] < 255); numFacetSelectors[facetIndex]++; continueLoop = true; break; } } if (continueLoop) { continue; } diamondFacets[numFacets].target = facet; diamondFacets[numFacets].selectors = new bytes4[](l.selectorCount); diamondFacets[numFacets].selectors[0] = selector; numFacetSelectors[numFacets] = 1; numFacets++; } } for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) { uint256 numSelectors = numFacetSelectors[facetIndex]; bytes4[] memory selectors = diamondFacets[facetIndex].selectors; // setting the number of selectors assembly { mstore(selectors, numSelectors) } } // setting the number of facets assembly { mstore(diamondFacets, numFacets) } } /** * @inheritdoc IDiamondLoupe */ function facetFunctionSelectors ( address facet ) external override view returns (bytes4[] memory selectors) { DiamondBaseStorage.Layout storage l = DiamondBaseStorage.layout(); selectors = new bytes4[](l.selectorCount); uint256 numSelectors; uint256 selectorIndex; // loop through function selectors for (uint256 slotIndex; selectorIndex < l.selectorCount; slotIndex++) { bytes32 slot = l.selectorSlots[slotIndex]; for (uint256 selectorSlotIndex; selectorSlotIndex < 8; selectorSlotIndex++) { selectorIndex++; if (selectorIndex > l.selectorCount) { break; } bytes4 selector = bytes4(slot << (selectorSlotIndex << 5)); if (facet == address(bytes20(l.facets[selector]))) { selectors[numSelectors] = selector; numSelectors++; } } } // set the number of selectors in the array assembly { mstore(selectors, numSelectors) } } /** * @inheritdoc IDiamondLoupe */ function facetAddresses () external override view returns (address[] memory addresses) { DiamondBaseStorage.Layout storage l = DiamondBaseStorage.layout(); addresses = new address[](l.selectorCount); uint256 numFacets; uint256 selectorIndex; for (uint256 slotIndex; selectorIndex < l.selectorCount; slotIndex++) { bytes32 slot = l.selectorSlots[slotIndex]; for (uint256 selectorSlotIndex; selectorSlotIndex < 8; selectorSlotIndex++) { selectorIndex++; if (selectorIndex > l.selectorCount) { break; } bytes4 selector = bytes4(slot << (selectorSlotIndex << 5)); address facet = address(bytes20(l.facets[selector])); bool continueLoop; for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) { if (facet == addresses[facetIndex]) { continueLoop = true; break; } } if (continueLoop) { continue; } addresses[numFacets] = facet; numFacets++; } } // set the number of facet addresses in the array assembly { mstore(addresses, numFacets) } } /** * @inheritdoc IDiamondLoupe */ function facetAddress ( bytes4 selector ) external override view returns (address facet) { facet = address(bytes20( DiamondBaseStorage.layout().facets[selector] )); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IERC173} from './IERC173.sol'; import {OwnableInternal} from './OwnableInternal.sol'; import {OwnableStorage} from './OwnableStorage.sol'; /** * @title Ownership access control based on ERC173 */ abstract contract Ownable is IERC173, OwnableInternal { using OwnableStorage for OwnableStorage.Layout; /** * @inheritdoc IERC173 */ function owner () virtual override public view returns (address) { return OwnableStorage.layout().owner; } /** * @inheritdoc IERC173 */ function transferOwnership ( address account ) virtual override public onlyOwner { OwnableStorage.layout().setOwner(account); emit OwnershipTransferred(msg.sender, account); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {SafeOwnableStorage} from './SafeOwnableStorage.sol'; abstract contract SafeOwnableInternal { using SafeOwnableStorage for SafeOwnableStorage.Layout; modifier onlyNomineeOwner () { require( msg.sender == SafeOwnableStorage.layout().nomineeOwner, 'SafeOwnable: sender must be nominee owner' ); _; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library SafeOwnableStorage { struct Layout { address nomineeOwner; } bytes32 internal constant STORAGE_SLOT = keccak256( 'solidstate.contracts.storage.SafeOwnable' ); function layout () internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } function setNomineeOwner ( Layout storage l, address nomineeOwner ) internal { l.nomineeOwner = nomineeOwner; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {OwnableStorage} from './OwnableStorage.sol'; abstract contract OwnableInternal { using OwnableStorage for OwnableStorage.Layout; modifier onlyOwner { require( msg.sender == OwnableStorage.layout().owner, 'Ownable: sender must be owner' ); _; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library OwnableStorage { struct Layout { address owner; } bytes32 internal constant STORAGE_SLOT = keccak256( 'solidstate.contracts.storage.Ownable' ); function layout () internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } function setOwner ( Layout storage l, address owner ) internal { l.owner = owner; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title ERC165 interface registration interface * @dev see https://eips.ethereum.org/EIPS/eip-165 */ interface IERC165 { /** * @notice query whether contract has registered support for given interface * @param interfaceId interface id * @return bool whether interface is supported */ function supportsInterface ( bytes4 interfaceId ) external view returns (bool); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library ERC165Storage { struct Layout { // TODO: use EnumerableSet to allow post-diamond-cut auditing mapping (bytes4 => bool) supportedInterfaces; } bytes32 internal constant STORAGE_SLOT = keccak256( 'solidstate.contracts.storage.ERC165' ); function layout () internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } function isSupportedInterface ( Layout storage l, bytes4 interfaceId ) internal view returns (bool) { return l.supportedInterfaces[interfaceId]; } function setSupportedInterface ( Layout storage l, bytes4 interfaceId, bool status ) internal { require(interfaceId != 0xffffffff, 'ERC165: invalid interface id'); l.supportedInterfaces[interfaceId] = status; } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {AddressUtils} from '../utils/AddressUtils.sol'; /** * @title Base proxy contract */ abstract contract Proxy { using AddressUtils for address; /** * @notice delegate all calls to implementation contract * @dev reverts if implementation address contains no code, for compatibility with metamorphic contracts * @dev memory location in use by assembly may be unsafe in other contexts */ fallback () virtual external payable { address implementation = _getImplementation(); require( implementation.isContract(), 'Proxy: implementation must be contract' ); assembly { calldatacopy(0, 0, calldatasize()) let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) returndatacopy(0, 0, returndatasize()) switch result case 0 { revert(0, returndatasize()) } default { return (0, returndatasize()) } } } /** * @notice get logic implementation address * @return implementation address */ function _getImplementation () virtual internal returns (address); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {AddressUtils} from '../../utils/AddressUtils.sol'; import {IDiamondCuttable} from './IDiamondCuttable.sol'; /** * @dev derived from https://github.com/mudgen/diamond-2 (MIT license) */ library DiamondBaseStorage { using AddressUtils for address; using DiamondBaseStorage for DiamondBaseStorage.Layout; struct Layout { // function selector => (facet address, selector slot position) mapping (bytes4 => bytes32) facets; // total number of selectors registered uint16 selectorCount; // array of selector slots with 8 selectors per slot mapping (uint256 => bytes32) selectorSlots; address fallbackAddress; } bytes32 constant CLEAR_ADDRESS_MASK = bytes32(uint256(0xffffffffffffffffffffffff)); bytes32 constant CLEAR_SELECTOR_MASK = bytes32(uint256(0xffffffff << 224)); bytes32 internal constant STORAGE_SLOT = keccak256( 'solidstate.contracts.storage.DiamondBase' ); event DiamondCut (IDiamondCuttable.FacetCut[] facetCuts, address target, bytes data); function layout () internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } /** * @notice update functions callable on Diamond proxy * @param l storage layout * @param facetCuts array of structured Diamond facet update data * @param target optional recipient of initialization delegatecall * @param data optional initialization call data */ function diamondCut( Layout storage l, IDiamondCuttable.FacetCut[] memory facetCuts, address target, bytes memory data ) internal { unchecked { uint256 originalSelectorCount = l.selectorCount; uint256 selectorCount = originalSelectorCount; bytes32 selectorSlot; // Check if last selector slot is not full if (selectorCount & 7 > 0) { // get last selectorSlot selectorSlot = l.selectorSlots[selectorCount >> 3]; } for (uint256 i; i < facetCuts.length; i++) { IDiamondCuttable.FacetCut memory facetCut = facetCuts[i]; IDiamondCuttable.FacetCutAction action = facetCut.action; require( facetCut.selectors.length > 0, 'DiamondBase: no selectors specified' ); if (action == IDiamondCuttable.FacetCutAction.ADD) { (selectorCount, selectorSlot) = l.addFacetSelectors( selectorCount, selectorSlot, facetCut ); } else if (action == IDiamondCuttable.FacetCutAction.REMOVE) { (selectorCount, selectorSlot) = l.removeFacetSelectors( selectorCount, selectorSlot, facetCut ); } else if (action == IDiamondCuttable.FacetCutAction.REPLACE) { l.replaceFacetSelectors(facetCut); } } if (selectorCount != originalSelectorCount) { l.selectorCount = uint16(selectorCount); } // If last selector slot is not full if (selectorCount & 7 > 0) { l.selectorSlots[selectorCount >> 3] = selectorSlot; } emit DiamondCut(facetCuts, target, data); initialize(target, data); } } function addFacetSelectors ( Layout storage l, uint256 selectorCount, bytes32 selectorSlot, IDiamondCuttable.FacetCut memory facetCut ) internal returns (uint256, bytes32) { unchecked { require( facetCut.target == address(this) || facetCut.target.isContract(), 'DiamondBase: ADD target has no code' ); for (uint256 i; i < facetCut.selectors.length; i++) { bytes4 selector = facetCut.selectors[i]; bytes32 oldFacet = l.facets[selector]; require( address(bytes20(oldFacet)) == address(0), 'DiamondBase: selector already added' ); // add facet for selector l.facets[selector] = bytes20(facetCut.target) | bytes32(selectorCount); uint256 selectorInSlotPosition = (selectorCount & 7) << 5; // clear selector position in slot and add selector selectorSlot = ( selectorSlot & ~(CLEAR_SELECTOR_MASK >> selectorInSlotPosition) ) | (bytes32(selector) >> selectorInSlotPosition); // if slot is full then write it to storage if (selectorInSlotPosition == 224) { l.selectorSlots[selectorCount >> 3] = selectorSlot; selectorSlot = 0; } selectorCount++; } return (selectorCount, selectorSlot); } } function removeFacetSelectors ( Layout storage l, uint256 selectorCount, bytes32 selectorSlot, IDiamondCuttable.FacetCut memory facetCut ) internal returns (uint256, bytes32) { unchecked { require( facetCut.target == address(0), 'DiamondBase: REMOVE target must be zero address' ); uint256 selectorSlotCount = selectorCount >> 3; uint256 selectorInSlotIndex = selectorCount & 7; for (uint256 i; i < facetCut.selectors.length; i++) { bytes4 selector = facetCut.selectors[i]; bytes32 oldFacet = l.facets[selector]; require( address(bytes20(oldFacet)) != address(0), 'DiamondBase: selector not found' ); require( address(bytes20(oldFacet)) != address(this), 'DiamondBase: selector is immutable' ); if (selectorSlot == 0) { selectorSlotCount--; selectorSlot = l.selectorSlots[selectorSlotCount]; selectorInSlotIndex = 7; } else { selectorInSlotIndex--; } bytes4 lastSelector; uint256 oldSelectorsSlotCount; uint256 oldSelectorInSlotPosition; // adding a block here prevents stack too deep error { // replace selector with last selector in l.facets lastSelector = bytes4(selectorSlot << (selectorInSlotIndex << 5)); if (lastSelector != selector) { // update last selector slot position info l.facets[lastSelector] = ( oldFacet & CLEAR_ADDRESS_MASK ) | bytes20(l.facets[lastSelector]); } delete l.facets[selector]; uint256 oldSelectorCount = uint16(uint256(oldFacet)); oldSelectorsSlotCount = oldSelectorCount >> 3; oldSelectorInSlotPosition = (oldSelectorCount & 7) << 5; } if (oldSelectorsSlotCount != selectorSlotCount) { bytes32 oldSelectorSlot = l.selectorSlots[oldSelectorsSlotCount]; // clears the selector we are deleting and puts the last selector in its place. oldSelectorSlot = ( oldSelectorSlot & ~(CLEAR_SELECTOR_MASK >> oldSelectorInSlotPosition) ) | (bytes32(lastSelector) >> oldSelectorInSlotPosition); // update storage with the modified slot l.selectorSlots[oldSelectorsSlotCount] = oldSelectorSlot; } else { // clears the selector we are deleting and puts the last selector in its place. selectorSlot = ( selectorSlot & ~(CLEAR_SELECTOR_MASK >> oldSelectorInSlotPosition) ) | (bytes32(lastSelector) >> oldSelectorInSlotPosition); } if (selectorInSlotIndex == 0) { delete l.selectorSlots[selectorSlotCount]; selectorSlot = 0; } } selectorCount = (selectorSlotCount << 3) | selectorInSlotIndex; return (selectorCount, selectorSlot); } } function replaceFacetSelectors ( Layout storage l, IDiamondCuttable.FacetCut memory facetCut ) internal { unchecked { require( facetCut.target.isContract(), 'DiamondBase: REPLACE target has no code' ); for (uint256 i; i < facetCut.selectors.length; i++) { bytes4 selector = facetCut.selectors[i]; bytes32 oldFacet = l.facets[selector]; address oldFacetAddress = address(bytes20(oldFacet)); require( oldFacetAddress != address(0), 'DiamondBase: selector not found' ); require( oldFacetAddress != address(this), 'DiamondBase: selector is immutable' ); require( oldFacetAddress != facetCut.target, 'DiamondBase: REPLACE target is identical' ); // replace old facet address l.facets[selector] = (oldFacet & CLEAR_ADDRESS_MASK) | bytes20(facetCut.target); } } } function initialize ( address target, bytes memory data ) private { require( (target == address(0)) == (data.length == 0), 'DiamondBase: invalid initialization parameters' ); if (target != address(0)) { if (target != address(this)) { require( target.isContract(), 'DiamondBase: initialization target has no code' ); } (bool success, ) = target.delegatecall(data); if (!success) { assembly { returndatacopy(0, 0, returndatasize()) revert(0, returndatasize()) } } } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title Diamond proxy introspection interface * @dev see https://eips.ethereum.org/EIPS/eip-2535 */ interface IDiamondLoupe { struct Facet { address target; bytes4[] selectors; } /** * @notice get all facets and their selectors * @return diamondFacets array of structured facet data */ function facets () external view returns (Facet[] memory diamondFacets); /** * @notice get all selectors for given facet address * @param facet address of facet to query * @return selectors array of function selectors */ function facetFunctionSelectors ( address facet ) external view returns (bytes4[] memory selectors); /** * @notice get addresses of all facets used by diamond * @return addresses array of facet addresses */ function facetAddresses () external view returns (address[] memory addresses); /** * @notice get the address of the facet associated with given selector * @param selector function selector to query * @return facet facet address (zero address if not found) */ function facetAddress ( bytes4 selector ) external view returns (address facet); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title Diamond proxy upgrade interface * @dev see https://eips.ethereum.org/EIPS/eip-2535 */ interface IDiamondCuttable { enum FacetCutAction { ADD, REPLACE, REMOVE } event DiamondCut (FacetCut[] facetCuts, address target, bytes data); struct FacetCut { address target; FacetCutAction action; bytes4[] selectors; } /** * @notice update diamond facets and optionally execute arbitrary initialization function * @param facetCuts facet addresses, actions, and function selectors * @param target initialization function target * @param data initialization function call data */ function diamondCut ( FacetCut[] calldata facetCuts, address target, bytes calldata data ) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library AddressUtils { function toString (address account) internal pure returns (string memory) { bytes32 value = bytes32(uint256(uint160(account))); bytes memory alphabet = '0123456789abcdef'; bytes memory chars = new bytes(42); chars[0] = '0'; chars[1] = 'x'; for (uint256 i = 0; i < 20; i++) { chars[2 + i * 2] = alphabet[uint8(value[i + 12] >> 4)]; chars[3 + i * 2] = alphabet[uint8(value[i + 12] & 0x0f)]; } return string(chars); } function isContract (address account) internal view returns (bool) { uint size; assembly { size := extcodesize(account) } return size > 0; } function sendValue (address payable account, uint amount) internal { (bool success, ) = account.call{ value: amount }(''); require(success, 'AddressUtils: failed to send value'); } function functionCall (address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, 'AddressUtils: failed low-level call'); } function functionCall (address target, bytes memory data, string memory error) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, error); } function functionCallWithValue (address target, bytes memory data, uint value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, 'AddressUtils: failed low-level call with value'); } function functionCallWithValue (address target, bytes memory data, uint value, string memory error) internal returns (bytes memory) { require(address(this).balance >= value, 'AddressUtils: insufficient balance for call'); return _functionCallWithValue(target, data, value, error); } function _functionCallWithValue (address target, bytes memory data, uint value, string memory error) private returns (bytes memory) { require(isContract(target), 'AddressUtils: function call to non-contract'); (bool success, bytes memory returnData) = target.call{ value: value }(data); if (success) { return returnData; } else if (returnData.length > 0) { assembly { let returnData_size := mload(returnData) revert(add(32, returnData), returnData_size) } } else { revert(error); } } }
File 3 of 3: MagicMintReentrancyFix
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import '@solidstate/contracts/token/ERC20/base/ERC20BaseInternal.sol'; import '@solidstate/contracts/utils/ReentrancyGuard.sol'; import './MagicWhitelistStorage.sol'; contract MagicMintReentrancyFix is ERC20BaseInternal, ReentrancyGuard { function mint(address account, uint256 amount) external nonReentrant { require( MagicWhitelistStorage.layout().whitelist[msg.sender], 'Magic: sender must be whitelisted' ); _mint(account, amount); } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {IERC20Internal} from '../IERC20Internal.sol'; import {ERC20BaseStorage} from './ERC20BaseStorage.sol'; /** * @title Base ERC20 implementation, excluding optional extensions */ abstract contract ERC20BaseInternal is IERC20Internal { /** * @notice query the total minted token supply * @return token supply */ function _totalSupply () virtual internal view returns (uint) { return ERC20BaseStorage.layout().totalSupply; } /** * @notice query the token balance of given account * @param account address to query * @return token balance */ function _balanceOf ( address account ) virtual internal view returns (uint) { return ERC20BaseStorage.layout().balances[account]; } /** * @notice enable spender to spend tokens on behalf of holder * @param holder address on whose behalf tokens may be spent * @param spender recipient of allowance * @param amount quantity of tokens approved for spending */ function _approve ( address holder, address spender, uint amount ) virtual internal { require(holder != address(0), 'ERC20: approve from the zero address'); require(spender != address(0), 'ERC20: approve to the zero address'); ERC20BaseStorage.layout().allowances[holder][spender] = amount; emit Approval(holder, spender, amount); } /** * @notice mint tokens for given account * @param account recipient of minted tokens * @param amount quantity of tokens minted */ function _mint ( address account, uint amount ) virtual internal { require(account != address(0), 'ERC20: mint to the zero address'); _beforeTokenTransfer(address(0), account, amount); ERC20BaseStorage.Layout storage l = ERC20BaseStorage.layout(); l.totalSupply += amount; l.balances[account] += amount; emit Transfer(address(0), account, amount); } /** * @notice burn tokens held by given account * @param account holder of burned tokens * @param amount quantity of tokens burned */ function _burn ( address account, uint amount ) virtual internal { require(account != address(0), 'ERC20: burn from the zero address'); _beforeTokenTransfer(account, address(0), amount); ERC20BaseStorage.Layout storage l = ERC20BaseStorage.layout(); uint256 balance = l.balances[account]; require(balance >= amount, "ERC20: burn amount exceeds balance"); unchecked { l.balances[account] = balance - amount; } l.totalSupply -= amount; emit Transfer(account, address(0), amount); } /** * @notice transfer tokens from holder to recipient * @param holder owner of tokens to be transferred * @param recipient beneficiary of transfer * @param amount quantity of tokens transferred */ function _transfer ( address holder, address recipient, uint amount ) virtual internal { require(holder != address(0), 'ERC20: transfer from the zero address'); require(recipient != address(0), 'ERC20: transfer to the zero address'); _beforeTokenTransfer(holder, recipient, amount); ERC20BaseStorage.Layout storage l = ERC20BaseStorage.layout(); uint256 holderBalance = l.balances[holder]; require(holderBalance >= amount, 'ERC20: transfer amount exceeds balance'); unchecked { l.balances[holder] = holderBalance - amount; } l.balances[recipient] += amount; emit Transfer(holder, recipient, amount); } /** * @notice ERC20 hook, called before all transfers including mint and burn * @dev function should be overridden and new implementation must call super * @param from sender of tokens * @param to receiver of tokens * @param amount quantity of tokens transferred */ function _beforeTokenTransfer ( address from, address to, uint amount ) virtual internal {} } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import {ReentrancyGuardStorage} from './ReentrancyGuardStorage.sol'; /** * @title Utility contract for preventing reentrancy attacks */ abstract contract ReentrancyGuard { modifier nonReentrant () { ReentrancyGuardStorage.Layout storage l = ReentrancyGuardStorage.layout(); require(l.status != 2, 'ReentrancyGuard: reentrant call'); l.status = 2; _; l.status = 1; } } // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; library MagicWhitelistStorage { struct Layout { mapping(address => bool) whitelist; } bytes32 internal constant STORAGE_SLOT = keccak256('treasure.contracts.storage.MagicWhitelist'); function layout() internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; /** * @title Partial ERC20 interface needed by internal functions */ interface IERC20Internal { event Transfer( address indexed from, address indexed to, uint256 value ); event Approval( address indexed owner, address indexed spender, uint256 value ); } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library ERC20BaseStorage { struct Layout { mapping (address => uint) balances; mapping (address => mapping (address => uint)) allowances; uint totalSupply; } bytes32 internal constant STORAGE_SLOT = keccak256( 'solidstate.contracts.storage.ERC20Base' ); function layout () internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } } // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; library ReentrancyGuardStorage { struct Layout { uint status; } bytes32 internal constant STORAGE_SLOT = keccak256( 'solidstate.contracts.storage.ReentrancyGuard' ); function layout () internal pure returns (Layout storage l) { bytes32 slot = STORAGE_SLOT; assembly { l.slot := slot } } }