diff --git a/.gitignore b/.gitignore index a474e7333..5230263bf 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,10 @@ artifacts/@openzeppelin artifacts/build-info build/ scripts/reference-scripts -cache*/ +cache/ +cache-zk/ +cache_hardhat-zk/ +cache_hardhat/ coverage/ dist/ node_modules/ @@ -19,7 +22,7 @@ abi/ contracts/abi/ contracts/README.md artifacts/ -artifacts-*/ +artifacts-zk/ artifacts_forge/ contract_artifacts/ diff --git a/.gitmodules b/.gitmodules index db5ccba93..083a93f87 100644 --- a/.gitmodules +++ b/.gitmodules @@ -46,12 +46,3 @@ [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/OpenZeppelin/openzeppelin-contracts -[submodule "lib/v3-periphery"] - path = lib/v3-periphery - url = https://github.com/uniswap/v3-periphery -[submodule "lib/v3-core"] - path = lib/v3-core - url = https://github.com/uniswap/v3-core -[submodule "lib/swap-router-contracts"] - path = lib/swap-router-contracts - url = https://github.com/Uniswap/swap-router-contracts diff --git a/contracts/extension/plugin/Router.sol b/contracts/extension/plugin/Router.sol index 301db0803..cf8b9d911 100644 --- a/contracts/extension/plugin/Router.sol +++ b/contracts/extension/plugin/Router.sol @@ -70,7 +70,7 @@ abstract contract Router is Multicall, ERC165, IRouter { _delegate(_pluginAddress); } - receive() external payable {} + receive() external payable virtual {} function _delegate(address implementation) internal virtual { assembly { diff --git a/contracts/prebuilts/account/dynamic/DynamicAccount.sol b/contracts/prebuilts/account/dynamic/DynamicAccount.sol deleted file mode 100644 index 0d5ac3b26..000000000 --- a/contracts/prebuilts/account/dynamic/DynamicAccount.sol +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -/* solhint-disable avoid-low-level-calls */ -/* solhint-disable no-inline-assembly */ -/* solhint-disable reason-string */ - -import "../utils/AccountCore.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/presets/BaseRouter.sol"; - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -contract DynamicAccount is AccountCore, BaseRouter { - /*/////////////////////////////////////////////////////////////// - Constructor and Initializer - //////////////////////////////////////////////////////////////*/ - - constructor( - IEntryPoint _entrypoint, - Extension[] memory _defaultExtensions - ) AccountCore(_entrypoint, msg.sender) BaseRouter(_defaultExtensions) { - _disableInitializers(); - } - - /// @notice Initializes the smart contract wallet. - function initialize(address _defaultAdmin, bytes calldata _data) public override initializer { - __BaseRouter_init(); - AccountCoreStorage.data().creationSalt = _generateSalt(_defaultAdmin, _data); - _setAdmin(_defaultAdmin, true); - } - - /*/////////////////////////////////////////////////////////////// - Internal overrides - //////////////////////////////////////////////////////////////*/ - - /// @dev Returns whether all relevant permission and other checks are met before any upgrade. - function _isAuthorizedCallToUpgrade() internal view virtual override returns (bool) { - return isAdmin(msg.sender); - } -} diff --git a/contracts/prebuilts/account/dynamic/DynamicAccountFactory.sol b/contracts/prebuilts/account/dynamic/DynamicAccountFactory.sol deleted file mode 100644 index 227a32509..000000000 --- a/contracts/prebuilts/account/dynamic/DynamicAccountFactory.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.12; - -// Utils -import "../utils/BaseAccountFactory.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -// Extensions -import "../../../extension/upgradeable//PermissionsEnumerable.sol"; -import "../../../extension/upgradeable//ContractMetadata.sol"; - -// Smart wallet implementation -import { DynamicAccount, IEntryPoint } from "./DynamicAccount.sol"; - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -contract DynamicAccountFactory is BaseAccountFactory, ContractMetadata, PermissionsEnumerable { - address public constant ENTRYPOINT_ADDRESS = 0x0000000071727De22E5E9d8BAf0edAc6f37da032; - - /*/////////////////////////////////////////////////////////////// - Constructor - //////////////////////////////////////////////////////////////*/ - - constructor( - address _defaultAdmin, - IExtension.Extension[] memory _defaultExtensions - ) - BaseAccountFactory( - payable(address(new DynamicAccount(IEntryPoint(ENTRYPOINT_ADDRESS), _defaultExtensions))), - ENTRYPOINT_ADDRESS - ) - { - _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); - } - - /*/////////////////////////////////////////////////////////////// - Internal functions - //////////////////////////////////////////////////////////////*/ - - /// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`. - function _initializeAccount(address _account, address _admin, bytes calldata _data) internal override { - DynamicAccount(payable(_account)).initialize(_admin, _data); - } - - /// @dev Returns whether contract metadata can be set in the given execution context. - function _canSetContractURI() internal view virtual override returns (bool) { - return hasRole(DEFAULT_ADMIN_ROLE, msg.sender); - } - - /// @notice Returns the sender in the given execution context. - function _msgSender() internal view override(Multicall, Permissions) returns (address) { - return msg.sender; - } -} diff --git a/contracts/prebuilts/account/interfaces/IAccount.sol b/contracts/prebuilts/account/interfaces/IAccount.sol deleted file mode 100644 index 5f80a930b..000000000 --- a/contracts/prebuilts/account/interfaces/IAccount.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.0; - -import "./PackedUserOperation.sol"; - -interface IAccount { - /** - * Validate user's signature and nonce - * the entryPoint will make the call to the recipient only if this validation call returns successfully. - * signature failure should be reported by returning SIG_VALIDATION_FAILED (1). - * This allows making a "simulation call" without a valid signature - * Other failures (e.g. nonce mismatch, or invalid signature format) should still revert to signal failure. - * - * @dev Must validate caller is the entryPoint. - * Must validate the signature and nonce - * @param userOp - The operation that is about to be executed. - * @param userOpHash - Hash of the user's request data. can be used as the basis for signature. - * @param missingAccountFunds - Missing funds on the account's deposit in the entrypoint. - * This is the minimum amount to transfer to the sender(entryPoint) to be - * able to make the call. The excess is left as a deposit in the entrypoint - * for future calls. Can be withdrawn anytime using "entryPoint.withdrawTo()". - * In case there is a paymaster in the request (or the current deposit is high - * enough), this value will be zero. - * @return validationData - Packaged ValidationData structure. use `_packValidationData` and - * `_unpackValidationData` to encode and decode. - * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, - * otherwise, an address of an "authorizer" contract. - * <6-byte> validUntil - Last timestamp this operation is valid. 0 for "indefinite" - * <6-byte> validAfter - First timestamp this operation is valid - * If an account doesn't use time-range, it is enough to - * return SIG_VALIDATION_FAILED value (1) for signature failure. - * Note that the validation code cannot use block.timestamp (or block.number) directly. - */ - function validateUserOp( - PackedUserOperation calldata userOp, - bytes32 userOpHash, - uint256 missingAccountFunds - ) external returns (uint256 validationData); -} diff --git a/contracts/prebuilts/account/interfaces/IAccountCore.sol b/contracts/prebuilts/account/interfaces/IAccountCore.sol deleted file mode 100644 index 8ea905e81..000000000 --- a/contracts/prebuilts/account/interfaces/IAccountCore.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: Apache 2.0 -pragma solidity ^0.8.12; - -import "./IAccount.sol"; -import "../../../extension/interface/IAccountPermissions.sol"; -import "../../../extension/interface/IMulticall.sol"; - -interface IAccountCore is IAccount, IAccountPermissions, IMulticall { - /// @dev Returns the address of the factory from which the account was created. - function factory() external view returns (address); -} diff --git a/contracts/prebuilts/account/interfaces/IAccountExecute.sol b/contracts/prebuilts/account/interfaces/IAccountExecute.sol deleted file mode 100644 index 157e4ff18..000000000 --- a/contracts/prebuilts/account/interfaces/IAccountExecute.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.0; - -import "./PackedUserOperation.sol"; - -interface IAccountExecute { - /** - * Account may implement this execute method. - * passing this methodSig at the beginning of callData will cause the entryPoint to pass the full UserOp (and hash) - * to the account. - * The account should skip the methodSig, and use the callData (and optionally, other UserOp fields) - * - * @param userOp - The operation that was just validated. - * @param userOpHash - Hash of the user's request data. - */ - function executeUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external; -} diff --git a/contracts/prebuilts/account/interfaces/IAccountFactory.sol b/contracts/prebuilts/account/interfaces/IAccountFactory.sol deleted file mode 100644 index 4452a34be..000000000 --- a/contracts/prebuilts/account/interfaces/IAccountFactory.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.12; - -import "./IAccountFactoryCore.sol"; - -interface IAccountFactory is IAccountFactoryCore { - /*/////////////////////////////////////////////////////////////// - Callback Functions - //////////////////////////////////////////////////////////////*/ - - /// @notice Callback function for an Account to register its signers. - function onSignerAdded(address signer, bytes32 salt) external; - - /// @notice Callback function for an Account to un-register its signers. - function onSignerRemoved(address signer, bytes32 salt) external; -} diff --git a/contracts/prebuilts/account/interfaces/IAccountFactoryCore.sol b/contracts/prebuilts/account/interfaces/IAccountFactoryCore.sol deleted file mode 100644 index 672955a5e..000000000 --- a/contracts/prebuilts/account/interfaces/IAccountFactoryCore.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.12; - -interface IAccountFactoryCore { - /*/////////////////////////////////////////////////////////////// - Events - //////////////////////////////////////////////////////////////*/ - - /// @notice Emitted when a new Account is created. - event AccountCreated(address indexed account, address indexed accountAdmin); - - /// @notice Emitted when a new signer is added to an Account. - event SignerAdded(address indexed account, address indexed signer); - - /// @notice Emitted when a new signer is added to an Account. - event SignerRemoved(address indexed account, address indexed signer); - - /*/////////////////////////////////////////////////////////////// - Extension Functions - //////////////////////////////////////////////////////////////*/ - - /// @notice Deploys a new Account for admin. - function createAccount(address admin, bytes calldata _data) external returns (address account); - - /*/////////////////////////////////////////////////////////////// - View Functions - //////////////////////////////////////////////////////////////*/ - - /// @notice Returns the address of the Account implementation. - function accountImplementation() external view returns (address); - - /// @notice Returns all accounts created on the factory. - function getAllAccounts() external view returns (address[] memory); - - /// @notice Returns the address of an Account that would be deployed with the given admin signer. - function getAddress(address adminSigner, bytes calldata data) external view returns (address); - - /// @notice Returns all accounts on which a signer has (active or inactive) permissions. - function getAccountsOfSigner(address signer) external view returns (address[] memory accounts); -} diff --git a/contracts/prebuilts/account/interfaces/IAggregator.sol b/contracts/prebuilts/account/interfaces/IAggregator.sol deleted file mode 100644 index 18150b0e6..000000000 --- a/contracts/prebuilts/account/interfaces/IAggregator.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.0; - -import "./PackedUserOperation.sol"; - -/** - * Aggregated Signatures validator. - */ -interface IAggregator { - /** - * Validate aggregated signature. - * Revert if the aggregated signature does not match the given list of operations. - * @param userOps - Array of UserOperations to validate the signature for. - * @param signature - The aggregated signature. - */ - function validateSignatures(PackedUserOperation[] calldata userOps, bytes calldata signature) external view; - - /** - * Validate signature of a single userOp. - * This method should be called by bundler after EntryPointSimulation.simulateValidation() returns - * the aggregator this account uses. - * First it validates the signature over the userOp. Then it returns data to be used when creating the handleOps. - * @param userOp - The userOperation received from the user. - * @return sigForUserOp - The value to put into the signature field of the userOp when calling handleOps. - * (usually empty, unless account and aggregator support some kind of "multisig". - */ - function validateUserOpSignature( - PackedUserOperation calldata userOp - ) external view returns (bytes memory sigForUserOp); - - /** - * Aggregate multiple signatures into a single value. - * This method is called off-chain to calculate the signature to pass with handleOps() - * bundler MAY use optimized custom code perform this aggregation. - * @param userOps - Array of UserOperations to collect the signatures from. - * @return aggregatedSignature - The aggregated signature. - */ - function aggregateSignatures( - PackedUserOperation[] calldata userOps - ) external view returns (bytes memory aggregatedSignature); -} diff --git a/contracts/prebuilts/account/interfaces/IEntryPoint.sol b/contracts/prebuilts/account/interfaces/IEntryPoint.sol deleted file mode 100644 index d938ab2ed..000000000 --- a/contracts/prebuilts/account/interfaces/IEntryPoint.sol +++ /dev/null @@ -1,204 +0,0 @@ -/** - ** Account-Abstraction (EIP-4337) singleton EntryPoint implementation. - ** Only one instance required on each chain. - **/ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.0; - -/* solhint-disable avoid-low-level-calls */ -/* solhint-disable no-inline-assembly */ -/* solhint-disable reason-string */ - -import "./PackedUserOperation.sol"; -import "./IStakeManager.sol"; -import "./IAggregator.sol"; -import "./INonceManager.sol"; - -interface IEntryPoint is IStakeManager, INonceManager { - /*** - * An event emitted after each successful request. - * @param userOpHash - Unique identifier for the request (hash its entire content, except signature). - * @param sender - The account that generates this request. - * @param paymaster - If non-null, the paymaster that pays for this request. - * @param nonce - The nonce value from the request. - * @param success - True if the sender transaction succeeded, false if reverted. - * @param actualGasCost - Actual amount paid (by account or paymaster) for this UserOperation. - * @param actualGasUsed - Total gas used by this UserOperation (including preVerification, creation, - * validation and execution). - */ - event UserOperationEvent( - bytes32 indexed userOpHash, - address indexed sender, - address indexed paymaster, - uint256 nonce, - bool success, - uint256 actualGasCost, - uint256 actualGasUsed - ); - - /** - * Account "sender" was deployed. - * @param userOpHash - The userOp that deployed this account. UserOperationEvent will follow. - * @param sender - The account that is deployed - * @param factory - The factory used to deploy this account (in the initCode) - * @param paymaster - The paymaster used by this UserOp - */ - event AccountDeployed(bytes32 indexed userOpHash, address indexed sender, address factory, address paymaster); - - /** - * An event emitted if the UserOperation "callData" reverted with non-zero length. - * @param userOpHash - The request unique identifier. - * @param sender - The sender of this request. - * @param nonce - The nonce used in the request. - * @param revertReason - The return bytes from the (reverted) call to "callData". - */ - event UserOperationRevertReason( - bytes32 indexed userOpHash, - address indexed sender, - uint256 nonce, - bytes revertReason - ); - - /** - * An event emitted if the UserOperation Paymaster's "postOp" call reverted with non-zero length. - * @param userOpHash - The request unique identifier. - * @param sender - The sender of this request. - * @param nonce - The nonce used in the request. - * @param revertReason - The return bytes from the (reverted) call to "callData". - */ - event PostOpRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason); - - /** - * UserOp consumed more than prefund. The UserOperation is reverted, and no refund is made. - * @param userOpHash - The request unique identifier. - * @param sender - The sender of this request. - * @param nonce - The nonce used in the request. - */ - event UserOperationPrefundTooLow(bytes32 indexed userOpHash, address indexed sender, uint256 nonce); - - /** - * An event emitted by handleOps(), before starting the execution loop. - * Any event emitted before this event, is part of the validation. - */ - event BeforeExecution(); - - /** - * Signature aggregator used by the following UserOperationEvents within this bundle. - * @param aggregator - The aggregator used for the following UserOperationEvents. - */ - event SignatureAggregatorChanged(address indexed aggregator); - - /** - * A custom revert error of handleOps, to identify the offending op. - * Should be caught in off-chain handleOps simulation and not happen on-chain. - * Useful for mitigating DoS attempts against batchers or for troubleshooting of factory/account/paymaster reverts. - * NOTE: If simulateValidation passes successfully, there should be no reason for handleOps to fail on it. - * @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero). - * @param reason - Revert reason. The string starts with a unique code "AAmn", - * where "m" is "1" for factory, "2" for account and "3" for paymaster issues, - * so a failure can be attributed to the correct entity. - */ - error FailedOp(uint256 opIndex, string reason); - - /** - * A custom revert error of handleOps, to report a revert by account or paymaster. - * @param opIndex - Index into the array of ops to the failed one (in simulateValidation, this is always zero). - * @param reason - Revert reason. see FailedOp(uint256,string), above - * @param inner - data from inner cought revert reason - * @dev note that inner is truncated to 2048 bytes - */ - error FailedOpWithRevert(uint256 opIndex, string reason, bytes inner); - - error PostOpReverted(bytes returnData); - - /** - * Error case when a signature aggregator fails to verify the aggregated signature it had created. - * @param aggregator The aggregator that failed to verify the signature - */ - error SignatureValidationFailed(address aggregator); - - // Return value of getSenderAddress. - error SenderAddressResult(address sender); - - // UserOps handled, per aggregator. - struct UserOpsPerAggregator { - PackedUserOperation[] userOps; - // Aggregator address - IAggregator aggregator; - // Aggregated signature - bytes signature; - } - - /** - * Execute a batch of UserOperations. - * No signature aggregator is used. - * If any account requires an aggregator (that is, it returned an aggregator when - * performing simulateValidation), then handleAggregatedOps() must be used instead. - * @param ops - The operations to execute. - * @param beneficiary - The address to receive the fees. - */ - function handleOps(PackedUserOperation[] calldata ops, address payable beneficiary) external; - - /** - * Execute a batch of UserOperation with Aggregators - * @param opsPerAggregator - The operations to execute, grouped by aggregator (or address(0) for no-aggregator accounts). - * @param beneficiary - The address to receive the fees. - */ - function handleAggregatedOps( - UserOpsPerAggregator[] calldata opsPerAggregator, - address payable beneficiary - ) external; - - /** - * Generate a request Id - unique identifier for this request. - * The request ID is a hash over the content of the userOp (except the signature), the entrypoint and the chainid. - * @param userOp - The user operation to generate the request ID for. - * @return hash the hash of this UserOperation - */ - function getUserOpHash(PackedUserOperation calldata userOp) external view returns (bytes32); - - /** - * Gas and return values during simulation. - * @param preOpGas - The gas used for validation (including preValidationGas) - * @param prefund - The required prefund for this operation - * @param accountValidationData - returned validationData from account. - * @param paymasterValidationData - return validationData from paymaster. - * @param paymasterContext - Returned by validatePaymasterUserOp (to be passed into postOp) - */ - struct ReturnInfo { - uint256 preOpGas; - uint256 prefund; - uint256 accountValidationData; - uint256 paymasterValidationData; - bytes paymasterContext; - } - - /** - * Returned aggregated signature info: - * The aggregator returned by the account, and its current stake. - */ - struct AggregatorStakeInfo { - address aggregator; - StakeInfo stakeInfo; - } - - /** - * Get counterfactual sender address. - * Calculate the sender contract address that will be generated by the initCode and salt in the UserOperation. - * This method always revert, and returns the address in SenderAddressResult error - * @param initCode - The constructor code to be passed into the UserOperation. - */ - function getSenderAddress(bytes memory initCode) external; - - error DelegateAndRevert(bool success, bytes ret); - - /** - * Helper method for dry-run testing. - * @dev calling this method, the EntryPoint will make a delegatecall to the given data, and report (via revert) the result. - * The method always revert, so is only useful off-chain for dry run calls, in cases where state-override to replace - * actual EntryPoint code is less convenient. - * @param target a target contract to make a delegatecall from entrypoint - * @param data data to pass to target in a delegatecall - */ - function delegateAndRevert(address target, bytes calldata data) external; -} diff --git a/contracts/prebuilts/account/interfaces/INonceManager.sol b/contracts/prebuilts/account/interfaces/INonceManager.sol deleted file mode 100644 index 08f3e6525..000000000 --- a/contracts/prebuilts/account/interfaces/INonceManager.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.0; - -interface INonceManager { - /** - * Return the next nonce for this sender. - * Within a given key, the nonce values are sequenced (starting with zero, and incremented by one on each userop) - * But UserOp with different keys can come with arbitrary order. - * - * @param sender the account address - * @param key the high 192 bit of the nonce - * @return nonce a full nonce to pass for next UserOp with this sender. - */ - function getNonce(address sender, uint192 key) external view returns (uint256 nonce); - - /** - * Manually increment the nonce of the sender. - * This method is exposed just for completeness.. - * Account does NOT need to call it, neither during validation, nor elsewhere, - * as the EntryPoint will update the nonce regardless. - * Possible use-case is call it with various keys to "initialize" their nonces to one, so that future - * UserOperations will not pay extra for the first transaction with a given key. - */ - function incrementNonce(uint192 key) external; -} diff --git a/contracts/prebuilts/account/interfaces/IOracle.sol b/contracts/prebuilts/account/interfaces/IOracle.sol deleted file mode 100644 index bef4f2d3c..000000000 --- a/contracts/prebuilts/account/interfaces/IOracle.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.23; - -interface IOracle { - function decimals() external view returns (uint8); - function latestRoundData() - external - view - returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); -} diff --git a/contracts/prebuilts/account/interfaces/IPaymaster.sol b/contracts/prebuilts/account/interfaces/IPaymaster.sol deleted file mode 100644 index b501c7148..000000000 --- a/contracts/prebuilts/account/interfaces/IPaymaster.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.0; - -import "./PackedUserOperation.sol"; - -/** - * The interface exposed by a paymaster contract, who agrees to pay the gas for user's operations. - * A paymaster must hold a stake to cover the required entrypoint stake and also the gas for the transaction. - */ -interface IPaymaster { - enum PostOpMode { - // User op succeeded. - opSucceeded, - // User op reverted. Still has to pay for gas. - opReverted, - // Only used internally in the EntryPoint (cleanup after postOp reverts). Never calling paymaster with this value - postOpReverted - } - - /** - * Payment validation: check if paymaster agrees to pay. - * Must verify sender is the entryPoint. - * Revert to reject this request. - * Note that bundlers will reject this method if it changes the state, unless the paymaster is trusted (whitelisted). - * The paymaster pre-pays using its deposit, and receive back a refund after the postOp method returns. - * @param userOp - The user operation. - * @param userOpHash - Hash of the user's request data. - * @param maxCost - The maximum cost of this transaction (based on maximum gas and gas price from userOp). - * @return context - Value to send to a postOp. Zero length to signify postOp is not required. - * @return validationData - Signature and time-range of this operation, encoded the same as the return - * value of validateUserOperation. - * <20-byte> sigAuthorizer - 0 for valid signature, 1 to mark signature failure, - * other values are invalid for paymaster. - * <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite" - * <6-byte> validAfter - first timestamp this operation is valid - * Note that the validation code cannot use block.timestamp (or block.number) directly. - */ - function validatePaymasterUserOp( - PackedUserOperation calldata userOp, - bytes32 userOpHash, - uint256 maxCost - ) external returns (bytes memory context, uint256 validationData); - - /** - * Post-operation handler. - * Must verify sender is the entryPoint. - * @param mode - Enum with the following options: - * opSucceeded - User operation succeeded. - * opReverted - User op reverted. The paymaster still has to pay for gas. - * postOpReverted - never passed in a call to postOp(). - * @param context - The context value returned by validatePaymasterUserOp - * @param actualGasCost - Actual gas used so far (without this postOp call). - * @param actualUserOpFeePerGas - the gas price this UserOp pays. This value is based on the UserOp's maxFeePerGas - * and maxPriorityFee (and basefee) - * It is not the same as tx.gasprice, which is what the bundler pays. - */ - function postOp( - PostOpMode mode, - bytes calldata context, - uint256 actualGasCost, - uint256 actualUserOpFeePerGas - ) external; -} diff --git a/contracts/prebuilts/account/interfaces/IStakeManager.sol b/contracts/prebuilts/account/interfaces/IStakeManager.sol deleted file mode 100644 index 710522cc7..000000000 --- a/contracts/prebuilts/account/interfaces/IStakeManager.sol +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -pragma solidity ^0.8.0; - -/** - * Manage deposits and stakes. - * Deposit is just a balance used to pay for UserOperations (either by a paymaster or an account). - * Stake is value locked for at least "unstakeDelay" by the staked entity. - */ -interface IStakeManager { - event Deposited(address indexed account, uint256 totalDeposit); - - event Withdrawn(address indexed account, address withdrawAddress, uint256 amount); - - // Emitted when stake or unstake delay are modified. - event StakeLocked(address indexed account, uint256 totalStaked, uint256 unstakeDelaySec); - - // Emitted once a stake is scheduled for withdrawal. - event StakeUnlocked(address indexed account, uint256 withdrawTime); - - event StakeWithdrawn(address indexed account, address withdrawAddress, uint256 amount); - - /** - * @param deposit - The entity's deposit. - * @param staked - True if this entity is staked. - * @param stake - Actual amount of ether staked for this entity. - * @param unstakeDelaySec - Minimum delay to withdraw the stake. - * @param withdrawTime - First block timestamp where 'withdrawStake' will be callable, or zero if already locked. - * @dev Sizes were chosen so that deposit fits into one cell (used during handleOp) - * and the rest fit into a 2nd cell (used during stake/unstake) - * - 112 bit allows for 10^15 eth - * - 48 bit for full timestamp - * - 32 bit allows 150 years for unstake delay - */ - struct DepositInfo { - uint256 deposit; - bool staked; - uint112 stake; - uint32 unstakeDelaySec; - uint48 withdrawTime; - } - - // API struct used by getStakeInfo and simulateValidation. - struct StakeInfo { - uint256 stake; - uint256 unstakeDelaySec; - } - - /** - * Get deposit info. - * @param account - The account to query. - * @return info - Full deposit information of given account. - */ - function getDepositInfo(address account) external view returns (DepositInfo memory info); - - /** - * Get account balance. - * @param account - The account to query. - * @return - The deposit (for gas payment) of the account. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * Add to the deposit of the given account. - * @param account - The account to add to. - */ - function depositTo(address account) external payable; - - /** - * Add to the account's stake - amount and delay - * any pending unstake is first cancelled. - * @param _unstakeDelaySec - The new lock duration before the deposit can be withdrawn. - */ - function addStake(uint32 _unstakeDelaySec) external payable; - - /** - * Attempt to unlock the stake. - * The value can be withdrawn (using withdrawStake) after the unstake delay. - */ - function unlockStake() external; - - /** - * Withdraw from the (unlocked) stake. - * Must first call unlockStake and wait for the unstakeDelay to pass. - * @param withdrawAddress - The address to send withdrawn value. - */ - function withdrawStake(address payable withdrawAddress) external; - - /** - * Withdraw from the deposit. - * @param withdrawAddress - The address to send withdrawn value. - * @param withdrawAmount - The amount to withdraw. - */ - function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external; -} diff --git a/contracts/prebuilts/account/interfaces/PackedUserOperation.sol b/contracts/prebuilts/account/interfaces/PackedUserOperation.sol deleted file mode 100644 index d1e14a8ea..000000000 --- a/contracts/prebuilts/account/interfaces/PackedUserOperation.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.0; - -/** - * User Operation struct - * @param sender - The sender account of this request. - * @param nonce - Unique value the sender uses to verify it is not a replay. - * @param initCode - If set, the account contract will be created by this constructor/ - * @param callData - The method call to execute on this account. - * @param accountGasLimits - Packed gas limits for validateUserOp and gas limit passed to the callData method call. - * @param preVerificationGas - Gas not calculated by the handleOps method, but added to the gas paid. - * Covers batch overhead. - * @param gasFees - packed gas fields maxPriorityFeePerGas and maxFeePerGas - Same as EIP-1559 gas parameters. - * @param paymasterAndData - If set, this field holds the paymaster address, verification gas limit, postOp gas limit and paymaster-specific extra data - * The paymaster will pay for the transaction instead of the sender. - * @param signature - Sender-verified signature over the entire request, the EntryPoint address and the chain ID. - */ -struct PackedUserOperation { - address sender; - uint256 nonce; - bytes initCode; - bytes callData; - bytes32 accountGasLimits; - uint256 preVerificationGas; - bytes32 gasFees; - bytes paymasterAndData; - bytes signature; -} diff --git a/contracts/prebuilts/account/managed/ManagedAccount.sol b/contracts/prebuilts/account/managed/ManagedAccount.sol deleted file mode 100644 index 4e70bb605..000000000 --- a/contracts/prebuilts/account/managed/ManagedAccount.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -/* solhint-disable avoid-low-level-calls */ -/* solhint-disable no-inline-assembly */ -/* solhint-disable reason-string */ - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -import "../utils/AccountCore.sol"; -import "@thirdweb-dev/dynamic-contracts/src/core/Router.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IRouterState.sol"; - -contract ManagedAccount is AccountCore, Router, IRouterState { - constructor(IEntryPoint _entrypoint, address _factory) AccountCore(_entrypoint, _factory) {} - - /// @notice Returns the implementation contract address for a given function signature. - function getImplementationForFunction(bytes4 _functionSelector) public view virtual override returns (address) { - return Router(payable(factory)).getImplementationForFunction(_functionSelector); - } - - /// @notice Returns all extensions of the Router. - function getAllExtensions() external view returns (Extension[] memory) { - return IRouterState(payable(factory)).getAllExtensions(); - } -} diff --git a/contracts/prebuilts/account/managed/ManagedAccountFactory.sol b/contracts/prebuilts/account/managed/ManagedAccountFactory.sol deleted file mode 100644 index c5f51e3ca..000000000 --- a/contracts/prebuilts/account/managed/ManagedAccountFactory.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.12; - -// Utils -import "@thirdweb-dev/dynamic-contracts/src/presets/BaseRouter.sol"; -import "../utils/BaseAccountFactory.sol"; - -// Extensions -import "../../../extension/upgradeable//PermissionsEnumerable.sol"; -import "../../../extension/upgradeable//ContractMetadata.sol"; - -// Smart wallet implementation -import { ManagedAccount, IEntryPoint } from "./ManagedAccount.sol"; - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -contract ManagedAccountFactory is BaseAccountFactory, ContractMetadata, PermissionsEnumerable, BaseRouter { - /*/////////////////////////////////////////////////////////////// - Constructor - //////////////////////////////////////////////////////////////*/ - - constructor( - address _defaultAdmin, - IEntryPoint _entrypoint, - Extension[] memory _defaultExtensions - ) - BaseRouter(_defaultExtensions) - BaseAccountFactory(payable(address(new ManagedAccount(_entrypoint, address(this)))), address(_entrypoint)) - { - __BaseRouter_init(); - _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); - - bytes32 _extensionRole = keccak256("EXTENSION_ROLE"); - _setupRole(_extensionRole, _defaultAdmin); - _setRoleAdmin(_extensionRole, _extensionRole); - } - - /*/////////////////////////////////////////////////////////////// - Internal functions - //////////////////////////////////////////////////////////////*/ - - /// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`. - function _initializeAccount(address _account, address _admin, bytes calldata _data) internal override { - ManagedAccount(payable(_account)).initialize(_admin, _data); - } - - /// @dev Returns whether all relevant permission and other checks are met before any upgrade. - function _isAuthorizedCallToUpgrade() internal view virtual override returns (bool) { - return hasRole(keccak256("EXTENSION_ROLE"), msg.sender); - } - - /// @dev Returns whether contract metadata can be set in the given execution context. - function _canSetContractURI() internal view virtual override returns (bool) { - return hasRole(DEFAULT_ADMIN_ROLE, msg.sender); - } - - /// @notice Returns the sender in the given execution context. - function _msgSender() internal view override(Multicall, Permissions) returns (address) { - return msg.sender; - } -} diff --git a/contracts/prebuilts/account/non-upgradeable/Account.sol b/contracts/prebuilts/account/non-upgradeable/Account.sol deleted file mode 100644 index 14e2a5463..000000000 --- a/contracts/prebuilts/account/non-upgradeable/Account.sol +++ /dev/null @@ -1,169 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -/* solhint-disable avoid-low-level-calls */ -/* solhint-disable no-inline-assembly */ -/* solhint-disable reason-string */ - -// Extensions -import "../utils/AccountCore.sol"; -import "../../../extension/upgradeable/ContractMetadata.sol"; -import "../../../external-deps/openzeppelin/token/ERC721/utils/ERC721Holder.sol"; -import "../../../external-deps/openzeppelin/token/ERC1155/utils/ERC1155Holder.sol"; - -// Utils -import "../../../eip/ERC1271.sol"; -import "../utils/Helpers.sol"; -import "../../../external-deps/openzeppelin/utils/cryptography/ECDSA.sol"; -import "../utils/BaseAccountFactory.sol"; - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -contract Account is AccountCore, ContractMetadata, ERC1271, ERC721Holder, ERC1155Holder { - using ECDSA for bytes32; - using EnumerableSet for EnumerableSet.AddressSet; - - bytes32 private constant MSG_TYPEHASH = keccak256("AccountMessage(bytes message)"); - - /*/////////////////////////////////////////////////////////////// - Constructor, Initializer, Modifiers - //////////////////////////////////////////////////////////////*/ - - constructor(IEntryPoint _entrypoint, address _factory) AccountCore(_entrypoint, _factory) {} - - /// @notice Checks whether the caller is the EntryPoint contract or the admin. - modifier onlyAdminOrEntrypoint() virtual { - require(msg.sender == address(entryPoint()) || isAdmin(msg.sender), "Account: not admin or EntryPoint."); - _; - } - - /// @notice Lets the account receive native tokens. - receive() external payable {} - - /*/////////////////////////////////////////////////////////////// - View functions - //////////////////////////////////////////////////////////////*/ - - /// @notice See {IERC165-supportsInterface}. - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155Receiver) returns (bool) { - return - interfaceId == type(IERC1155Receiver).interfaceId || - interfaceId == type(IERC721Receiver).interfaceId || - super.supportsInterface(interfaceId); - } - - /** - * @notice See EIP-1271 - * - * @param _hash The original message hash of the data to sign (before mixing this contract's domain separator) - * @param _signature The signature produced on signing the typed data hash (result of `getMessageHash(abi.encode(rawData))`) - */ - function isValidSignature( - bytes32 _hash, - bytes memory _signature - ) public view virtual override returns (bytes4 magicValue) { - bytes32 targetDigest = getMessageHash(_hash); - address signer = targetDigest.recover(_signature); - - if (isAdmin(signer)) { - return MAGICVALUE; - } - - address caller = msg.sender; - EnumerableSet.AddressSet storage approvedTargets = _accountPermissionsStorage().approvedTargets[signer]; - - require( - approvedTargets.contains(caller) || (approvedTargets.length() == 1 && approvedTargets.at(0) == address(0)), - "Account: caller not approved target." - ); - - if (isActiveSigner(signer)) { - magicValue = MAGICVALUE; - } - } - - /** - * @notice Returns the hash of message that should be signed for EIP1271 verification. - * @param _hash The message hash to sign for the EIP-1271 origin verifying contract. - * @return messageHash The digest to sign for EIP-1271 verification. - */ - function getMessageHash(bytes32 _hash) public view returns (bytes32) { - bytes32 messageHash = keccak256(abi.encode(_hash)); - bytes32 typedDataHash = keccak256(abi.encode(MSG_TYPEHASH, messageHash)); - return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), typedDataHash)); - } - - /*/////////////////////////////////////////////////////////////// - External functions - //////////////////////////////////////////////////////////////*/ - - /// @notice Executes a transaction (called directly from an admin, or by entryPoint) - function execute(address _target, uint256 _value, bytes calldata _calldata) external virtual onlyAdminOrEntrypoint { - _registerOnFactory(); - _call(_target, _value, _calldata); - } - - /// @notice Executes a sequence transaction (called directly from an admin, or by entryPoint) - function executeBatch( - address[] calldata _target, - uint256[] calldata _value, - bytes[] calldata _calldata - ) external virtual onlyAdminOrEntrypoint { - _registerOnFactory(); - - require(_target.length == _calldata.length && _target.length == _value.length, "Account: wrong array lengths."); - for (uint256 i = 0; i < _target.length; i++) { - _call(_target[i], _value[i], _calldata[i]); - } - } - - /// @notice Deposit funds for this account in Entrypoint. - function addDeposit() public payable { - entryPoint().depositTo{ value: msg.value }(address(this)); - } - - /// @notice Withdraw funds for this account from Entrypoint. - function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public { - _onlyAdmin(); - entryPoint().withdrawTo(withdrawAddress, amount); - } - - /*/////////////////////////////////////////////////////////////// - Internal functions - //////////////////////////////////////////////////////////////*/ - - /// @dev Registers the account on the factory if it hasn't been registered yet. - function _registerOnFactory() internal virtual { - BaseAccountFactory factoryContract = BaseAccountFactory(factory); - if (!factoryContract.isRegistered(address(this))) { - factoryContract.onRegister(AccountCoreStorage.data().creationSalt); - } - } - - /// @dev Calls a target contract and reverts if it fails. - function _call( - address _target, - uint256 value, - bytes memory _calldata - ) internal virtual returns (bytes memory result) { - bool success; - (success, result) = _target.call{ value: value }(_calldata); - if (!success) { - assembly { - revert(add(result, 32), mload(result)) - } - } - } - - /// @dev Returns whether contract metadata can be set in the given execution context. - function _canSetContractURI() internal view virtual override returns (bool) { - return isAdmin(msg.sender) || msg.sender == address(this); - } -} diff --git a/contracts/prebuilts/account/non-upgradeable/AccountFactory.sol b/contracts/prebuilts/account/non-upgradeable/AccountFactory.sol deleted file mode 100644 index 1ab5ce47c..000000000 --- a/contracts/prebuilts/account/non-upgradeable/AccountFactory.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.12; - -// Utils -import "../utils/BaseAccountFactory.sol"; -import "../../../external-deps/openzeppelin/proxy/Clones.sol"; - -// Extensions -import "../../../extension/upgradeable//PermissionsEnumerable.sol"; -import "../../../extension/upgradeable//ContractMetadata.sol"; - -// Smart wallet implementation -import { Account } from "./Account.sol"; - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -contract AccountFactory is BaseAccountFactory, ContractMetadata, PermissionsEnumerable { - /*/////////////////////////////////////////////////////////////// - Constructor - //////////////////////////////////////////////////////////////*/ - - constructor( - address _defaultAdmin, - IEntryPoint _entrypoint - ) BaseAccountFactory(address(new Account(_entrypoint, address(this))), address(_entrypoint)) { - _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); - } - - /*/////////////////////////////////////////////////////////////// - Internal functions - //////////////////////////////////////////////////////////////*/ - - /// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`. - function _initializeAccount(address _account, address _admin, bytes calldata _data) internal override { - Account(payable(_account)).initialize(_admin, _data); - } - - /// @dev Returns whether contract metadata can be set in the given execution context. - function _canSetContractURI() internal view virtual override returns (bool) { - return hasRole(DEFAULT_ADMIN_ROLE, msg.sender); - } - - /// @notice Returns the sender in the given execution context. - function _msgSender() internal view override(Multicall, Permissions) returns (address) { - return msg.sender; - } -} diff --git a/contracts/prebuilts/account/token-bound-account/TokenBoundAccount.sol b/contracts/prebuilts/account/token-bound-account/TokenBoundAccount.sol deleted file mode 100644 index e6ffda8ee..000000000 --- a/contracts/prebuilts/account/token-bound-account/TokenBoundAccount.sol +++ /dev/null @@ -1,252 +0,0 @@ -// SPDX-License-Identifier: Apache 2.0 -pragma solidity ^0.8.0; - -/* solhint-disable avoid-low-level-calls */ -/* solhint-disable no-inline-assembly */ -/* solhint-disable reason-string */ - -// Base -import "../utils/BaseAccount.sol"; - -// Extensions -import "../../../extension/Multicall.sol"; -import "../../../extension/upgradeable/Initializable.sol"; -import "../../../extension/upgradeable/ContractMetadata.sol"; -import "../../../external-deps/openzeppelin/token/ERC721/utils/ERC721Holder.sol"; -import "../../../external-deps/openzeppelin/token/ERC1155/utils/ERC1155Holder.sol"; -import "../../../eip/ERC1271.sol"; - -// Utils -import "../../../external-deps/openzeppelin/utils/cryptography/ECDSA.sol"; -import "../utils/BaseAccountFactory.sol"; - -import "./erc6551-utils/ERC6551AccountLib.sol"; -import "./erc6551-utils/IERC6551Account.sol"; - -import "../../../eip/interface/IERC721.sol"; -import "../non-upgradeable/Account.sol"; - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -contract TokenBoundAccount is - Initializable, - ERC1271, - Multicall, - BaseAccount, - ContractMetadata, - ERC721Holder, - ERC1155Holder, - IERC6551Account, - EIP712 -{ - using ECDSA for bytes32; - - /*/////////////////////////////////////////////////////////////// - Events - //////////////////////////////////////////////////////////////*/ - - event TokenBoundAccountCreated(address indexed account, bytes indexed data); - - /*/////////////////////////////////////////////////////////////// - State - //////////////////////////////////////////////////////////////*/ - - /// @notice EIP 4337 factory for this contract. - address public immutable factory; - - /// @notice EIP 4337 Entrypoint contract. - IEntryPoint private immutable entrypointContract; - - uint256 public state; - - /*/////////////////////////////////////////////////////////////// - Constructor, Initializer, Modifiers - //////////////////////////////////////////////////////////////*/ - - /** - * @notice Executes once when a contract is created to initialize state variables - * - * @param _entrypoint - 0x0000000071727De22E5E9d8BAf0edAc6f37da032 - * @param _factory - The factory contract address to issue token Bound accounts - * - */ - constructor(IEntryPoint _entrypoint, address _factory) EIP712("TokenBoundAccount", "1") { - _disableInitializers(); - factory = _factory; - entrypointContract = _entrypoint; - } - - // solhint-disable-next-line no-empty-blocks - receive() external payable virtual {} - - /// @notice Initializes the smart contract wallet. - function initialize(address _defaultAdmin, bytes calldata _data) public virtual initializer { - emit TokenBoundAccountCreated(_defaultAdmin, _data); - } - - /// @notice Returns whether a signer is authorized to perform transactions using the wallet. - function isValidSigner(address _signer, PackedUserOperation calldata) public view returns (bool) { - return (owner() == _signer); - } - - function isValidSigner(address signer, bytes calldata) external view returns (bytes4) { - if (_isValidSigner(signer)) { - return IERC6551Account.isValidSigner.selector; - } - return bytes4(0); - } - - function _isValidSigner(address signer) internal view returns (bool) { - return signer == owner(); - } - - /// @notice See EIP-1271 - function isValidSignature( - bytes32 _hash, - bytes memory _signature - ) public view virtual override returns (bytes4 magicValue) { - address signer = _hash.recover(_signature); - - if (owner() == signer) { - magicValue = MAGICVALUE; - } - } - - function owner() public view returns (address) { - (uint256 chainId, address tokenContract, uint256 tokenId) = ERC6551AccountLib.token(); - - if (chainId != block.chainid) return address(0); - - return IERC721(tokenContract).ownerOf(tokenId); - } - - /// @notice Withdraw funds for this account from Entrypoint. - function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public virtual { - require(owner() == msg.sender, "Account: not NFT owner"); - entryPoint().withdrawTo(withdrawAddress, amount); - } - - function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId) { - return ERC6551AccountLib.token(); - } - - /// @notice See {IERC165-supportsInterface}. - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155Receiver) returns (bool) { - return - interfaceId == type(IERC1155Receiver).interfaceId || - interfaceId == type(IERC721Receiver).interfaceId || - super.supportsInterface(interfaceId); - } - - /// @notice Returns the EIP 4337 entrypoint contract. - function entryPoint() public view virtual override returns (IEntryPoint) { - return entrypointContract; - } - - /// @notice Returns the balance of the account in Entrypoint. - function getDeposit() public view virtual returns (uint256) { - return entryPoint().balanceOf(address(this)); - } - - /// @notice Executes a transaction (called directly from an admin, or by entryPoint) - function execute(address _target, uint256 _value, bytes calldata _calldata) external virtual onlyAdminOrEntrypoint { - _call(_target, _value, _calldata); - } - - /// @notice Executes a sequence transaction (called directly from an admin, or by entryPoint) - function executeBatch( - address[] calldata _target, - uint256[] calldata _value, - bytes[] calldata _calldata - ) external virtual onlyAdminOrEntrypoint { - require(_target.length == _calldata.length && _target.length == _value.length, "Account: wrong array lengths."); - for (uint256 i = 0; i < _target.length; i++) { - _call(_target[i], _value[i], _calldata[i]); - } - } - - /// @notice Deposit funds for this account in Entrypoint. - function addDeposit() public payable virtual { - entryPoint().depositTo{ value: msg.value }(address(this)); - } - - /*/////////////////////////////////////////////////////////////// - Internal Functions - //////////////////////////////////////////////////////////////*/ - - function _call( - address _target, - uint256 value, - bytes memory _calldata - ) internal virtual returns (bytes memory result) { - ++state; - bool success; - (success, result) = _target.call{ value: value }(_calldata); - if (!success) { - assembly { - revert(add(result, 32), mload(result)) - } - } - } - - /// @notice Validates the signature of a user operation. - function _validateSignature( - PackedUserOperation calldata userOp, - bytes32 userOpHash - ) internal virtual override returns (uint256 validationData) { - bytes32 hash = userOpHash.toEthSignedMessageHash(); - address signer = hash.recover(userOp.signature); - - if (!isValidSigner(signer, userOp)) return SIG_VALIDATION_FAILED; - return 0; - } - - function getFunctionSignature(bytes calldata data) internal pure returns (bytes4 functionSelector) { - require(data.length >= 4, "Data too short"); - return bytes4(data[:4]); - } - - function decodeExecuteCalldata(bytes calldata data) internal pure returns (address _target, uint256 _value) { - require(data.length >= 4 + 32 + 32, "Data too short"); - - // Decode the address, which is bytes 4 to 35 - _target = abi.decode(data[4:36], (address)); - - // Decode the value, which is bytes 36 to 68 - _value = abi.decode(data[36:68], (uint256)); - } - - function decodeExecuteBatchCalldata( - bytes calldata data - ) internal pure returns (address[] memory _targets, uint256[] memory _values, bytes[] memory _callData) { - require(data.length >= 4 + 32 + 32 + 32, "Data too short"); - - (_targets, _values, _callData) = abi.decode(data[4:], (address[], uint256[], bytes[])); - } - - /// @dev Returns whether contract metadata can be set in the given execution context. - function _canSetContractURI() internal view virtual override returns (bool) { - return msg.sender == owner(); - } - - /*/////////////////////////////////////////////////////////////// - Modifiers - //////////////////////////////////////////////////////////////*/ - - modifier onlyAdminOrEntrypoint() { - require(msg.sender == address(entryPoint()) || msg.sender == owner(), "Account: not admin or EntryPoint."); - _; - } - - modifier onlyAdmin() { - require(msg.sender == owner(), "Account: not admin."); - _; - } -} diff --git a/contracts/prebuilts/account/token-bound-account/erc6551-utils/ERC6551AccountLib.sol b/contracts/prebuilts/account/token-bound-account/erc6551-utils/ERC6551AccountLib.sol deleted file mode 100644 index ff99e843c..000000000 --- a/contracts/prebuilts/account/token-bound-account/erc6551-utils/ERC6551AccountLib.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "../../../../external-deps/openzeppelin/utils/Create2.sol"; -import "./ERC6551BytecodeLib.sol"; - -library ERC6551AccountLib { - function computeAddress( - address registry, - address implementation, - uint256 chainId, - address tokenContract, - uint256 tokenId, - uint256 _salt - ) internal pure returns (address) { - bytes32 bytecodeHash = keccak256( - ERC6551BytecodeLib.getCreationCode(implementation, chainId, tokenContract, tokenId, _salt) - ); - - return Create2.computeAddress(bytes32(_salt), bytecodeHash, registry); - } - - function token() internal view returns (uint256, address, uint256) { - bytes memory footer = new bytes(0x60); - - assembly { - // copy 0x60 bytes from end of footer - extcodecopy(address(), add(footer, 0x20), 0x4d, 0xad) - } - - return abi.decode(footer, (uint256, address, uint256)); - } - - function salt() internal view returns (uint256) { - bytes memory footer = new bytes(0x20); - - assembly { - // copy 0x20 bytes from beginning of footer - extcodecopy(address(), add(footer, 0x20), 0x2d, 0x4d) - } - - return abi.decode(footer, (uint256)); - } -} diff --git a/contracts/prebuilts/account/token-bound-account/erc6551-utils/ERC6551BytecodeLib.sol b/contracts/prebuilts/account/token-bound-account/erc6551-utils/ERC6551BytecodeLib.sol deleted file mode 100644 index 604ba74d2..000000000 --- a/contracts/prebuilts/account/token-bound-account/erc6551-utils/ERC6551BytecodeLib.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -library ERC6551BytecodeLib { - function getCreationCode( - address implementation_, - uint256 chainId_, - address tokenContract_, - uint256 tokenId_, - uint256 salt_ - ) internal pure returns (bytes memory) { - return - abi.encodePacked( - hex"3d60ad80600a3d3981f3363d3d373d3d3d363d73", - implementation_, - hex"5af43d82803e903d91602b57fd5bf3", - abi.encode(salt_, chainId_, tokenContract_, tokenId_) - ); - } -} diff --git a/contracts/prebuilts/account/token-bound-account/erc6551-utils/IERC6551Account.sol b/contracts/prebuilts/account/token-bound-account/erc6551-utils/IERC6551Account.sol deleted file mode 100644 index 160e27e7b..000000000 --- a/contracts/prebuilts/account/token-bound-account/erc6551-utils/IERC6551Account.sol +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -/// @dev the ERC-165 identifier for this interface is `0x6faff5f1` -interface IERC6551Account { - /** - * @dev Allows the account to receive Ether - * - * Accounts MUST implement a `receive` function - * - * Accounts MAY perform arbitrary logic to restrict conditions - * under which Ether can be received - */ - receive() external payable; - - /** - * @dev Returns the identifier of the non-fungible token which owns the account - * - * The return value of this function MUST be constant - it MUST NOT change over time - * - * @return chainId The EIP-155 ID of the chain the token exists on - * @return tokenContract The contract address of the token - * @return tokenId The ID of the token - */ - function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId); - - /** - * @dev Returns a value that SHOULD be modified each time the account changes state - * - * @return The current account state - */ - function state() external view returns (uint256); - - /** - * @dev Returns a magic value indicating whether a given signer is authorized to act on behalf - * of the account - * - * MUST return the bytes4 magic value 0x523e3260 if the given signer is valid - * - * By default, the holder of the non-fungible token the account is bound to MUST be considered - * a valid signer - * - * Accounts MAY implement additional authorization logic which invalidates the holder as a - * signer or grants signing permissions to other non-holder accounts - * - * @param signer The address to check signing authorization for - * @param context Additional data used to determine whether the signer is valid - * @return magicValue Magic value indicating whether the signer is valid - */ - function isValidSigner(address signer, bytes calldata context) external view returns (bytes4 magicValue); -} diff --git a/contracts/prebuilts/account/token-paymaster/BasePaymaster.sol b/contracts/prebuilts/account/token-paymaster/BasePaymaster.sol deleted file mode 100644 index a1b337ab3..000000000 --- a/contracts/prebuilts/account/token-paymaster/BasePaymaster.sol +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -/* solhint-disable reason-string */ - -import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import "../interfaces/IPaymaster.sol"; -import "../interfaces/IEntryPoint.sol"; -import "../utils/UserOperationLib.sol"; -/** - * Helper class for creating a paymaster. - * provides helper methods for staking. - * Validates that the postOp is called only by the entryPoint. - */ -abstract contract BasePaymaster is IPaymaster, Ownable { - IEntryPoint public immutable entryPoint; - - uint256 internal constant PAYMASTER_VALIDATION_GAS_OFFSET = UserOperationLib.PAYMASTER_VALIDATION_GAS_OFFSET; - uint256 internal constant PAYMASTER_POSTOP_GAS_OFFSET = UserOperationLib.PAYMASTER_POSTOP_GAS_OFFSET; - uint256 internal constant PAYMASTER_DATA_OFFSET = UserOperationLib.PAYMASTER_DATA_OFFSET; - - constructor(IEntryPoint _entryPoint) Ownable() { - _validateEntryPointInterface(_entryPoint); - entryPoint = _entryPoint; - } - - //sanity check: make sure this EntryPoint was compiled against the same - // IEntryPoint of this paymaster - function _validateEntryPointInterface(IEntryPoint _entryPoint) internal virtual { - require( - IERC165(address(_entryPoint)).supportsInterface(type(IEntryPoint).interfaceId), - "IEntryPoint interface mismatch" - ); - } - - /// @inheritdoc IPaymaster - function validatePaymasterUserOp( - PackedUserOperation calldata userOp, - bytes32 userOpHash, - uint256 maxCost - ) external override returns (bytes memory context, uint256 validationData) { - _requireFromEntryPoint(); - return _validatePaymasterUserOp(userOp, userOpHash, maxCost); - } - - /** - * Validate a user operation. - * @param userOp - The user operation. - * @param userOpHash - The hash of the user operation. - * @param maxCost - The maximum cost of the user operation. - */ - function _validatePaymasterUserOp( - PackedUserOperation calldata userOp, - bytes32 userOpHash, - uint256 maxCost - ) internal virtual returns (bytes memory context, uint256 validationData); - - /// @inheritdoc IPaymaster - function postOp( - PostOpMode mode, - bytes calldata context, - uint256 actualGasCost, - uint256 actualUserOpFeePerGas - ) external override { - _requireFromEntryPoint(); - _postOp(mode, context, actualGasCost, actualUserOpFeePerGas); - } - - /** - * Post-operation handler. - * (verified to be called only through the entryPoint) - * @dev If subclass returns a non-empty context from validatePaymasterUserOp, - * it must also implement this method. - * @param mode - Enum with the following options: - * opSucceeded - User operation succeeded. - * opReverted - User op reverted. The paymaster still has to pay for gas. - * postOpReverted - never passed in a call to postOp(). - * @param context - The context value returned by validatePaymasterUserOp - * @param actualGasCost - Actual gas used so far (without this postOp call). - * @param actualUserOpFeePerGas - the gas price this UserOp pays. This value is based on the UserOp's maxFeePerGas - * and maxPriorityFee (and basefee) - * It is not the same as tx.gasprice, which is what the bundler pays. - */ - function _postOp( - PostOpMode mode, - bytes calldata context, - uint256 actualGasCost, - uint256 actualUserOpFeePerGas - ) internal virtual { - (mode, context, actualGasCost, actualUserOpFeePerGas); // unused params - // subclass must override this method if validatePaymasterUserOp returns a context - revert("must override"); - } - - /** - * Add a deposit for this paymaster, used for paying for transaction fees. - */ - function deposit() public payable { - entryPoint.depositTo{ value: msg.value }(address(this)); - } - - /** - * Withdraw value from the deposit. - * @param withdrawAddress - Target to send to. - * @param amount - Amount to withdraw. - */ - function withdrawTo(address payable withdrawAddress, uint256 amount) public onlyOwner { - entryPoint.withdrawTo(withdrawAddress, amount); - } - - /** - * Add stake for this paymaster. - * This method can also carry eth value to add to the current stake. - * @param unstakeDelaySec - The unstake delay for this paymaster. Can only be increased. - */ - function addStake(uint32 unstakeDelaySec) external payable onlyOwner { - entryPoint.addStake{ value: msg.value }(unstakeDelaySec); - } - - /** - * Return current paymaster's deposit on the entryPoint. - */ - function getDeposit() public view returns (uint256) { - return entryPoint.balanceOf(address(this)); - } - - /** - * Unlock the stake, in order to withdraw it. - * The paymaster can't serve requests once unlocked, until it calls addStake again - */ - function unlockStake() external onlyOwner { - entryPoint.unlockStake(); - } - - /** - * Withdraw the entire paymaster's stake. - * stake must be unlocked first (and then wait for the unstakeDelay to be over) - * @param withdrawAddress - The address to send withdrawn value. - */ - function withdrawStake(address payable withdrawAddress) external onlyOwner { - entryPoint.withdrawStake(withdrawAddress); - } - - /** - * Validate the call is made from a valid entrypoint - */ - function _requireFromEntryPoint() internal virtual { - require(msg.sender == address(entryPoint), "Sender not EntryPoint"); - } -} diff --git a/contracts/prebuilts/account/token-paymaster/TokenPaymaster.sol b/contracts/prebuilts/account/token-paymaster/TokenPaymaster.sol deleted file mode 100644 index c7104c763..000000000 --- a/contracts/prebuilts/account/token-paymaster/TokenPaymaster.sol +++ /dev/null @@ -1,212 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -// Import the required libraries and contracts -import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; - -import "../interfaces/IEntryPoint.sol"; -import "./BasePaymaster.sol"; -import "../utils/Helpers.sol"; -import "../utils/UniswapHelper.sol"; -import "../utils/OracleHelper.sol"; - -/// @title Sample ERC-20 Token Paymaster for ERC-4337 -/// This Paymaster covers gas fees in exchange for ERC20 tokens charged using allowance pre-issued by ERC-4337 accounts. -/// The contract refunds excess tokens if the actual gas cost is lower than the initially provided amount. -/// The token price cannot be queried in the validation code due to storage access restrictions of ERC-4337. -/// The price is cached inside the contract and is updated in the 'postOp' stage if the change is >10%. -/// It is theoretically possible the token has depreciated so much since the last 'postOp' the refund becomes negative. -/// The contract reverts the inner user transaction in that case but keeps the charge. -/// The contract also allows honest clients to prepay tokens at a higher price to avoid getting reverted. -/// It also allows updating price configuration and withdrawing tokens by the contract owner. -/// The contract uses an Oracle to fetch the latest token prices. -/// @dev Inherits from BasePaymaster. -contract TokenPaymaster is BasePaymaster, UniswapHelper, OracleHelper { - using UserOperationLib for PackedUserOperation; - - struct TokenPaymasterConfig { - /// @notice The price markup percentage applied to the token price (1e26 = 100%). Ranges from 1e26 to 2e26 - uint256 priceMarkup; - /// @notice Exchange tokens to native currency if the EntryPoint balance of this Paymaster falls below this value - uint128 minEntryPointBalance; - /// @notice Estimated gas cost for refunding tokens after the transaction is completed - uint48 refundPostopCost; - /// @notice Transactions are only valid as long as the cached price is not older than this value - uint48 priceMaxAge; - } - - event ConfigUpdated(TokenPaymasterConfig tokenPaymasterConfig); - - event UserOperationSponsored( - address indexed user, - uint256 actualTokenCharge, - uint256 actualGasCost, - uint256 actualTokenPriceWithMarkup - ); - - event Received(address indexed sender, uint256 value); - - /// @notice All 'price' variables are multiplied by this value to avoid rounding up - uint256 private constant PRICE_DENOMINATOR = 1e26; - - TokenPaymasterConfig public tokenPaymasterConfig; - - uint256 private immutable _tokenDecimals; - - /// @notice Initializes the TokenPaymaster contract with the given parameters. - /// @param _token The ERC20 token used for transaction fee payments. - /// @param _entryPoint The EntryPoint contract used in the Account Abstraction infrastructure. - /// @param _wrappedNative The ERC-20 token that wraps the native asset for current chain. - /// @param _uniswap The Uniswap V3 SwapRouter contract. - /// @param _tokenPaymasterConfig The configuration for the Token Paymaster. - /// @param _oracleHelperConfig The configuration for the Oracle Helper. - /// @param _uniswapHelperConfig The configuration for the Uniswap Helper. - /// @param _owner The address that will be set as the owner of the contract. - constructor( - IERC20Metadata _token, - IEntryPoint _entryPoint, - IERC20 _wrappedNative, - IV3SwapRouter _uniswap, - TokenPaymasterConfig memory _tokenPaymasterConfig, - OracleHelperConfig memory _oracleHelperConfig, - UniswapHelperConfig memory _uniswapHelperConfig, - address _owner - ) - BasePaymaster(_entryPoint) - OracleHelper(_oracleHelperConfig) - UniswapHelper(_token, _wrappedNative, _uniswap, _uniswapHelperConfig) - { - _tokenDecimals = _token.decimals(); - require(_tokenDecimals <= 18, "TPM: token not supported"); - - setTokenPaymasterConfig(_tokenPaymasterConfig); - transferOwnership(_owner); - } - - /// @notice Updates the configuration for the Token Paymaster. - /// @param _tokenPaymasterConfig The new configuration struct. - function setTokenPaymasterConfig(TokenPaymasterConfig memory _tokenPaymasterConfig) public onlyOwner { - require(_tokenPaymasterConfig.priceMarkup <= 2 * PRICE_DENOMINATOR, "TPM: price markup too high"); - require(_tokenPaymasterConfig.priceMarkup >= PRICE_DENOMINATOR, "TPM: price markup too low"); - tokenPaymasterConfig = _tokenPaymasterConfig; - emit ConfigUpdated(_tokenPaymasterConfig); - } - - function setUniswapConfiguration(UniswapHelperConfig memory _uniswapHelperConfig) external onlyOwner { - _setUniswapHelperConfiguration(_uniswapHelperConfig); - } - - function setOracleConfiguration(OracleHelperConfig memory _oracleHelperConfig) external onlyOwner { - _setOracleConfiguration(_oracleHelperConfig); - } - - /// @notice Allows the contract owner to withdraw a specified amount of tokens from the contract. - /// @param to The address to transfer the tokens to. - /// @param amount The amount of tokens to transfer. - function withdrawToken(address to, uint256 amount) external onlyOwner { - SafeERC20.safeTransfer(token, to, amount); - } - - /// @notice Validates a paymaster user operation and calculates the required token amount for the transaction. - /// @param userOp The user operation data. - /// @param requiredPreFund The maximum cost (in native token) the paymaster has to prefund. - /// @return context The context containing the token amount and user sender address (if applicable). - /// @return validationResult A uint256 value indicating the result of the validation (always 0 in this implementation). - function _validatePaymasterUserOp( - PackedUserOperation calldata userOp, - bytes32, - uint256 requiredPreFund - ) internal override returns (bytes memory context, uint256 validationResult) { - unchecked { - uint256 priceMarkup = tokenPaymasterConfig.priceMarkup; - uint256 dataLength = userOp.paymasterAndData.length - PAYMASTER_DATA_OFFSET; - require(dataLength == 0 || dataLength == 32, "TPM: invalid data length"); - uint256 maxFeePerGas = userOp.unpackMaxFeePerGas(); - uint256 refundPostopCost = tokenPaymasterConfig.refundPostopCost; - require(refundPostopCost < userOp.unpackPostOpGasLimit(), "TPM: postOpGasLimit too low"); - uint256 preChargeNative = requiredPreFund + (refundPostopCost * maxFeePerGas); - - bool forceUpdate = (block.timestamp - cachedPriceTimestamp) > tokenPaymasterConfig.priceMaxAge; - updateCachedPrice(forceUpdate); - - // note: as price is in native-asset-per-token and we want more tokens increasing it means dividing it by markup - uint256 cachedPriceWithMarkup = (cachedPrice * PRICE_DENOMINATOR) / priceMarkup; - if (dataLength == 32) { - uint256 clientSuppliedPrice = uint256( - bytes32(userOp.paymasterAndData[PAYMASTER_DATA_OFFSET:PAYMASTER_DATA_OFFSET + 32]) - ); - if (clientSuppliedPrice < cachedPriceWithMarkup) { - // note: smaller number means 'more native asset per token' - cachedPriceWithMarkup = clientSuppliedPrice; - } - } - uint256 tokenAmount = weiToToken(preChargeNative, _tokenDecimals, cachedPriceWithMarkup); - SafeERC20.safeTransferFrom(token, userOp.sender, address(this), tokenAmount); - context = abi.encode(tokenAmount, userOp.sender); - validationResult = _packValidationData( - false, - uint48(cachedPriceTimestamp + tokenPaymasterConfig.priceMaxAge), - 0 - ); - } - } - - /// @notice Performs post-operation tasks, such as updating the token price and refunding excess tokens. - /// @dev This function is called after a user operation has been executed or reverted. - /// @param context The context containing the token amount and user sender address. - /// @param actualGasCost The actual gas cost of the transaction. - /// @param actualUserOpFeePerGas - the gas price this UserOp pays. This value is based on the UserOp's maxFeePerGas - // and maxPriorityFee (and basefee) - // It is not the same as tx.gasprice, which is what the bundler pays. - function _postOp( - PostOpMode, - bytes calldata context, - uint256 actualGasCost, - uint256 actualUserOpFeePerGas - ) internal override { - unchecked { - uint256 priceMarkup = tokenPaymasterConfig.priceMarkup; - (uint256 preCharge, address userOpSender) = abi.decode(context, (uint256, address)); - uint256 _cachedPrice = cachedPrice; - // note: as price is in native-asset-per-token and we want more tokens increasing it means dividing it by markup - uint256 cachedPriceWithMarkup = (_cachedPrice * PRICE_DENOMINATOR) / priceMarkup; - // Refund tokens based on actual gas cost - uint256 actualChargeNative = actualGasCost + tokenPaymasterConfig.refundPostopCost * actualUserOpFeePerGas; - uint256 actualTokenNeeded = weiToToken(actualChargeNative, _tokenDecimals, cachedPriceWithMarkup); - - if (preCharge > actualTokenNeeded) { - // If the initially provided token amount is greater than the actual amount needed, refund the difference - SafeERC20.safeTransfer(token, userOpSender, preCharge - actualTokenNeeded); - } else if (preCharge < actualTokenNeeded) { - // Attempt to cover Paymaster's gas expenses by withdrawing the 'overdraft' from the client - // If the transfer reverts also revert the 'postOp' to remove the incentive to cheat - SafeERC20.safeTransferFrom(token, userOpSender, address(this), actualTokenNeeded - preCharge); - } - - emit UserOperationSponsored(userOpSender, actualTokenNeeded, actualGasCost, cachedPriceWithMarkup); - refillEntryPointDeposit(_cachedPrice); - } - } - - /// @notice If necessary this function uses this Paymaster's token balance to refill the deposit on EntryPoint - /// @param _cachedPrice the token price that will be used to calculate the swap amount. - function refillEntryPointDeposit(uint256 _cachedPrice) private { - uint256 currentEntryPointBalance = entryPoint.balanceOf(address(this)); - if (currentEntryPointBalance < tokenPaymasterConfig.minEntryPointBalance) { - uint256 swappedWeth = _maybeSwapTokenToWeth(token, _cachedPrice); - unwrapWeth(swappedWeth); - entryPoint.depositTo{ value: address(this).balance }(address(this)); - } - } - - receive() external payable { - emit Received(msg.sender, msg.value); - } - - function withdrawEth(address payable recipient, uint256 amount) external onlyOwner { - (bool success, ) = recipient.call{ value: amount }(""); - require(success, "withdraw failed"); - } -} diff --git a/contracts/prebuilts/account/utils/AccountCore.sol b/contracts/prebuilts/account/utils/AccountCore.sol deleted file mode 100644 index 32c480ef9..000000000 --- a/contracts/prebuilts/account/utils/AccountCore.sol +++ /dev/null @@ -1,245 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -/* solhint-disable avoid-low-level-calls */ -/* solhint-disable no-inline-assembly */ -/* solhint-disable reason-string */ - -// Base -import "./BaseAccount.sol"; - -// Fixed Extensions -import "../../../extension/Multicall.sol"; -import "../../../extension/upgradeable/Initializable.sol"; -import "../../../extension/upgradeable/AccountPermissions.sol"; - -// Utils -import "./Helpers.sol"; -import "./AccountCoreStorage.sol"; -import "./BaseAccountFactory.sol"; -import { AccountExtension } from "./AccountExtension.sol"; -import "../../../external-deps/openzeppelin/utils/cryptography/ECDSA.sol"; - -import "../interfaces/IAccountCore.sol"; - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, AccountPermissions { - using ECDSA for bytes32; - using EnumerableSet for EnumerableSet.AddressSet; - - /*/////////////////////////////////////////////////////////////// - State - //////////////////////////////////////////////////////////////*/ - - /// @notice EIP 4337 factory for this contract. - address public immutable factory; - - /// @notice EIP 4337 Entrypoint contract. - IEntryPoint private immutable entrypointContract; - - /*/////////////////////////////////////////////////////////////// - Constructor, Initializer, Modifiers - //////////////////////////////////////////////////////////////*/ - - constructor(IEntryPoint _entrypoint, address _factory) EIP712("Account", "1") { - _disableInitializers(); - factory = _factory; - entrypointContract = _entrypoint; - } - - /// @notice Initializes the smart contract wallet. - function initialize(address _defaultAdmin, bytes calldata _data) public virtual initializer { - // This is passed as data in the `_registerOnFactory()` call in `AccountExtension` / `Account`. - AccountCoreStorage.data().creationSalt = _generateSalt(_defaultAdmin, _data); - _setAdmin(_defaultAdmin, true); - } - - /*/////////////////////////////////////////////////////////////// - View functions - //////////////////////////////////////////////////////////////*/ - - /// @notice Returns the EIP 4337 entrypoint contract. - function entryPoint() public view virtual override returns (IEntryPoint) { - address entrypointOverride = AccountCoreStorage.data().entrypointOverride; - if (address(entrypointOverride) != address(0)) { - return IEntryPoint(entrypointOverride); - } - return entrypointContract; - } - - /** - @notice Returns whether a signer is authorized to perform transactions using the account. - Validity of the signature is based upon signer permission start/end timestamps, txn target, and txn value. - Account admins will always return true, and signers with address(0) as the only approved target will skip target checks. - - @param _signer The signer to check. - @param _userOp The user operation to check. - - @return Whether the signer is authorized to perform the transaction. - */ - - /* solhint-disable*/ - function isValidSigner(address _signer, PackedUserOperation calldata _userOp) public view virtual returns (bool) { - // First, check if the signer is an admin. - if (_accountPermissionsStorage().isAdmin[_signer]) { - return true; - } - - SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[_signer]; - EnumerableSet.AddressSet storage approvedTargets = _accountPermissionsStorage().approvedTargets[_signer]; - - // If not an admin, check if the signer is active. - if ( - permissions.startTimestamp > block.timestamp || - block.timestamp >= permissions.endTimestamp || - approvedTargets.length() == 0 - ) { - // Account: no active permissions. - return false; - } - - // Extract the function signature from the userOp calldata and check whether the signer is attempting to call `execute` or `executeBatch`. - bytes4 sig = getFunctionSignature(_userOp.callData); - - // if address(0) is the only approved target, set isWildCard to true (wildcard approved). - bool isWildCard = approvedTargets.length() == 1 && approvedTargets.at(0) == address(0); - - // checking target and value for `execute` - if (sig == AccountExtension.execute.selector) { - // Extract the `target` and `value` arguments from the calldata for `execute`. - (address target, uint256 value) = decodeExecuteCalldata(_userOp.callData); - - // if wildcard target is not approved, check that the target is in the approvedTargets set. - if (!isWildCard) { - // Check if the target is approved. - if (!approvedTargets.contains(target)) { - // Account: target not approved. - return false; - } - } - - // Check if the value is within the allowed range. - if (permissions.nativeTokenLimitPerTransaction < value) { - // Account: value too high OR Account: target not approved. - return false; - } - } - // checking target and value for `executeBatch` - else if (sig == AccountExtension.executeBatch.selector) { - // Extract the `target` and `value` array arguments from the calldata for `executeBatch`. - (address[] memory targets, uint256[] memory values, ) = decodeExecuteBatchCalldata(_userOp.callData); - - // if wildcard target is not approved, check that the targets are in the approvedTargets set. - if (!isWildCard) { - for (uint256 i = 0; i < targets.length; i++) { - if (!approvedTargets.contains(targets[i])) { - // If any target is not approved, break the loop. - return false; - } - } - } - - // For each target+value pair, check if the value is within the allowed range. - for (uint256 i = 0; i < targets.length; i++) { - if (permissions.nativeTokenLimitPerTransaction < values[i]) { - // Account: value too high OR Account: target not approved. - return false; - } - } - } else { - // Account: calling invalid fn. - return false; - } - - return true; - } - - /* solhint-enable */ - - /*/////////////////////////////////////////////////////////////// - External functions - //////////////////////////////////////////////////////////////*/ - - /// @notice Overrides the Entrypoint contract being used. - function setEntrypointOverride(IEntryPoint _entrypointOverride) public virtual { - _onlyAdmin(); - AccountCoreStorage.data().entrypointOverride = address(_entrypointOverride); - } - - /*/////////////////////////////////////////////////////////////// - Internal functions - //////////////////////////////////////////////////////////////*/ - - /// @dev Returns the salt used when deploying an Account. - function _generateSalt(address _admin, bytes memory _data) internal view virtual returns (bytes32) { - return keccak256(abi.encode(_admin, _data)); - } - - function getFunctionSignature(bytes calldata data) internal pure returns (bytes4 functionSelector) { - require(data.length >= 4, "!Data"); - return bytes4(data[:4]); - } - - function decodeExecuteCalldata(bytes calldata data) internal pure returns (address _target, uint256 _value) { - require(data.length >= 4 + 32 + 32, "!Data"); - - // Decode the address, which is bytes 4 to 35 - _target = abi.decode(data[4:36], (address)); - - // Decode the value, which is bytes 36 to 68 - _value = abi.decode(data[36:68], (uint256)); - } - - function decodeExecuteBatchCalldata( - bytes calldata data - ) internal pure returns (address[] memory _targets, uint256[] memory _values, bytes[] memory _callData) { - require(data.length >= 4 + 32 + 32 + 32, "!Data"); - - (_targets, _values, _callData) = abi.decode(data[4:], (address[], uint256[], bytes[])); - } - - /// @notice Validates the signature of a user operation. - function _validateSignature( - PackedUserOperation calldata userOp, - bytes32 userOpHash - ) internal virtual override returns (uint256 validationData) { - bytes32 hash = userOpHash.toEthSignedMessageHash(); - address signer = hash.recover(userOp.signature); - - if (!isValidSigner(signer, userOp)) return SIG_VALIDATION_FAILED; - - SignerPermissionsStatic memory permissions = _accountPermissionsStorage().signerPermissions[signer]; - - uint48 validAfter = uint48(permissions.startTimestamp); - uint48 validUntil = uint48(permissions.endTimestamp); - - return _packValidationData(ValidationData(address(0), validAfter, validUntil)); - } - - /// @notice Makes the given account an admin. - function _setAdmin(address _account, bool _isAdmin) internal virtual override { - super._setAdmin(_account, _isAdmin); - if (factory.code.length > 0) { - if (_isAdmin) { - BaseAccountFactory(factory).onSignerAdded(_account, AccountCoreStorage.data().creationSalt); - } else { - BaseAccountFactory(factory).onSignerRemoved(_account, AccountCoreStorage.data().creationSalt); - } - } - } - - /// @notice Runs after every `changeRole` run. - function _afterSignerPermissionsUpdate(SignerPermissionRequest calldata _req) internal virtual override { - if (factory.code.length > 0) { - BaseAccountFactory(factory).onSignerAdded(_req.signer, AccountCoreStorage.data().creationSalt); - } - } -} diff --git a/contracts/prebuilts/account/utils/AccountCoreStorage.sol b/contracts/prebuilts/account/utils/AccountCoreStorage.sol deleted file mode 100644 index 4356ef94a..000000000 --- a/contracts/prebuilts/account/utils/AccountCoreStorage.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -library AccountCoreStorage { - /// @custom:storage-location erc7201:account.core.storage - /// @dev keccak256(abi.encode(uint256(keccak256("account.core.storage")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 public constant ACCOUNT_CORE_STORAGE_POSITION = - 0x036f52c1827dab135f7fd44ca0bddde297e2f659c710e0ec53e975f22b548300; - - struct Data { - address entrypointOverride; - bytes32 creationSalt; - } - - function data() internal pure returns (Data storage acountCoreData) { - bytes32 position = ACCOUNT_CORE_STORAGE_POSITION; - assembly { - acountCoreData.slot := position - } - } -} diff --git a/contracts/prebuilts/account/utils/AccountExtension.sol b/contracts/prebuilts/account/utils/AccountExtension.sol deleted file mode 100644 index b2ceca2b7..000000000 --- a/contracts/prebuilts/account/utils/AccountExtension.sol +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -/* solhint-disable avoid-low-level-calls */ -/* solhint-disable no-inline-assembly */ -/* solhint-disable reason-string */ - -// Extensions -import "../../../extension/upgradeable/AccountPermissions.sol"; -import "../../../extension/upgradeable/ContractMetadata.sol"; -import "../../../external-deps/openzeppelin/token/ERC721/utils/ERC721Holder.sol"; -import "../../../external-deps/openzeppelin/token/ERC1155/utils/ERC1155Holder.sol"; - -// Utils -import "../../../eip/ERC1271.sol"; -import "../../../external-deps/openzeppelin/utils/cryptography/ECDSA.sol"; -import "../../../external-deps/openzeppelin/utils/structs/EnumerableSet.sol"; -import "./BaseAccountFactory.sol"; -import "./AccountCore.sol"; -import "./AccountCoreStorage.sol"; - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -contract AccountExtension is ContractMetadata, ERC1271, AccountPermissions, ERC721Holder, ERC1155Holder { - using ECDSA for bytes32; - using EnumerableSet for EnumerableSet.AddressSet; - - bytes32 private constant MSG_TYPEHASH = keccak256("AccountMessage(bytes message)"); - - /*/////////////////////////////////////////////////////////////// - Constructor, Initializer, Modifiers - //////////////////////////////////////////////////////////////*/ - - /// @notice Checks whether the caller is the EntryPoint contract or the admin. - modifier onlyAdminOrEntrypoint() virtual { - require( - msg.sender == address(AccountCore(payable(address(this))).entryPoint()) || isAdmin(msg.sender), - "Account: not admin or EntryPoint." - ); - _; - } - - // solhint-disable-next-line no-empty-blocks - receive() external payable virtual {} - - constructor() EIP712("Account", "1") {} - - /*/////////////////////////////////////////////////////////////// - View functions - //////////////////////////////////////////////////////////////*/ - - /// @notice See {IERC165-supportsInterface}. - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155Receiver) returns (bool) { - return - interfaceId == type(IERC1155Receiver).interfaceId || - interfaceId == type(IERC721Receiver).interfaceId || - super.supportsInterface(interfaceId); - } - - /** - * @notice See EIP-1271 - * - * @param _hash The original message hash of the data to sign (before mixing this contract's domain separator) - * @param _signature The signature produced on signing the typed data hash (result of `getMessageHash(abi.encode(rawData))`) - */ - function isValidSignature( - bytes32 _hash, - bytes memory _signature - ) public view virtual override returns (bytes4 magicValue) { - bytes32 targetDigest = getMessageHash(_hash); - address signer = targetDigest.recover(_signature); - - if (isAdmin(signer)) { - return MAGICVALUE; - } - - address caller = msg.sender; - EnumerableSet.AddressSet storage approvedTargets = _accountPermissionsStorage().approvedTargets[signer]; - - require( - approvedTargets.contains(caller) || (approvedTargets.length() == 1 && approvedTargets.at(0) == address(0)), - "Account: caller not approved target." - ); - - if (isActiveSigner(signer)) { - magicValue = MAGICVALUE; - } - } - - /** - * @notice Returns the hash of message that should be signed for EIP1271 verification. - * @param _hash The message hash to sign for the EIP-1271 origin verifying contract. - * @return messageHash The digest to sign for EIP-1271 verification. - */ - function getMessageHash(bytes32 _hash) public view returns (bytes32) { - bytes32 messageHash = keccak256(abi.encode(_hash)); - bytes32 typedDataHash = keccak256(abi.encode(MSG_TYPEHASH, messageHash)); - return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), typedDataHash)); - } - - /*/////////////////////////////////////////////////////////////// - External functions - //////////////////////////////////////////////////////////////*/ - - /// @notice Executes a transaction (called directly from an admin, or by entryPoint) - function execute(address _target, uint256 _value, bytes calldata _calldata) external virtual onlyAdminOrEntrypoint { - _registerOnFactory(); - _call(_target, _value, _calldata); - } - - /// @notice Executes a sequence transaction (called directly from an admin, or by entryPoint) - function executeBatch( - address[] calldata _target, - uint256[] calldata _value, - bytes[] calldata _calldata - ) external virtual onlyAdminOrEntrypoint { - _registerOnFactory(); - require(_target.length == _calldata.length && _target.length == _value.length, "Account: wrong array lengths."); - for (uint256 i = 0; i < _target.length; i++) { - _call(_target[i], _value[i], _calldata[i]); - } - } - - /// @notice Deposit funds for this account in Entrypoint. - function addDeposit() public payable { - AccountCore(payable(address(this))).entryPoint().depositTo{ value: msg.value }(address(this)); - } - - /// @notice Withdraw funds for this account from Entrypoint. - function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public { - _onlyAdmin(); - AccountCore(payable(address(this))).entryPoint().withdrawTo(withdrawAddress, amount); - } - - /*/////////////////////////////////////////////////////////////// - Internal functions - //////////////////////////////////////////////////////////////*/ - - /// @dev Registers the account on the factory if it hasn't been registered yet. - function _registerOnFactory() internal virtual { - address factory = AccountCore(payable(address(this))).factory(); - BaseAccountFactory factoryContract = BaseAccountFactory(factory); - if (!factoryContract.isRegistered(address(this))) { - factoryContract.onRegister(AccountCoreStorage.data().creationSalt); - } - } - - /// @dev Calls a target contract and reverts if it fails. - function _call(address _target, uint256 value, bytes memory _calldata) internal returns (bytes memory result) { - bool success; - (success, result) = _target.call{ value: value }(_calldata); - if (!success) { - assembly { - revert(add(result, 32), mload(result)) - } - } - } - - /// @dev Returns whether contract metadata can be set in the given execution context. - function _canSetContractURI() internal view virtual override returns (bool) { - return isAdmin(msg.sender) || msg.sender == address(this); - } - - function _afterSignerPermissionsUpdate(SignerPermissionRequest calldata _req) internal virtual override {} -} diff --git a/contracts/prebuilts/account/utils/AccountSeaportBulkSigSupport.sol b/contracts/prebuilts/account/utils/AccountSeaportBulkSigSupport.sol deleted file mode 100644 index e5a1dcb7a..000000000 --- a/contracts/prebuilts/account/utils/AccountSeaportBulkSigSupport.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import { SeaportEIP1271 } from "../../../extension/SeaportEIP1271.sol"; -import { AccountPermissions, AccountPermissionsStorage } from "../../../extension/upgradeable/AccountPermissions.sol"; -import { EnumerableSet } from "../../../external-deps/openzeppelin/utils/structs/EnumerableSet.sol"; - -contract AccountSeaportBulkSigSupport is SeaportEIP1271 { - using EnumerableSet for EnumerableSet.AddressSet; - - constructor() SeaportEIP1271("Account", "1") {} - - /// @notice Returns whether a given signer is an authorized signer for the contract. - function _isAuthorizedSigner(address _signer) internal view virtual override returns (bool authorized) { - // is signer an admin? - if (AccountPermissionsStorage.data().isAdmin[_signer]) { - authorized = true; - } - - address caller = msg.sender; - EnumerableSet.AddressSet storage approvedTargets = AccountPermissionsStorage.data().approvedTargets[_signer]; - - require( - approvedTargets.contains(caller) || (approvedTargets.length() == 1 && approvedTargets.at(0) == address(0)), - "Account: caller not approved target." - ); - - // is signer an active signer of account? - AccountPermissions.SignerPermissionsStatic memory permissions = AccountPermissionsStorage - .data() - .signerPermissions[_signer]; - if ( - permissions.startTimestamp <= block.timestamp && - block.timestamp < permissions.endTimestamp && - AccountPermissionsStorage.data().approvedTargets[_signer].length() > 0 - ) { - authorized = true; - } - } -} diff --git a/contracts/prebuilts/account/utils/BaseAccount.sol b/contracts/prebuilts/account/utils/BaseAccount.sol deleted file mode 100644 index d1b7a48dd..000000000 --- a/contracts/prebuilts/account/utils/BaseAccount.sol +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -/* solhint-disable avoid-low-level-calls */ -/* solhint-disable no-empty-blocks */ - -import "../interfaces/IAccount.sol"; -import "../interfaces/IEntryPoint.sol"; -import "./UserOperationLib.sol"; - -/** - * Basic account implementation. - * This contract provides the basic logic for implementing the IAccount interface - validateUserOp - * Specific account implementation should inherit it and provide the account-specific logic. - */ -abstract contract BaseAccount is IAccount { - using UserOperationLib for PackedUserOperation; - - /** - * Return the account nonce. - * This method returns the next sequential nonce. - * For a nonce of a specific key, use `entrypoint.getNonce(account, key)` - */ - function getNonce() public view virtual returns (uint256) { - return entryPoint().getNonce(address(this), 0); - } - - /** - * Return the entryPoint used by this account. - * Subclass should return the current entryPoint used by this account. - */ - function entryPoint() public view virtual returns (IEntryPoint); - - /// @inheritdoc IAccount - function validateUserOp( - PackedUserOperation calldata userOp, - bytes32 userOpHash, - uint256 missingAccountFunds - ) external virtual override returns (uint256 validationData) { - _requireFromEntryPoint(); - validationData = _validateSignature(userOp, userOpHash); - _validateNonce(userOp.nonce); - _payPrefund(missingAccountFunds); - } - - /** - * Ensure the request comes from the known entrypoint. - */ - function _requireFromEntryPoint() internal view virtual { - require(msg.sender == address(entryPoint()), "account: not from EntryPoint"); - } - - /** - * Validate the signature is valid for this message. - * @param userOp - Validate the userOp.signature field. - * @param userOpHash - Convenient field: the hash of the request, to check the signature against. - * (also hashes the entrypoint and chain id) - * @return validationData - Signature and time-range of this operation. - * <20-byte> aggregatorOrSigFail - 0 for valid signature, 1 to mark signature failure, - * otherwise, an address of an aggregator contract. - * <6-byte> validUntil - last timestamp this operation is valid. 0 for "indefinite" - * <6-byte> validAfter - first timestamp this operation is valid - * If the account doesn't use time-range, it is enough to return - * SIG_VALIDATION_FAILED value (1) for signature failure. - * Note that the validation code cannot use block.timestamp (or block.number) directly. - */ - function _validateSignature( - PackedUserOperation calldata userOp, - bytes32 userOpHash - ) internal virtual returns (uint256 validationData); - - /** - * Validate the nonce of the UserOperation. - * This method may validate the nonce requirement of this account. - * e.g. - * To limit the nonce to use sequenced UserOps only (no "out of order" UserOps): - * `require(nonce < type(uint64).max)` - * For a hypothetical account that *requires* the nonce to be out-of-order: - * `require(nonce & type(uint64).max == 0)` - * - * The actual nonce uniqueness is managed by the EntryPoint, and thus no other - * action is needed by the account itself. - * - * @param nonce to validate - * - * solhint-disable-next-line no-empty-blocks - */ - function _validateNonce(uint256 nonce) internal view virtual {} - - /** - * Sends to the entrypoint (msg.sender) the missing funds for this transaction. - * SubClass MAY override this method for better funds management - * (e.g. send to the entryPoint more than the minimum required, so that in future transactions - * it will not be required to send again). - * @param missingAccountFunds - The minimum value this method should send the entrypoint. - * This value MAY be zero, in case there is enough deposit, - * or the userOp has a paymaster. - */ - function _payPrefund(uint256 missingAccountFunds) internal virtual { - if (missingAccountFunds != 0) { - (bool success, ) = payable(msg.sender).call{ value: missingAccountFunds, gas: type(uint256).max }(""); - (success); - //ignore failure (its EntryPoint's job to verify, not account.) - } - } -} diff --git a/contracts/prebuilts/account/utils/BaseAccountFactory.sol b/contracts/prebuilts/account/utils/BaseAccountFactory.sol deleted file mode 100644 index 5e30cb5c1..000000000 --- a/contracts/prebuilts/account/utils/BaseAccountFactory.sol +++ /dev/null @@ -1,168 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.12; - -// Utils -import "../../../extension/Multicall.sol"; -import "../../../external-deps/openzeppelin/proxy/Clones.sol"; -import "../../../external-deps/openzeppelin/utils/structs/EnumerableSet.sol"; -import "./BaseAccount.sol"; -import "../../../extension/interface/IAccountPermissions.sol"; -import "../../../lib/BytesLib.sol"; - -// Interface -import "../interfaces/IAccountFactory.sol"; - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -abstract contract BaseAccountFactory is IAccountFactory, Multicall { - using EnumerableSet for EnumerableSet.AddressSet; - - /*/////////////////////////////////////////////////////////////// - State - //////////////////////////////////////////////////////////////*/ - - address public immutable accountImplementation; - address public immutable entrypoint; - - EnumerableSet.AddressSet private allAccounts; - mapping(address => EnumerableSet.AddressSet) internal accountsOfSigner; - - /*/////////////////////////////////////////////////////////////// - Constructor - //////////////////////////////////////////////////////////////*/ - - constructor(address _accountImpl, address _entrypoint) { - accountImplementation = _accountImpl; - entrypoint = _entrypoint; - } - - /*/////////////////////////////////////////////////////////////// - External functions - //////////////////////////////////////////////////////////////*/ - - /// @notice Deploys a new Account for admin. - function createAccount(address _admin, bytes calldata _data) external virtual override returns (address) { - address impl = accountImplementation; - bytes32 salt = _generateSalt(_admin, _data); - address account = Clones.predictDeterministicAddress(impl, salt); - - if (account.code.length > 0) { - return account; - } - - account = Clones.cloneDeterministic(impl, salt); - - if (msg.sender != entrypoint) { - require(allAccounts.add(account), "AccountFactory: account already registered"); - } - - _initializeAccount(account, _admin, _data); - - emit AccountCreated(account, _admin); - - return account; - } - - /// @notice Callback function for an Account to register itself on the factory. - function onRegister(bytes32 _salt) external { - address account = msg.sender; - require(_isAccountOfFactory(account, _salt), "AccountFactory: not an account."); - - require(allAccounts.add(account), "AccountFactory: account already registered"); - } - - function onSignerAdded(address _signer, bytes32 _salt) external { - address account = msg.sender; - require(_isAccountOfFactory(account, _salt), "AccountFactory: not an account."); - - bool isNewSigner = accountsOfSigner[_signer].add(account); - - if (isNewSigner) { - emit SignerAdded(account, _signer); - } - } - - /// @notice Callback function for an Account to un-register its signers. - function onSignerRemoved(address _signer, bytes32 _salt) external { - address account = msg.sender; - require(_isAccountOfFactory(account, _salt), "AccountFactory: not an account."); - - bool isAccount = accountsOfSigner[_signer].remove(account); - - if (isAccount) { - emit SignerRemoved(account, _signer); - } - } - - /*/////////////////////////////////////////////////////////////// - View functions - //////////////////////////////////////////////////////////////*/ - - /// @notice Returns whether an account is registered on this factory. - function isRegistered(address _account) external view returns (bool) { - return allAccounts.contains(_account); - } - - /// @notice Returns the total number of accounts. - function totalAccounts() external view returns (uint256) { - return allAccounts.length(); - } - - /// @notice Returns all accounts between the given indices. - function getAccounts(uint256 _start, uint256 _end) external view returns (address[] memory accounts) { - require(_start < _end && _end <= allAccounts.length(), "BaseAccountFactory: invalid indices"); - - uint256 len = _end - _start; - accounts = new address[](_end - _start); - - for (uint256 i = 0; i < len; i += 1) { - accounts[i] = allAccounts.at(i + _start); - } - } - - /// @notice Returns all accounts created on the factory. - function getAllAccounts() external view returns (address[] memory) { - return allAccounts.values(); - } - - /// @notice Returns the address of an Account that would be deployed with the given admin signer. - function getAddress(address _adminSigner, bytes calldata _data) public view returns (address) { - bytes32 salt = _generateSalt(_adminSigner, _data); - return Clones.predictDeterministicAddress(accountImplementation, salt); - } - - /// @notice Returns all accounts that the given address is a signer of. - function getAccountsOfSigner(address signer) external view returns (address[] memory accounts) { - return accountsOfSigner[signer].values(); - } - - /*/////////////////////////////////////////////////////////////// - Internal functions - //////////////////////////////////////////////////////////////*/ - - /// @dev Returns whether the caller is an account deployed by this factory. - function _isAccountOfFactory(address _account, bytes32 _salt) internal view virtual returns (bool) { - address predicted = Clones.predictDeterministicAddress(accountImplementation, _salt); - return _account == predicted; - } - - function _getImplementation(address cloneAddress) internal view returns (address) { - bytes memory code = cloneAddress.code; - return BytesLib.toAddress(code, 10); - } - - /// @dev Returns the salt used when deploying an Account. - function _generateSalt(address _admin, bytes memory _data) internal view virtual returns (bytes32) { - return keccak256(abi.encode(_admin, _data)); - } - - /// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`. - function _initializeAccount(address _account, address _admin, bytes calldata _data) internal virtual; -} diff --git a/contracts/prebuilts/account/utils/EntryPoint.sol b/contracts/prebuilts/account/utils/EntryPoint.sol deleted file mode 100644 index 253890734..000000000 --- a/contracts/prebuilts/account/utils/EntryPoint.sol +++ /dev/null @@ -1,725 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; -/* solhint-disable avoid-low-level-calls */ -/* solhint-disable no-inline-assembly */ - -import "../interfaces/IAccount.sol"; -import "../interfaces/IAccountExecute.sol"; -import "../interfaces/IPaymaster.sol"; -import "../interfaces/IEntryPoint.sol"; - -import "../utils/Exec.sol"; -import "./StakeManager.sol"; -import "./SenderCreator.sol"; -import "./Helpers.sol"; -import "./NonceManager.sol"; -import "./UserOperationLib.sol"; - -import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; - -/* - * Account-Abstraction (EIP-4337) singleton EntryPoint implementation. - * Only one instance required on each chain. - */ - -/// @custom:security-contact https://bounty.ethereum.org -contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard, ERC165 { - using UserOperationLib for PackedUserOperation; - - SenderCreator private immutable _senderCreator = new SenderCreator(); - - function senderCreator() internal view virtual returns (SenderCreator) { - return _senderCreator; - } - - //compensate for innerHandleOps' emit message and deposit refund. - // allow some slack for future gas price changes. - uint256 private constant INNER_GAS_OVERHEAD = 10000; - - // Marker for inner call revert on out of gas - bytes32 private constant INNER_OUT_OF_GAS = hex"deaddead"; - bytes32 private constant INNER_REVERT_LOW_PREFUND = hex"deadaa51"; - - uint256 private constant REVERT_REASON_MAX_LEN = 2048; - uint256 private constant PENALTY_PERCENT = 10; - - /// @inheritdoc IERC165 - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - // note: solidity "type(IEntryPoint).interfaceId" is without inherited methods but we want to check everything - return - interfaceId == - (type(IEntryPoint).interfaceId ^ type(IStakeManager).interfaceId ^ type(INonceManager).interfaceId) || - interfaceId == type(IEntryPoint).interfaceId || - interfaceId == type(IStakeManager).interfaceId || - interfaceId == type(INonceManager).interfaceId || - super.supportsInterface(interfaceId); - } - - /** - * Compensate the caller's beneficiary address with the collected fees of all UserOperations. - * @param beneficiary - The address to receive the fees. - * @param amount - Amount to transfer. - */ - function _compensate(address payable beneficiary, uint256 amount) internal { - require(beneficiary != address(0), "AA90 invalid beneficiary"); - (bool success, ) = beneficiary.call{ value: amount }(""); - require(success, "AA91 failed send to beneficiary"); - } - - /** - * Execute a user operation. - * @param opIndex - Index into the opInfo array. - * @param userOp - The userOp to execute. - * @param opInfo - The opInfo filled by validatePrepayment for this userOp. - * @return collected - The total amount this userOp paid. - */ - function _executeUserOp( - uint256 opIndex, - PackedUserOperation calldata userOp, - UserOpInfo memory opInfo - ) internal returns (uint256 collected) { - uint256 preGas = gasleft(); - bytes memory context = getMemoryBytesFromOffset(opInfo.contextOffset); - bool success; - { - uint256 saveFreePtr; - assembly ("memory-safe") { - saveFreePtr := mload(0x40) - } - bytes calldata callData = userOp.callData; - bytes memory innerCall; - bytes4 methodSig; - assembly { - let len := callData.length - if gt(len, 3) { - methodSig := calldataload(callData.offset) - } - } - if (methodSig == IAccountExecute.executeUserOp.selector) { - bytes memory executeUserOp = abi.encodeCall(IAccountExecute.executeUserOp, (userOp, opInfo.userOpHash)); - innerCall = abi.encodeCall(this.innerHandleOp, (executeUserOp, opInfo, context)); - } else { - innerCall = abi.encodeCall(this.innerHandleOp, (callData, opInfo, context)); - } - assembly ("memory-safe") { - success := call(gas(), address(), 0, add(innerCall, 0x20), mload(innerCall), 0, 32) - collected := mload(0) - mstore(0x40, saveFreePtr) - } - } - if (!success) { - bytes32 innerRevertCode; - assembly ("memory-safe") { - let len := returndatasize() - if eq(32, len) { - returndatacopy(0, 0, 32) - innerRevertCode := mload(0) - } - } - if (innerRevertCode == INNER_OUT_OF_GAS) { - // handleOps was called with gas limit too low. abort entire bundle. - //can only be caused by bundler (leaving not enough gas for inner call) - revert FailedOp(opIndex, "AA95 out of gas"); - } else if (innerRevertCode == INNER_REVERT_LOW_PREFUND) { - // innerCall reverted on prefund too low. treat entire prefund as "gas cost" - uint256 actualGas = preGas - gasleft() + opInfo.preOpGas; - uint256 actualGasCost = opInfo.prefund; - emitPrefundTooLow(opInfo); - emitUserOperationEvent(opInfo, false, actualGasCost, actualGas); - collected = actualGasCost; - } else { - emit PostOpRevertReason( - opInfo.userOpHash, - opInfo.mUserOp.sender, - opInfo.mUserOp.nonce, - Exec.getReturnData(REVERT_REASON_MAX_LEN) - ); - - uint256 actualGas = preGas - gasleft() + opInfo.preOpGas; - collected = _postExecution(IPaymaster.PostOpMode.postOpReverted, opInfo, context, actualGas); - } - } - } - - function emitUserOperationEvent( - UserOpInfo memory opInfo, - bool success, - uint256 actualGasCost, - uint256 actualGas - ) internal virtual { - emit UserOperationEvent( - opInfo.userOpHash, - opInfo.mUserOp.sender, - opInfo.mUserOp.paymaster, - opInfo.mUserOp.nonce, - success, - actualGasCost, - actualGas - ); - } - - function emitPrefundTooLow(UserOpInfo memory opInfo) internal virtual { - emit UserOperationPrefundTooLow(opInfo.userOpHash, opInfo.mUserOp.sender, opInfo.mUserOp.nonce); - } - - /// @inheritdoc IEntryPoint - function handleOps(PackedUserOperation[] calldata ops, address payable beneficiary) public nonReentrant { - uint256 opslen = ops.length; - UserOpInfo[] memory opInfos = new UserOpInfo[](opslen); - - unchecked { - for (uint256 i = 0; i < opslen; i++) { - UserOpInfo memory opInfo = opInfos[i]; - (uint256 validationData, uint256 pmValidationData) = _validatePrepayment(i, ops[i], opInfo); - _validateAccountAndPaymasterValidationData(i, validationData, pmValidationData, address(0)); - } - - uint256 collected = 0; - emit BeforeExecution(); - - for (uint256 i = 0; i < opslen; i++) { - collected += _executeUserOp(i, ops[i], opInfos[i]); - } - - _compensate(beneficiary, collected); - } - } - - /// @inheritdoc IEntryPoint - function handleAggregatedOps( - UserOpsPerAggregator[] calldata opsPerAggregator, - address payable beneficiary - ) public nonReentrant { - uint256 opasLen = opsPerAggregator.length; - uint256 totalOps = 0; - for (uint256 i = 0; i < opasLen; i++) { - UserOpsPerAggregator calldata opa = opsPerAggregator[i]; - PackedUserOperation[] calldata ops = opa.userOps; - IAggregator aggregator = opa.aggregator; - - //address(1) is special marker of "signature error" - require(address(aggregator) != address(1), "AA96 invalid aggregator"); - - if (address(aggregator) != address(0)) { - // solhint-disable-next-line no-empty-blocks - try aggregator.validateSignatures(ops, opa.signature) {} catch { - revert SignatureValidationFailed(address(aggregator)); - } - } - - totalOps += ops.length; - } - - UserOpInfo[] memory opInfos = new UserOpInfo[](totalOps); - - uint256 opIndex = 0; - for (uint256 a = 0; a < opasLen; a++) { - UserOpsPerAggregator calldata opa = opsPerAggregator[a]; - PackedUserOperation[] calldata ops = opa.userOps; - IAggregator aggregator = opa.aggregator; - - uint256 opslen = ops.length; - for (uint256 i = 0; i < opslen; i++) { - UserOpInfo memory opInfo = opInfos[opIndex]; - (uint256 validationData, uint256 paymasterValidationData) = _validatePrepayment( - opIndex, - ops[i], - opInfo - ); - _validateAccountAndPaymasterValidationData( - i, - validationData, - paymasterValidationData, - address(aggregator) - ); - opIndex++; - } - } - - emit BeforeExecution(); - - uint256 collected = 0; - opIndex = 0; - for (uint256 a = 0; a < opasLen; a++) { - UserOpsPerAggregator calldata opa = opsPerAggregator[a]; - emit SignatureAggregatorChanged(address(opa.aggregator)); - PackedUserOperation[] calldata ops = opa.userOps; - uint256 opslen = ops.length; - - for (uint256 i = 0; i < opslen; i++) { - collected += _executeUserOp(opIndex, ops[i], opInfos[opIndex]); - opIndex++; - } - } - emit SignatureAggregatorChanged(address(0)); - - _compensate(beneficiary, collected); - } - - /** - * A memory copy of UserOp static fields only. - * Excluding: callData, initCode and signature. Replacing paymasterAndData with paymaster. - */ - struct MemoryUserOp { - address sender; - uint256 nonce; - uint256 verificationGasLimit; - uint256 callGasLimit; - uint256 paymasterVerificationGasLimit; - uint256 paymasterPostOpGasLimit; - uint256 preVerificationGas; - address paymaster; - uint256 maxFeePerGas; - uint256 maxPriorityFeePerGas; - } - - struct UserOpInfo { - MemoryUserOp mUserOp; - bytes32 userOpHash; - uint256 prefund; - uint256 contextOffset; - uint256 preOpGas; - } - - /** - * Inner function to handle a UserOperation. - * Must be declared "external" to open a call context, but it can only be called by handleOps. - * @param callData - The callData to execute. - * @param opInfo - The UserOpInfo struct. - * @param context - The context bytes. - * @return actualGasCost - the actual cost in eth this UserOperation paid for gas - */ - function innerHandleOp( - bytes memory callData, - UserOpInfo memory opInfo, - bytes calldata context - ) external returns (uint256 actualGasCost) { - uint256 preGas = gasleft(); - require(msg.sender == address(this), "AA92 internal call only"); - MemoryUserOp memory mUserOp = opInfo.mUserOp; - - uint256 callGasLimit = mUserOp.callGasLimit; - unchecked { - // handleOps was called with gas limit too low. abort entire bundle. - if ((gasleft() * 63) / 64 < callGasLimit + mUserOp.paymasterPostOpGasLimit + INNER_GAS_OVERHEAD) { - assembly ("memory-safe") { - mstore(0, INNER_OUT_OF_GAS) - revert(0, 32) - } - } - } - - IPaymaster.PostOpMode mode = IPaymaster.PostOpMode.opSucceeded; - if (callData.length > 0) { - bool success = Exec.call(mUserOp.sender, 0, callData, callGasLimit); - if (!success) { - bytes memory result = Exec.getReturnData(REVERT_REASON_MAX_LEN); - if (result.length > 0) { - emit UserOperationRevertReason(opInfo.userOpHash, mUserOp.sender, mUserOp.nonce, result); - } - mode = IPaymaster.PostOpMode.opReverted; - } - } - - unchecked { - uint256 actualGas = preGas - gasleft() + opInfo.preOpGas; - return _postExecution(mode, opInfo, context, actualGas); - } - } - - /// @inheritdoc IEntryPoint - function getUserOpHash(PackedUserOperation calldata userOp) public view returns (bytes32) { - return keccak256(abi.encode(userOp.hash(), address(this), block.chainid)); - } - - /** - * Copy general fields from userOp into the memory opInfo structure. - * @param userOp - The user operation. - * @param mUserOp - The memory user operation. - */ - function _copyUserOpToMemory(PackedUserOperation calldata userOp, MemoryUserOp memory mUserOp) internal pure { - mUserOp.sender = userOp.sender; - mUserOp.nonce = userOp.nonce; - (mUserOp.verificationGasLimit, mUserOp.callGasLimit) = UserOperationLib.unpackUints(userOp.accountGasLimits); - mUserOp.preVerificationGas = userOp.preVerificationGas; - (mUserOp.maxPriorityFeePerGas, mUserOp.maxFeePerGas) = UserOperationLib.unpackUints(userOp.gasFees); - bytes calldata paymasterAndData = userOp.paymasterAndData; - if (paymasterAndData.length > 0) { - require(paymasterAndData.length >= UserOperationLib.PAYMASTER_DATA_OFFSET, "AA93 invalid paymasterAndData"); - ( - mUserOp.paymaster, - mUserOp.paymasterVerificationGasLimit, - mUserOp.paymasterPostOpGasLimit - ) = UserOperationLib.unpackPaymasterStaticFields(paymasterAndData); - } else { - mUserOp.paymaster = address(0); - mUserOp.paymasterVerificationGasLimit = 0; - mUserOp.paymasterPostOpGasLimit = 0; - } - } - - /** - * Get the required prefunded gas fee amount for an operation. - * @param mUserOp - The user operation in memory. - */ - function _getRequiredPrefund(MemoryUserOp memory mUserOp) internal pure returns (uint256 requiredPrefund) { - unchecked { - uint256 requiredGas = mUserOp.verificationGasLimit + - mUserOp.callGasLimit + - mUserOp.paymasterVerificationGasLimit + - mUserOp.paymasterPostOpGasLimit + - mUserOp.preVerificationGas; - - requiredPrefund = requiredGas * mUserOp.maxFeePerGas; - } - } - - /** - * Create sender smart contract account if init code is provided. - * @param opIndex - The operation index. - * @param opInfo - The operation info. - * @param initCode - The init code for the smart contract account. - */ - function _createSenderIfNeeded(uint256 opIndex, UserOpInfo memory opInfo, bytes calldata initCode) internal { - if (initCode.length != 0) { - address sender = opInfo.mUserOp.sender; - if (sender.code.length != 0) revert FailedOp(opIndex, "AA10 sender already constructed"); - address sender1 = senderCreator().createSender{ gas: opInfo.mUserOp.verificationGasLimit }(initCode); - if (sender1 == address(0)) revert FailedOp(opIndex, "AA13 initCode failed or OOG"); - if (sender1 != sender) revert FailedOp(opIndex, "AA14 initCode must return sender"); - if (sender1.code.length == 0) revert FailedOp(opIndex, "AA15 initCode must create sender"); - address factory = address(bytes20(initCode[0:20])); - emit AccountDeployed(opInfo.userOpHash, sender, factory, opInfo.mUserOp.paymaster); - } - } - - /// @inheritdoc IEntryPoint - function getSenderAddress(bytes calldata initCode) public { - address sender = senderCreator().createSender(initCode); - revert SenderAddressResult(sender); - } - - /** - * Call account.validateUserOp. - * Revert (with FailedOp) in case validateUserOp reverts, or account didn't send required prefund. - * Decrement account's deposit if needed. - * @param opIndex - The operation index. - * @param op - The user operation. - * @param opInfo - The operation info. - * @param requiredPrefund - The required prefund amount. - */ - function _validateAccountPrepayment( - uint256 opIndex, - PackedUserOperation calldata op, - UserOpInfo memory opInfo, - uint256 requiredPrefund, - uint256 verificationGasLimit - ) internal returns (uint256 validationData) { - unchecked { - MemoryUserOp memory mUserOp = opInfo.mUserOp; - address sender = mUserOp.sender; - _createSenderIfNeeded(opIndex, opInfo, op.initCode); - address paymaster = mUserOp.paymaster; - uint256 missingAccountFunds = 0; - if (paymaster == address(0)) { - uint256 bal = balanceOf(sender); - missingAccountFunds = bal > requiredPrefund ? 0 : requiredPrefund - bal; - } - try - IAccount(sender).validateUserOp{ gas: verificationGasLimit }(op, opInfo.userOpHash, missingAccountFunds) - returns (uint256 _validationData) { - validationData = _validationData; - } catch { - revert FailedOpWithRevert(opIndex, "AA23 reverted", Exec.getReturnData(REVERT_REASON_MAX_LEN)); - } - if (paymaster == address(0)) { - DepositInfo storage senderInfo = deposits[sender]; - uint256 deposit = senderInfo.deposit; - if (requiredPrefund > deposit) { - revert FailedOp(opIndex, "AA21 didn't pay prefund"); - } - senderInfo.deposit = deposit - requiredPrefund; - } - } - } - - /** - * In case the request has a paymaster: - * - Validate paymaster has enough deposit. - * - Call paymaster.validatePaymasterUserOp. - * - Revert with proper FailedOp in case paymaster reverts. - * - Decrement paymaster's deposit. - * @param opIndex - The operation index. - * @param op - The user operation. - * @param opInfo - The operation info. - * @param requiredPreFund - The required prefund amount. - */ - function _validatePaymasterPrepayment( - uint256 opIndex, - PackedUserOperation calldata op, - UserOpInfo memory opInfo, - uint256 requiredPreFund - ) internal returns (bytes memory context, uint256 validationData) { - unchecked { - uint256 preGas = gasleft(); - MemoryUserOp memory mUserOp = opInfo.mUserOp; - address paymaster = mUserOp.paymaster; - DepositInfo storage paymasterInfo = deposits[paymaster]; - uint256 deposit = paymasterInfo.deposit; - if (deposit < requiredPreFund) { - revert FailedOp(opIndex, "AA31 paymaster deposit too low"); - } - paymasterInfo.deposit = deposit - requiredPreFund; - uint256 pmVerificationGasLimit = mUserOp.paymasterVerificationGasLimit; - try - IPaymaster(paymaster).validatePaymasterUserOp{ gas: pmVerificationGasLimit }( - op, - opInfo.userOpHash, - requiredPreFund - ) - returns (bytes memory _context, uint256 _validationData) { - context = _context; - validationData = _validationData; - } catch { - revert FailedOpWithRevert(opIndex, "AA33 reverted", Exec.getReturnData(REVERT_REASON_MAX_LEN)); - } - if (preGas - gasleft() > pmVerificationGasLimit) { - revert FailedOp(opIndex, "AA36 over paymasterVerificationGasLimit"); - } - } - } - - /** - * Revert if either account validationData or paymaster validationData is expired. - * @param opIndex - The operation index. - * @param validationData - The account validationData. - * @param paymasterValidationData - The paymaster validationData. - * @param expectedAggregator - The expected aggregator. - */ - function _validateAccountAndPaymasterValidationData( - uint256 opIndex, - uint256 validationData, - uint256 paymasterValidationData, - address expectedAggregator - ) internal view { - (address aggregator, bool outOfTimeRange) = _getValidationData(validationData); - if (expectedAggregator != aggregator) { - revert FailedOp(opIndex, "AA24 signature error"); - } - if (outOfTimeRange) { - revert FailedOp(opIndex, "AA22 expired or not due"); - } - // pmAggregator is not a real signature aggregator: we don't have logic to handle it as address. - // Non-zero address means that the paymaster fails due to some signature check (which is ok only during estimation). - address pmAggregator; - (pmAggregator, outOfTimeRange) = _getValidationData(paymasterValidationData); - if (pmAggregator != address(0)) { - revert FailedOp(opIndex, "AA34 signature error"); - } - if (outOfTimeRange) { - revert FailedOp(opIndex, "AA32 paymaster expired or not due"); - } - } - - /** - * Parse validationData into its components. - * @param validationData - The packed validation data (sigFailed, validAfter, validUntil). - * @return aggregator the aggregator of the validationData - * @return outOfTimeRange true if current time is outside the time range of this validationData. - */ - function _getValidationData( - uint256 validationData - ) internal view returns (address aggregator, bool outOfTimeRange) { - if (validationData == 0) { - return (address(0), false); - } - ValidationData memory data = _parseValidationData(validationData); - // solhint-disable-next-line not-rely-on-time - outOfTimeRange = block.timestamp > data.validUntil || block.timestamp < data.validAfter; - aggregator = data.aggregator; - } - - /** - * Validate account and paymaster (if defined) and - * also make sure total validation doesn't exceed verificationGasLimit. - * This method is called off-chain (simulateValidation()) and on-chain (from handleOps) - * @param opIndex - The index of this userOp into the "opInfos" array. - * @param userOp - The userOp to validate. - */ - function _validatePrepayment( - uint256 opIndex, - PackedUserOperation calldata userOp, - UserOpInfo memory outOpInfo - ) internal returns (uint256 validationData, uint256 paymasterValidationData) { - uint256 preGas = gasleft(); - MemoryUserOp memory mUserOp = outOpInfo.mUserOp; - _copyUserOpToMemory(userOp, mUserOp); - outOpInfo.userOpHash = getUserOpHash(userOp); - - // Validate all numeric values in userOp are well below 128 bit, so they can safely be added - // and multiplied without causing overflow. - uint256 verificationGasLimit = mUserOp.verificationGasLimit; - uint256 maxGasValues = mUserOp.preVerificationGas | - verificationGasLimit | - mUserOp.callGasLimit | - mUserOp.paymasterVerificationGasLimit | - mUserOp.paymasterPostOpGasLimit | - mUserOp.maxFeePerGas | - mUserOp.maxPriorityFeePerGas; - require(maxGasValues <= type(uint120).max, "AA94 gas values overflow"); - - uint256 requiredPreFund = _getRequiredPrefund(mUserOp); - validationData = _validateAccountPrepayment(opIndex, userOp, outOpInfo, requiredPreFund, verificationGasLimit); - - if (!_validateAndUpdateNonce(mUserOp.sender, mUserOp.nonce)) { - revert FailedOp(opIndex, "AA25 invalid account nonce"); - } - - unchecked { - if (preGas - gasleft() > verificationGasLimit) { - revert FailedOp(opIndex, "AA26 over verificationGasLimit"); - } - } - - bytes memory context; - if (mUserOp.paymaster != address(0)) { - (context, paymasterValidationData) = _validatePaymasterPrepayment( - opIndex, - userOp, - outOpInfo, - requiredPreFund - ); - } - unchecked { - outOpInfo.prefund = requiredPreFund; - outOpInfo.contextOffset = getOffsetOfMemoryBytes(context); - outOpInfo.preOpGas = preGas - gasleft() + userOp.preVerificationGas; - } - } - - /** - * Process post-operation, called just after the callData is executed. - * If a paymaster is defined and its validation returned a non-empty context, its postOp is called. - * The excess amount is refunded to the account (or paymaster - if it was used in the request). - * @param mode - Whether is called from innerHandleOp, or outside (postOpReverted). - * @param opInfo - UserOp fields and info collected during validation. - * @param context - The context returned in validatePaymasterUserOp. - * @param actualGas - The gas used so far by this user operation. - */ - function _postExecution( - IPaymaster.PostOpMode mode, - UserOpInfo memory opInfo, - bytes memory context, - uint256 actualGas - ) private returns (uint256 actualGasCost) { - uint256 preGas = gasleft(); - unchecked { - address refundAddress; - MemoryUserOp memory mUserOp = opInfo.mUserOp; - uint256 gasPrice = getUserOpGasPrice(mUserOp); - - address paymaster = mUserOp.paymaster; - if (paymaster == address(0)) { - refundAddress = mUserOp.sender; - } else { - refundAddress = paymaster; - if (context.length > 0) { - actualGasCost = actualGas * gasPrice; - if (mode != IPaymaster.PostOpMode.postOpReverted) { - try - IPaymaster(paymaster).postOp{ gas: mUserOp.paymasterPostOpGasLimit }( - mode, - context, - actualGasCost, - gasPrice - ) - // solhint-disable-next-line no-empty-blocks - { - - } catch { - bytes memory reason = Exec.getReturnData(REVERT_REASON_MAX_LEN); - revert PostOpReverted(reason); - } - } - } - } - actualGas += preGas - gasleft(); - - // Calculating a penalty for unused execution gas - { - uint256 executionGasLimit = mUserOp.callGasLimit + mUserOp.paymasterPostOpGasLimit; - uint256 executionGasUsed = actualGas - opInfo.preOpGas; - // this check is required for the gas used within EntryPoint and not covered by explicit gas limits - if (executionGasLimit > executionGasUsed) { - uint256 unusedGas = executionGasLimit - executionGasUsed; - uint256 unusedGasPenalty = (unusedGas * PENALTY_PERCENT) / 100; - actualGas += unusedGasPenalty; - } - } - - actualGasCost = actualGas * gasPrice; - uint256 prefund = opInfo.prefund; - if (prefund < actualGasCost) { - if (mode == IPaymaster.PostOpMode.postOpReverted) { - actualGasCost = prefund; - emitPrefundTooLow(opInfo); - emitUserOperationEvent(opInfo, false, actualGasCost, actualGas); - } else { - assembly ("memory-safe") { - mstore(0, INNER_REVERT_LOW_PREFUND) - revert(0, 32) - } - } - } else { - uint256 refund = prefund - actualGasCost; - _incrementDeposit(refundAddress, refund); - bool success = mode == IPaymaster.PostOpMode.opSucceeded; - emitUserOperationEvent(opInfo, success, actualGasCost, actualGas); - } - } // unchecked - } - - /** - * The gas price this UserOp agrees to pay. - * Relayer/block builder might submit the TX with higher priorityFee, but the user should not. - * @param mUserOp - The userOp to get the gas price from. - */ - function getUserOpGasPrice(MemoryUserOp memory mUserOp) internal view returns (uint256) { - unchecked { - uint256 maxFeePerGas = mUserOp.maxFeePerGas; - uint256 maxPriorityFeePerGas = mUserOp.maxPriorityFeePerGas; - if (maxFeePerGas == maxPriorityFeePerGas) { - //legacy mode (for networks that don't support basefee opcode) - return maxFeePerGas; - } - return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee); - } - } - - /** - * The offset of the given bytes in memory. - * @param data - The bytes to get the offset of. - */ - function getOffsetOfMemoryBytes(bytes memory data) internal pure returns (uint256 offset) { - assembly { - offset := data - } - } - - /** - * The bytes in memory at the given offset. - * @param offset - The offset to get the bytes from. - */ - function getMemoryBytesFromOffset(uint256 offset) internal pure returns (bytes memory data) { - assembly ("memory-safe") { - data := offset - } - } - - /// @inheritdoc IEntryPoint - function delegateAndRevert(address target, bytes calldata data) external { - (bool success, bytes memory ret) = target.delegatecall(data); - revert DelegateAndRevert(success, ret); - } -} diff --git a/contracts/prebuilts/account/utils/Exec.sol b/contracts/prebuilts/account/utils/Exec.sol deleted file mode 100644 index a245deddd..000000000 --- a/contracts/prebuilts/account/utils/Exec.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -pragma solidity ^0.8.23; - -// solhint-disable no-inline-assembly - -/** - * Utility functions helpful when making different kinds of contract calls in Solidity. - */ -library Exec { - function call(address to, uint256 value, bytes memory data, uint256 txGas) internal returns (bool success) { - assembly ("memory-safe") { - success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0) - } - } - - function staticcall(address to, bytes memory data, uint256 txGas) internal view returns (bool success) { - assembly ("memory-safe") { - success := staticcall(txGas, to, add(data, 0x20), mload(data), 0, 0) - } - } - - function delegateCall(address to, bytes memory data, uint256 txGas) internal returns (bool success) { - assembly ("memory-safe") { - success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0) - } - } - - // get returned data from last call or calldelegate - function getReturnData(uint256 maxLen) internal pure returns (bytes memory returnData) { - assembly ("memory-safe") { - let len := returndatasize() - if gt(len, maxLen) { - len := maxLen - } - let ptr := mload(0x40) - mstore(0x40, add(ptr, add(len, 0x20))) - mstore(ptr, len) - returndatacopy(add(ptr, 0x20), 0, len) - returnData := ptr - } - } - - // revert with explicit byte array (probably reverted info from call) - function revertWithData(bytes memory returnData) internal pure { - assembly ("memory-safe") { - revert(add(returnData, 32), mload(returnData)) - } - } - - function callAndRevert(address to, bytes memory data, uint256 maxLen) internal { - bool success = call(to, 0, data, gasleft()); - if (!success) { - revertWithData(getReturnData(maxLen)); - } - } -} diff --git a/contracts/prebuilts/account/utils/Helpers.sol b/contracts/prebuilts/account/utils/Helpers.sol deleted file mode 100644 index cab842e29..000000000 --- a/contracts/prebuilts/account/utils/Helpers.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -/* solhint-disable no-inline-assembly */ - -/* - * For simulation purposes, validateUserOp (and validatePaymasterUserOp) - * must return this value in case of signature failure, instead of revert. - */ -uint256 constant SIG_VALIDATION_FAILED = 1; - -/* - * For simulation purposes, validateUserOp (and validatePaymasterUserOp) - * return this value on success. - */ -uint256 constant SIG_VALIDATION_SUCCESS = 0; - -/** - * Returned data from validateUserOp. - * validateUserOp returns a uint256, which is created by `_packedValidationData` and - * parsed by `_parseValidationData`. - * @param aggregator - address(0) - The account validated the signature by itself. - * address(1) - The account failed to validate the signature. - * otherwise - This is an address of a signature aggregator that must - * be used to validate the signature. - * @param validAfter - This UserOp is valid only after this timestamp. - * @param validaUntil - This UserOp is valid only up to this timestamp. - */ -struct ValidationData { - address aggregator; - uint48 validAfter; - uint48 validUntil; -} - -/** - * Extract sigFailed, validAfter, validUntil. - * Also convert zero validUntil to type(uint48).max. - * @param validationData - The packed validation data. - */ -function _parseValidationData(uint256 validationData) pure returns (ValidationData memory data) { - address aggregator = address(uint160(validationData)); - uint48 validUntil = uint48(validationData >> 160); - if (validUntil == 0) { - validUntil = type(uint48).max; - } - uint48 validAfter = uint48(validationData >> (48 + 160)); - return ValidationData(aggregator, validAfter, validUntil); -} - -/** - * Helper to pack the return value for validateUserOp. - * @param data - The ValidationData to pack. - */ -function _packValidationData(ValidationData memory data) pure returns (uint256) { - return uint160(data.aggregator) | (uint256(data.validUntil) << 160) | (uint256(data.validAfter) << (160 + 48)); -} - -/** - * Helper to pack the return value for validateUserOp, when not using an aggregator. - * @param sigFailed - True for signature failure, false for success. - * @param validUntil - Last timestamp this UserOperation is valid (or zero for infinite). - * @param validAfter - First timestamp this UserOperation is valid. - */ -function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter) pure returns (uint256) { - return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48)); -} - -/** - * keccak function over calldata. - * @dev copy calldata into memory, do keccak and drop allocated memory. Strangely, this is more efficient than letting solidity do it. - */ -function calldataKeccak(bytes calldata data) pure returns (bytes32 ret) { - assembly ("memory-safe") { - let mem := mload(0x40) - let len := data.length - calldatacopy(mem, data.offset, len) - ret := keccak256(mem, len) - } -} - -/** - * The minimum of two numbers. - * @param a - First number. - * @param b - Second number. - */ -function min(uint256 a, uint256 b) pure returns (uint256) { - return a < b ? a : b; -} diff --git a/contracts/prebuilts/account/utils/NonceManager.sol b/contracts/prebuilts/account/utils/NonceManager.sol deleted file mode 100644 index ec16026ea..000000000 --- a/contracts/prebuilts/account/utils/NonceManager.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -import "../interfaces/INonceManager.sol"; - -/** - * nonce management functionality - */ -abstract contract NonceManager is INonceManager { - /** - * The next valid sequence number for a given nonce key. - */ - mapping(address => mapping(uint192 => uint256)) public nonceSequenceNumber; - - /// @inheritdoc INonceManager - function getNonce(address sender, uint192 key) public view override returns (uint256 nonce) { - return nonceSequenceNumber[sender][key] | (uint256(key) << 64); - } - - // allow an account to manually increment its own nonce. - // (mainly so that during construction nonce can be made non-zero, - // to "absorb" the gas cost of first nonce increment to 1st transaction (construction), - // not to 2nd transaction) - function incrementNonce(uint192 key) public override { - nonceSequenceNumber[msg.sender][key]++; - } - - /** - * validate nonce uniqueness for this account. - * called just after validateUserOp() - * @return true if the nonce was incremented successfully. - * false if the current nonce doesn't match the given one. - */ - function _validateAndUpdateNonce(address sender, uint256 nonce) internal returns (bool) { - uint192 key = uint192(nonce >> 64); - uint64 seq = uint64(nonce); - return nonceSequenceNumber[sender][key]++ == seq; - } -} diff --git a/contracts/prebuilts/account/utils/OracleHelper.sol b/contracts/prebuilts/account/utils/OracleHelper.sol deleted file mode 100644 index 89ce08843..000000000 --- a/contracts/prebuilts/account/utils/OracleHelper.sol +++ /dev/null @@ -1,154 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -/* solhint-disable not-rely-on-time */ - -import "../interfaces/IOracle.sol"; - -/// @title Helper functions for dealing with various forms of price feed oracles. -/// @notice Maintains a price cache and updates the current price if needed. -/// In the best case scenario we have a direct oracle from the token to the native asset. -/// Also support tokens that have no direct price oracle to the native asset. -/// Sometimes oracles provide the price in the opposite direction of what we need in the moment. -abstract contract OracleHelper { - event TokenPriceUpdated(uint256 currentPrice, uint256 previousPrice, uint256 cachedPriceTimestamp); - - uint256 private constant PRICE_DENOMINATOR = 1e26; - - struct OracleHelperConfig { - /// @notice The price cache will be returned without even fetching the oracles for this number of seconds - uint48 cacheTimeToLive; - /// @notice The maximum acceptable age of the price oracle round - uint48 maxOracleRoundAge; - /// @notice The Oracle contract used to fetch the latest token prices - IOracle tokenOracle; - /// @notice The Oracle contract used to fetch the latest native asset prices. Only needed if tokenToNativeOracle flag is not set. - IOracle nativeOracle; - /// @notice If 'true' we will fetch price directly from tokenOracle - /// @notice If 'false' we will use nativeOracle to establish a token price through a shared third currency - bool tokenToNativeOracle; - /// @notice 'false' if price is bridging-asset-per-token (or native-asset-per-token), 'true' if price is tokens-per-bridging-asset - bool tokenOracleReverse; - /// @notice 'false' if price is bridging-asset-per-native-asset, 'true' if price is native-asset-per-bridging-asset - bool nativeOracleReverse; - /// @notice The price update threshold percentage from PRICE_DENOMINATOR that triggers a price update (1e26 = 100%) - uint256 priceUpdateThreshold; - } - - /// @notice The cached token price from the Oracle, always in (native-asset-per-token) * PRICE_DENOMINATOR format - uint256 public cachedPrice; - - /// @notice The timestamp of a block when the cached price was updated - uint48 public cachedPriceTimestamp; - - OracleHelperConfig public oracleHelperConfig; - - /// @notice The "10^(tokenOracle.decimals)" value used for the price calculation - uint128 private tokenOracleDecimalPower; - - /// @notice The "10^(nativeOracle.decimals)" value used for the price calculation - uint128 private nativeOracleDecimalPower; - - constructor(OracleHelperConfig memory _oracleHelperConfig) { - cachedPrice = type(uint256).max; // initialize the storage slot to invalid value - _setOracleConfiguration(_oracleHelperConfig); - } - - function _setOracleConfiguration(OracleHelperConfig memory _oracleHelperConfig) internal { - oracleHelperConfig = _oracleHelperConfig; - require(_oracleHelperConfig.priceUpdateThreshold <= PRICE_DENOMINATOR, "TPM: update threshold too high"); - tokenOracleDecimalPower = uint128(10 ** oracleHelperConfig.tokenOracle.decimals()); - if (oracleHelperConfig.tokenToNativeOracle) { - require(address(oracleHelperConfig.nativeOracle) == address(0), "TPM: native oracle must be zero"); - nativeOracleDecimalPower = 1; - } else { - nativeOracleDecimalPower = uint128(10 ** oracleHelperConfig.nativeOracle.decimals()); - } - } - - /// @notice Updates the token price by fetching the latest price from the Oracle. - /// @param force true to force cache update, even if called after short time or the change is lower than the update threshold. - /// @return newPrice the new cached token price - function updateCachedPrice(bool force) public returns (uint256) { - uint256 cacheTimeToLive = oracleHelperConfig.cacheTimeToLive; - uint256 cacheAge = block.timestamp - cachedPriceTimestamp; - if (!force && cacheAge <= cacheTimeToLive) { - return cachedPrice; - } - uint256 priceUpdateThreshold = oracleHelperConfig.priceUpdateThreshold; - IOracle tokenOracle = oracleHelperConfig.tokenOracle; - IOracle nativeOracle = oracleHelperConfig.nativeOracle; - - uint256 _cachedPrice = cachedPrice; - uint256 tokenPrice = fetchPrice(tokenOracle); - uint256 nativeAssetPrice = 1; - // If the 'TokenOracle' returns the price in the native asset units there is no need to fetch native asset price - if (!oracleHelperConfig.tokenToNativeOracle) { - nativeAssetPrice = fetchPrice(nativeOracle); - } - uint256 newPrice = calculatePrice( - tokenPrice, - nativeAssetPrice, - oracleHelperConfig.tokenOracleReverse, - oracleHelperConfig.nativeOracleReverse - ); - uint256 priceRatio = (PRICE_DENOMINATOR * newPrice) / _cachedPrice; - bool updateRequired = force || - priceRatio > PRICE_DENOMINATOR + priceUpdateThreshold || - priceRatio < PRICE_DENOMINATOR - priceUpdateThreshold; - if (!updateRequired) { - return _cachedPrice; - } - cachedPrice = newPrice; - cachedPriceTimestamp = uint48(block.timestamp); - emit TokenPriceUpdated(newPrice, _cachedPrice, cachedPriceTimestamp); - return newPrice; - } - - /** - * Calculate the effective price of the selected token denominated in native asset. - * - * @param tokenPrice - the price of the token relative to a native asset or a bridging asset like the U.S. dollar. - * @param nativeAssetPrice - the price of the native asset relative to a bridging asset or 1 if no bridging needed. - * @param tokenOracleReverse - flag indicating direction of the "tokenPrice". - * @param nativeOracleReverse - flag indicating direction of the "nativeAssetPrice". - * @return the native-asset-per-token price multiplied by the PRICE_DENOMINATOR constant. - */ - function calculatePrice( - uint256 tokenPrice, - uint256 nativeAssetPrice, - bool tokenOracleReverse, - bool nativeOracleReverse - ) private view returns (uint256) { - // tokenPrice is normalized as bridging-asset-per-token - if (tokenOracleReverse) { - // inverting tokenPrice that was tokens-per-bridging-asset (or tokens-per-native-asset) - tokenPrice = (PRICE_DENOMINATOR * tokenOracleDecimalPower) / tokenPrice; - } else { - // tokenPrice already bridging-asset-per-token (or native-asset-per-token) - tokenPrice = (PRICE_DENOMINATOR * tokenPrice) / tokenOracleDecimalPower; - } - - if (nativeOracleReverse) { - // multiplying by nativeAssetPrice that is native-asset-per-bridging-asset - // => result = (bridging-asset / token) * (native-asset / bridging-asset) = native-asset / token - return (nativeAssetPrice * tokenPrice) / nativeOracleDecimalPower; - } else { - // dividing by nativeAssetPrice that is bridging-asset-per-native-asset - // => result = (bridging-asset / token) / (bridging-asset / native-asset) = native-asset / token - return (tokenPrice * nativeOracleDecimalPower) / nativeAssetPrice; - } - } - - /// @notice Fetches the latest price from the given Oracle. - /// @dev This function is used to get the latest price from the tokenOracle or nativeOracle. - /// @param _oracle The Oracle contract to fetch the price from. - /// @return price The latest price fetched from the Oracle. - function fetchPrice(IOracle _oracle) internal view returns (uint256 price) { - (uint80 roundId, int256 answer, , uint256 updatedAt, uint80 answeredInRound) = _oracle.latestRoundData(); - require(answer > 0, "TPM: Chainlink price <= 0"); - require(updatedAt >= block.timestamp - oracleHelperConfig.maxOracleRoundAge, "TPM: Incomplete round"); - require(answeredInRound >= roundId, "TPM: Stale price"); - price = uint256(answer); - } -} diff --git a/contracts/prebuilts/account/utils/SenderCreator.sol b/contracts/prebuilts/account/utils/SenderCreator.sol deleted file mode 100644 index 91ac6c882..000000000 --- a/contracts/prebuilts/account/utils/SenderCreator.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -/** - * Helper contract for EntryPoint, to call userOp.initCode from a "neutral" address, - * which is explicitly not the entryPoint itself. - */ -contract SenderCreator { - /** - * Call the "initCode" factory to create and return the sender account address. - * @param initCode - The initCode value from a UserOp. contains 20 bytes of factory address, - * followed by calldata. - * @return sender - The returned address of the created account, or zero address on failure. - */ - function createSender(bytes calldata initCode) external returns (address sender) { - address factory = address(bytes20(initCode[0:20])); - bytes memory initCallData = initCode[20:]; - bool success; - /* solhint-disable no-inline-assembly */ - assembly ("memory-safe") { - success := call(gas(), factory, 0, add(initCallData, 0x20), mload(initCallData), 0, 32) - sender := mload(0) - } - if (!success) { - sender = address(0); - } - } -} diff --git a/contracts/prebuilts/account/utils/StakeManager.sol b/contracts/prebuilts/account/utils/StakeManager.sol deleted file mode 100644 index f53ecb8b5..000000000 --- a/contracts/prebuilts/account/utils/StakeManager.sol +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -pragma solidity ^0.8.23; - -import "../interfaces/IStakeManager.sol"; - -/* solhint-disable avoid-low-level-calls */ -/* solhint-disable not-rely-on-time */ - -/** - * Manage deposits and stakes. - * Deposit is just a balance used to pay for UserOperations (either by a paymaster or an account). - * Stake is value locked for at least "unstakeDelay" by a paymaster. - */ -abstract contract StakeManager is IStakeManager { - /// maps paymaster to their deposits and stakes - mapping(address => DepositInfo) public deposits; - - /// @inheritdoc IStakeManager - function getDepositInfo(address account) public view returns (DepositInfo memory info) { - return deposits[account]; - } - - /** - * Internal method to return just the stake info. - * @param addr - The account to query. - */ - function _getStakeInfo(address addr) internal view returns (StakeInfo memory info) { - DepositInfo storage depositInfo = deposits[addr]; - info.stake = depositInfo.stake; - info.unstakeDelaySec = depositInfo.unstakeDelaySec; - } - - /// @inheritdoc IStakeManager - function balanceOf(address account) public view returns (uint256) { - return deposits[account].deposit; - } - - receive() external payable { - depositTo(msg.sender); - } - - /** - * Increments an account's deposit. - * @param account - The account to increment. - * @param amount - The amount to increment by. - * @return the updated deposit of this account - */ - function _incrementDeposit(address account, uint256 amount) internal returns (uint256) { - DepositInfo storage info = deposits[account]; - uint256 newAmount = info.deposit + amount; - info.deposit = newAmount; - return newAmount; - } - - /** - * Add to the deposit of the given account. - * @param account - The account to add to. - */ - function depositTo(address account) public payable virtual { - uint256 newDeposit = _incrementDeposit(account, msg.value); - emit Deposited(account, newDeposit); - } - - /** - * Add to the account's stake - amount and delay - * any pending unstake is first cancelled. - * @param unstakeDelaySec The new lock duration before the deposit can be withdrawn. - */ - function addStake(uint32 unstakeDelaySec) public payable { - DepositInfo storage info = deposits[msg.sender]; - require(unstakeDelaySec > 0, "must specify unstake delay"); - require(unstakeDelaySec >= info.unstakeDelaySec, "cannot decrease unstake time"); - uint256 stake = info.stake + msg.value; - require(stake > 0, "no stake specified"); - require(stake <= type(uint112).max, "stake overflow"); - deposits[msg.sender] = DepositInfo(info.deposit, true, uint112(stake), unstakeDelaySec, 0); - emit StakeLocked(msg.sender, stake, unstakeDelaySec); - } - - /** - * Attempt to unlock the stake. - * The value can be withdrawn (using withdrawStake) after the unstake delay. - */ - function unlockStake() external { - DepositInfo storage info = deposits[msg.sender]; - require(info.unstakeDelaySec != 0, "not staked"); - require(info.staked, "already unstaking"); - uint48 withdrawTime = uint48(block.timestamp) + info.unstakeDelaySec; - info.withdrawTime = withdrawTime; - info.staked = false; - emit StakeUnlocked(msg.sender, withdrawTime); - } - - /** - * Withdraw from the (unlocked) stake. - * Must first call unlockStake and wait for the unstakeDelay to pass. - * @param withdrawAddress - The address to send withdrawn value. - */ - function withdrawStake(address payable withdrawAddress) external { - DepositInfo storage info = deposits[msg.sender]; - uint256 stake = info.stake; - require(stake > 0, "No stake to withdraw"); - require(info.withdrawTime > 0, "must call unlockStake() first"); - require(info.withdrawTime <= block.timestamp, "Stake withdrawal is not due"); - info.unstakeDelaySec = 0; - info.withdrawTime = 0; - info.stake = 0; - emit StakeWithdrawn(msg.sender, withdrawAddress, stake); - (bool success, ) = withdrawAddress.call{ value: stake }(""); - require(success, "failed to withdraw stake"); - } - - /** - * Withdraw from the deposit. - * @param withdrawAddress - The address to send withdrawn value. - * @param withdrawAmount - The amount to withdraw. - */ - function withdrawTo(address payable withdrawAddress, uint256 withdrawAmount) external { - DepositInfo storage info = deposits[msg.sender]; - require(withdrawAmount <= info.deposit, "Withdraw amount too large"); - info.deposit = info.deposit - withdrawAmount; - emit Withdrawn(msg.sender, withdrawAddress, withdrawAmount); - (bool success, ) = withdrawAddress.call{ value: withdrawAmount }(""); - require(success, "failed to withdraw"); - } -} diff --git a/contracts/prebuilts/account/utils/TokenCallbackHandler.sol b/contracts/prebuilts/account/utils/TokenCallbackHandler.sol deleted file mode 100644 index 1900af613..000000000 --- a/contracts/prebuilts/account/utils/TokenCallbackHandler.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.12; - -/* solhint-disable no-empty-blocks */ - -import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol"; -import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; -import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; - -/** - * Token callback handler. - * Handles supported tokens' callbacks, allowing account receiving these tokens. - */ -contract TokenCallbackHandler is IERC777Recipient, IERC721Receiver, IERC1155Receiver { - function tokensReceived( - address, - address, - address, - uint256, - bytes calldata, - bytes calldata - ) external pure override {} - - function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) { - return IERC721Receiver.onERC721Received.selector; - } - - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes calldata - ) external pure override returns (bytes4) { - return IERC1155Receiver.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] calldata, - uint256[] calldata, - bytes calldata - ) external pure override returns (bytes4) { - return IERC1155Receiver.onERC1155BatchReceived.selector; - } - - function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) { - return - interfaceId == type(IERC721Receiver).interfaceId || - interfaceId == type(IERC1155Receiver).interfaceId || - interfaceId == type(IERC165).interfaceId; - } -} diff --git a/contracts/prebuilts/account/utils/UniswapHelper.sol b/contracts/prebuilts/account/utils/UniswapHelper.sol deleted file mode 100644 index 30f5031ae..000000000 --- a/contracts/prebuilts/account/utils/UniswapHelper.sol +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -/* solhint-disable not-rely-on-time */ - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; - -import "@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.sol"; -import "@uniswap/v3-periphery/contracts/interfaces/IPeripheryPayments.sol"; - -abstract contract UniswapHelper { - event UniswapReverted(address tokenIn, address tokenOut, uint256 amountIn, uint256 amountOutMin); - - uint256 private constant PRICE_DENOMINATOR = 1e26; - - struct UniswapHelperConfig { - /// @notice Minimum native asset amount to receive from a single swap - uint256 minSwapAmount; - uint24 uniswapPoolFee; - uint8 slippage; - bool wethIsNativeAsset; - } - - /// @notice The Uniswap V3 SwapRouter contract - IV3SwapRouter public immutable uniswap; - - /// @notice The ERC20 token used for transaction fee payments - IERC20Metadata public immutable token; - - /// @notice The ERC-20 token that wraps the native asset for current chain - IERC20 public immutable wrappedNative; - - UniswapHelperConfig public uniswapHelperConfig; - - constructor( - IERC20Metadata _token, - IERC20 _wrappedNative, - IV3SwapRouter _uniswap, - UniswapHelperConfig memory _uniswapHelperConfig - ) { - _token.approve(address(_uniswap), type(uint256).max); - token = _token; - wrappedNative = _wrappedNative; - uniswap = _uniswap; - _setUniswapHelperConfiguration(_uniswapHelperConfig); - } - - function _setUniswapHelperConfiguration(UniswapHelperConfig memory _uniswapHelperConfig) internal { - uniswapHelperConfig = _uniswapHelperConfig; - } - - function _maybeSwapTokenToWeth(IERC20Metadata tokenIn, uint256 quote) internal returns (uint256) { - uint256 tokenBalance = tokenIn.balanceOf(address(this)); - uint256 tokenDecimals = tokenIn.decimals(); - - uint256 amountOutMin = addSlippage( - tokenToWei(tokenBalance, tokenDecimals, quote), - uniswapHelperConfig.slippage - ); - - if (amountOutMin < uniswapHelperConfig.minSwapAmount) { - return 0; - } - // note: calling 'swapToToken' but destination token is Wrapped Ether - return - swapToToken( - address(tokenIn), - address(wrappedNative), - tokenBalance, - amountOutMin, - uniswapHelperConfig.uniswapPoolFee - ); - } - - function addSlippage(uint256 amount, uint8 slippage) private pure returns (uint256) { - return (amount * (1000 - slippage)) / 1000; - } - - function tokenToWei(uint256 amount, uint256 decimals, uint256 price) public pure returns (uint256) { - return (amount * price * (10 ** (18 - decimals))) / PRICE_DENOMINATOR; - } - - function weiToToken(uint256 amount, uint256 decimals, uint256 price) public pure returns (uint256) { - return (amount * PRICE_DENOMINATOR) / (price * (10 ** (18 - decimals))); - } - - function unwrapWeth(uint256 amount) internal { - if (uniswapHelperConfig.wethIsNativeAsset) { - return; - } - IPeripheryPayments(address(uniswap)).unwrapWETH9(amount, address(this)); - } - - // swap ERC-20 tokens at market price - function swapToToken( - address tokenIn, - address tokenOut, - uint256 amountIn, - uint256 amountOutMin, - uint24 fee - ) internal returns (uint256 amountOut) { - IV3SwapRouter.ExactInputSingleParams memory params = IV3SwapRouter.ExactInputSingleParams( - tokenIn, //tokenIn - tokenOut, //tokenOut - fee, - address(uniswap), - amountIn, - amountOutMin, - 0 - ); - try uniswap.exactInputSingle(params) returns (uint256 _amountOut) { - amountOut = _amountOut; - } catch { - emit UniswapReverted(tokenIn, tokenOut, amountIn, amountOutMin); - amountOut = 0; - } - } -} diff --git a/contracts/prebuilts/account/utils/UserOperationLib.sol b/contracts/prebuilts/account/utils/UserOperationLib.sol deleted file mode 100644 index 0b5cbf719..000000000 --- a/contracts/prebuilts/account/utils/UserOperationLib.sol +++ /dev/null @@ -1,127 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -/* solhint-disable no-inline-assembly */ - -import "../interfaces/PackedUserOperation.sol"; -import { calldataKeccak, min } from "./Helpers.sol"; - -/** - * Utility functions helpful when working with UserOperation structs. - */ -library UserOperationLib { - uint256 public constant PAYMASTER_VALIDATION_GAS_OFFSET = 20; - uint256 public constant PAYMASTER_POSTOP_GAS_OFFSET = 36; - uint256 public constant PAYMASTER_DATA_OFFSET = 52; - /** - * Get sender from user operation data. - * @param userOp - The user operation data. - */ - function getSender(PackedUserOperation calldata userOp) internal pure returns (address) { - address data; - //read sender from userOp, which is first userOp member (saves 800 gas...) - assembly { - data := calldataload(userOp) - } - return address(uint160(data)); - } - - /** - * Relayer/block builder might submit the TX with higher priorityFee, - * but the user should not pay above what he signed for. - * @param userOp - The user operation data. - */ - function gasPrice(PackedUserOperation calldata userOp) internal view returns (uint256) { - unchecked { - (uint256 maxPriorityFeePerGas, uint256 maxFeePerGas) = unpackUints(userOp.gasFees); - if (maxFeePerGas == maxPriorityFeePerGas) { - //legacy mode (for networks that don't support basefee opcode) - return maxFeePerGas; - } - return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee); - } - } - - /** - * Pack the user operation data into bytes for hashing. - * @param userOp - The user operation data. - */ - function encode(PackedUserOperation calldata userOp) internal pure returns (bytes memory ret) { - address sender = getSender(userOp); - uint256 nonce = userOp.nonce; - bytes32 hashInitCode = calldataKeccak(userOp.initCode); - bytes32 hashCallData = calldataKeccak(userOp.callData); - bytes32 accountGasLimits = userOp.accountGasLimits; - uint256 preVerificationGas = userOp.preVerificationGas; - bytes32 gasFees = userOp.gasFees; - bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData); - - return - abi.encode( - sender, - nonce, - hashInitCode, - hashCallData, - accountGasLimits, - preVerificationGas, - gasFees, - hashPaymasterAndData - ); - } - - function unpackUints(bytes32 packed) internal pure returns (uint256 high128, uint256 low128) { - return (uint128(bytes16(packed)), uint128(uint256(packed))); - } - - //unpack just the high 128-bits from a packed value - function unpackHigh128(bytes32 packed) internal pure returns (uint256) { - return uint256(packed) >> 128; - } - - // unpack just the low 128-bits from a packed value - function unpackLow128(bytes32 packed) internal pure returns (uint256) { - return uint128(uint256(packed)); - } - - function unpackMaxPriorityFeePerGas(PackedUserOperation calldata userOp) internal pure returns (uint256) { - return unpackHigh128(userOp.gasFees); - } - - function unpackMaxFeePerGas(PackedUserOperation calldata userOp) internal pure returns (uint256) { - return unpackLow128(userOp.gasFees); - } - - function unpackVerificationGasLimit(PackedUserOperation calldata userOp) internal pure returns (uint256) { - return unpackHigh128(userOp.accountGasLimits); - } - - function unpackCallGasLimit(PackedUserOperation calldata userOp) internal pure returns (uint256) { - return unpackLow128(userOp.accountGasLimits); - } - - function unpackPaymasterVerificationGasLimit(PackedUserOperation calldata userOp) internal pure returns (uint256) { - return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET:PAYMASTER_POSTOP_GAS_OFFSET])); - } - - function unpackPostOpGasLimit(PackedUserOperation calldata userOp) internal pure returns (uint256) { - return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET:PAYMASTER_DATA_OFFSET])); - } - - function unpackPaymasterStaticFields( - bytes calldata paymasterAndData - ) internal pure returns (address paymaster, uint256 validationGasLimit, uint256 postOpGasLimit) { - return ( - address(bytes20(paymasterAndData[:PAYMASTER_VALIDATION_GAS_OFFSET])), - uint128(bytes16(paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET:PAYMASTER_POSTOP_GAS_OFFSET])), - uint128(bytes16(paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET:PAYMASTER_DATA_OFFSET])) - ); - } - - /** - * Hash the user operation data. - * @param userOp - The user operation data. - */ - function hash(PackedUserOperation calldata userOp) internal pure returns (bytes32) { - return keccak256(encode(userOp)); - } -} diff --git a/contracts/prebuilts/evolving-nfts/EvolvingNFT.sol b/contracts/prebuilts/evolving-nfts/EvolvingNFT.sol deleted file mode 100644 index 0f73209b2..000000000 --- a/contracts/prebuilts/evolving-nfts/EvolvingNFT.sol +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -/// @author thirdweb - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -import "@thirdweb-dev/dynamic-contracts/src/presets/BaseRouter.sol"; - -import "../../extension/Multicall.sol"; -import "../../extension/upgradeable/Initializable.sol"; -import "../../extension/upgradeable/init/ContractMetadataInit.sol"; -import "../../extension/upgradeable/init/RoyaltyInit.sol"; -import "../../extension/upgradeable/init/PrimarySaleInit.sol"; -import "../../extension/upgradeable/init/OwnableInit.sol"; -import "../../extension/upgradeable/init/PermissionsEnumerableInit.sol"; -import "../../extension/upgradeable/init/ERC2771ContextInit.sol"; -import "../../extension/upgradeable/init/ERC721AQueryableInit.sol"; - -contract EvolvingNFT is - Initializable, - BaseRouter, - Multicall, - ERC721AQueryableInit, - ERC2771ContextInit, - ContractMetadataInit, - RoyaltyInit, - PrimarySaleInit, - OwnableInit, - PermissionsEnumerableInit -{ - /// @dev Only EXTENSION_ROLE holders can update the contract's extensions. - bytes32 private constant EXTENSION_ROLE = keccak256("EXTENSION_ROLE"); - - constructor(Extension[] memory _extensions) BaseRouter(_extensions) { - _disableInitializers(); - } - - /// @dev Initializes the contract, like a constructor. - function initialize( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _saleRecipient, - address _royaltyRecipient, - uint128 _royaltyBps - ) external initializer initializerERC721A { - bytes32 _transferRole = keccak256("TRANSFER_ROLE"); - - // Initialize extensions - __BaseRouter_init(); - - // Initialize inherited contracts, most base-like -> most derived. - __ERC2771Context_init(_trustedForwarders); - __ERC721A_init(_name, _symbol); - - _setupContractURI(_contractURI); - _setupOwner(_defaultAdmin); - - _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); - _setupRole(EXTENSION_ROLE, _defaultAdmin); - _setupRole(keccak256("MINTER_ROLE"), _defaultAdmin); - _setupRole(_transferRole, _defaultAdmin); - _setupRole(_transferRole, address(0)); - - _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - _setupPrimarySaleRecipient(_saleRecipient); - - _setupRole(EXTENSION_ROLE, _defaultAdmin); - _setRoleAdmin(EXTENSION_ROLE, EXTENSION_ROLE); - } - - /*/////////////////////////////////////////////////////////////// - Internal functions - //////////////////////////////////////////////////////////////*/ - - /// @dev The start token ID for the contract. - function _startTokenId() internal pure override returns (uint256) { - return 1; - } - - /// @dev Returns whether all relevant permission and other checks are met before any upgrade. - function _isAuthorizedCallToUpgrade() internal view virtual override returns (bool) { - return _hasRole(EXTENSION_ROLE, msg.sender); - } - - /// @dev Checks whether an account has a particular role. - function _hasRole(bytes32 _role, address _account) internal view returns (bool) { - PermissionsStorage.Data storage data = PermissionsStorage.data(); - return data._hasRole[_role][_account]; - } -} diff --git a/contracts/prebuilts/evolving-nfts/EvolvingNFTLogic.sol b/contracts/prebuilts/evolving-nfts/EvolvingNFTLogic.sol deleted file mode 100644 index f7c645d40..000000000 --- a/contracts/prebuilts/evolving-nfts/EvolvingNFTLogic.sol +++ /dev/null @@ -1,251 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -/// @author thirdweb - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -// ========== External imports ========== - -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; - -import "../../eip/queryable/ERC721AQueryableUpgradeable.sol"; - -// ========== Internal imports ========== - -import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; -import { CurrencyTransferLib } from "../../lib/CurrencyTransferLib.sol"; - -// ========== Features ========== - -import "../../extension/Multicall.sol"; -import "../../extension/upgradeable/ContractMetadata.sol"; -import "../../extension/upgradeable/Royalty.sol"; -import "../../extension/upgradeable/PrimarySale.sol"; -import "../../extension/upgradeable/Ownable.sol"; -import "../../extension/upgradeable/Permissions.sol"; -import "../../extension/upgradeable/Drop.sol"; -import "../../extension/upgradeable/SharedMetadataBatch.sol"; -import { RulesEngine } from "../../extension/upgradeable/RulesEngine.sol"; - -contract EvolvingNFTLogic is - ContractMetadata, - Royalty, - PrimarySale, - Ownable, - SharedMetadataBatch, - Drop, - ERC2771ContextUpgradeable, - ERC721AQueryableUpgradeable -{ - using StringsUpgradeable for uint256; - using EnumerableSet for EnumerableSet.Bytes32Set; - - /*/////////////////////////////////////////////////////////////// - State variables - //////////////////////////////////////////////////////////////*/ - - /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles. - bytes32 private constant DEFAULT_ADMIN_ROLE = 0x00; - /// @dev Only TRANSFER_ROLE holders can have tokens transferred from or to them, during restricted transfers. - bytes32 private constant TRANSFER_ROLE = keccak256("TRANSFER_ROLE"); - /// @dev Only MINTER_ROLE holders can sign off on `MintRequest`s. - bytes32 private constant MINTER_ROLE = keccak256("MINTER_ROLE"); - - /*/////////////////////////////////////////////////////////////// - ERC 165 / 721 / 2981 logic - //////////////////////////////////////////////////////////////*/ - - /// @dev Returns the URI for a given tokenId. - function tokenURI( - uint256 _tokenId - ) public view virtual override(ERC721AUpgradeable, IERC721AUpgradeable) returns (string memory) { - if (!_exists(_tokenId)) { - revert("!ID"); - } - - // Get score - address owner = ownerOf(_tokenId); - uint256 score = 0; - - address engine = RulesEngine(address(this)).getRulesEngineOverride(); - if (engine != address(0)) { - score = RulesEngine(engine).getScore(owner); - } else { - score = RulesEngine(address(this)).getScore(owner); - } - - // Get the target ID i.e. `start` of the range that the score falls into. - bytes32[] memory ids = _sharedMetadataBatchStorage().ids.values(); - bytes32 targetId = 0; - uint256 check = 0; - - for (uint256 i = 0; i < ids.length; i += 1) { - uint256 id = uint256(ids[i]); - - if (id <= score && id >= check) { - targetId = ids[i]; - check = id; - } - } - return _getURIFromSharedMetadata(targetId, _tokenId); - } - - /// @dev See ERC 165 - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(ERC721AUpgradeable, IERC721AUpgradeable, IERC165) returns (bool) { - return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId; - } - - /// @dev The start token ID for the contract. - function _startTokenId() internal pure override returns (uint256) { - return 1; - } - - function startTokenId() public pure returns (uint256) { - return _startTokenId(); - } - - /*/////////////////////////////////////////////////////////////// - Internal functions - //////////////////////////////////////////////////////////////*/ - - /// @dev Collects and distributes the primary sale value of NFTs being claimed. - function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal override { - if (_pricePerToken == 0) { - require(msg.value == 0, "!Value"); - return; - } - - uint256 totalPrice = _quantityToClaim * _pricePerToken; - - bool validMsgValue; - if (_currency == CurrencyTransferLib.NATIVE_TOKEN) { - validMsgValue = msg.value == totalPrice; - } else { - validMsgValue = msg.value == 0; - } - require(validMsgValue, "!V"); - - address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient; - - CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice); - } - - /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim( - address _to, - uint256 _quantityBeingClaimed - ) internal override returns (uint256 startTokenId_) { - startTokenId_ = _nextTokenId(); - _safeMint(_to, _quantityBeingClaimed); - } - - /// @dev Checks whether primary sale recipient can be set in the given execution context. - function _canSetPrimarySaleRecipient() internal view override returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /// @dev Checks whether owner can be set in the given execution context. - function _canSetOwner() internal view override returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /// @dev Checks whether royalty info can be set in the given execution context. - function _canSetRoyaltyInfo() internal view override returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /// @dev Checks whether contract metadata can be set in the given execution context. - function _canSetContractURI() internal view override returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /// @dev Checks whether platform fee info can be set in the given execution context. - function _canSetClaimConditions() internal view override returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /// @dev Returns whether shared metadata can be set in the given execution context. - function _canSetSharedMetadata() internal view virtual override returns (bool) { - return _hasRole(MINTER_ROLE, _msgSender()); - } - - /// @dev Checks whether an account has a particular role. - function _hasRole(bytes32 _role, address _account) internal view returns (bool) { - PermissionsStorage.Data storage data = PermissionsStorage.data(); - return data._hasRole[_role][_account]; - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - /** - * Returns the total amount of tokens minted in the contract. - */ - function totalMinted() external view returns (uint256) { - unchecked { - return _nextTokenId() - _startTokenId(); - } - } - - /// @dev The tokenId of the next NFT that will be minted / lazy minted. - function nextTokenIdToMint() external view returns (uint256) { - return _nextTokenId(); - } - - /// @dev The next token ID of the NFT that can be claimed. - function nextTokenIdToClaim() external view returns (uint256) { - return _nextTokenId(); - } - - /// @dev Burns `tokenId`. See {ERC721-_burn}. - function burn(uint256 tokenId) external virtual { - // note: ERC721AUpgradeable's `_burn(uint256,bool)` internally checks for token approvals. - _burn(tokenId, true); - } - - /// @dev See {ERC721-_beforeTokenTransfer}. - function _beforeTokenTransfers( - address from, - address to, - uint256 startTokenId_, - uint256 quantity - ) internal virtual override { - super._beforeTokenTransfers(from, to, startTokenId_, quantity); - - // if transfer is restricted on the contract, we still want to allow burning and minting - if (!_hasRole(TRANSFER_ROLE, address(0)) && from != address(0) && to != address(0)) { - if (!_hasRole(TRANSFER_ROLE, from) && !_hasRole(TRANSFER_ROLE, to)) { - revert("!T"); - } - } - } - - function _dropMsgSender() internal view virtual override returns (address) { - return _msgSender(); - } - - function _msgSender() internal view virtual override returns (address sender) { - return ERC2771ContextUpgradeable._msgSender(); - } - - function _msgData() internal view virtual override returns (bytes calldata) { - return ERC2771ContextUpgradeable._msgData(); - } -} diff --git a/contracts/prebuilts/evolving-nfts/extension/RulesEngineExtension.sol b/contracts/prebuilts/evolving-nfts/extension/RulesEngineExtension.sol deleted file mode 100644 index 40e7abde4..000000000 --- a/contracts/prebuilts/evolving-nfts/extension/RulesEngineExtension.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -/// @author thirdweb - -import { PermissionsStorage } from "../../../extension/upgradeable/Permissions.sol"; -import { RulesEngine } from "../../../extension/upgradeable/RulesEngine.sol"; - -contract RulesEngineExtension is RulesEngine { - /// @dev Returns whether the rules of the contract can be set in the given execution context. - function _canSetRules() internal view virtual override returns (bool) { - return _hasRole(keccak256("MINTER_ROLE"), msg.sender); - } - - /// @dev Returns whether the rules engine used by the contract can be overriden in the given execution context. - function _canOverrideRulesEngine() internal view virtual override returns (bool) { - // DEFAULT_ADMIN_ROLE - return _hasRole(0x00, msg.sender); - } - - /// @dev Checks whether an account has a particular role. - function _hasRole(bytes32 _role, address _account) internal view returns (bool) { - PermissionsStorage.Data storage data = PermissionsStorage.data(); - return data._hasRole[_role][_account]; - } -} diff --git a/contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol b/contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol index 5d11d8796..a2f250592 100644 --- a/contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol +++ b/contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol @@ -20,10 +20,8 @@ import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721H import { ERC1155Holder, ERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; // ========== Internal imports ========== -import { BaseRouter, IRouter, IRouterState } from "@thirdweb-dev/dynamic-contracts/src/presets/BaseRouter.sol"; -import { ERC165 } from "../../../eip/ERC165.sol"; +import { Router, Multicall } from "../../../extension/plugin/Router.sol"; -import "../../../extension/Multicall.sol"; import "../../../extension/upgradeable/Initializable.sol"; import "../../../extension/upgradeable/ContractMetadata.sol"; import "../../../extension/upgradeable/PlatformFee.sol"; @@ -37,8 +35,6 @@ import { RoyaltyPaymentsLogic } from "../../../extension/upgradeable/RoyaltyPaym */ contract MarketplaceV3 is Initializable, - Multicall, - BaseRouter, ContractMetadata, PlatformFee, PermissionsEnumerable, @@ -47,7 +43,7 @@ contract MarketplaceV3 is RoyaltyPaymentsLogic, ERC721Holder, ERC1155Holder, - ERC165 + Router { /// @dev Only EXTENSION_ROLE holders can perform upgrades. bytes32 private constant EXTENSION_ROLE = keccak256("EXTENSION_ROLE"); @@ -62,21 +58,16 @@ contract MarketplaceV3 is Constructor + initializer logic //////////////////////////////////////////////////////////////*/ - /// @dev We accept constructor params as a struct to avoid `Stack too deep` errors. - struct MarketplaceConstructorParams { - Extension[] extensions; - address royaltyEngineAddress; - address nativeTokenWrapper; - } - constructor( - MarketplaceConstructorParams memory _marketplaceV3Params - ) BaseRouter(_marketplaceV3Params.extensions) RoyaltyPaymentsLogic(_marketplaceV3Params.royaltyEngineAddress) { - nativeTokenWrapper = _marketplaceV3Params.nativeTokenWrapper; + address _pluginMap, + address _royaltyEngineAddress, + address _nativeTokenWrapper + ) Router(_pluginMap) RoyaltyPaymentsLogic(_royaltyEngineAddress) { + nativeTokenWrapper = _nativeTokenWrapper; _disableInitializers(); } - receive() external payable { + receive() external payable override { assert(msg.sender == nativeTokenWrapper); // only accept ETH via fallback from the native token wrapper contract } @@ -88,9 +79,6 @@ contract MarketplaceV3 is address _platformFeeRecipient, uint16 _platformFeeBps ) external initializer { - // Initialize BaseRouter - __BaseRouter_init(); - // Initialize inherited contracts, most base-like -> most derived. __ReentrancyGuard_init(); __ERC2771Context_init(_trustedForwarders); @@ -128,12 +116,10 @@ contract MarketplaceV3 is function supportsInterface( bytes4 interfaceId - ) public view virtual override(ERC165, IERC165, ERC1155Receiver) returns (bool) { + ) public view virtual override(Router, IERC165, ERC1155Receiver) returns (bool) { return interfaceId == type(IERC1155Receiver).interfaceId || interfaceId == type(IERC721Receiver).interfaceId || - interfaceId == type(IRouter).interfaceId || - interfaceId == type(IRouterState).interfaceId || super.supportsInterface(interfaceId); } @@ -162,8 +148,8 @@ contract MarketplaceV3 is return data._hasRole[_role][_account]; } - /// @dev Returns whether all relevant permission and other checks are met before any upgrade. - function _isAuthorizedCallToUpgrade() internal view virtual override returns (bool) { + /// @dev Returns whether plug-in can be set in the given execution context. + function _canSetPlugin() internal view override returns (bool) { return _hasRole(EXTENSION_ROLE, msg.sender); } diff --git a/contracts/prebuilts/unaudited/burn-to-claim-drop/BurnToClaimDropERC721.sol b/contracts/prebuilts/unaudited/burn-to-claim-drop/BurnToClaimDropERC721.sol deleted file mode 100644 index 9346c1ff3..000000000 --- a/contracts/prebuilts/unaudited/burn-to-claim-drop/BurnToClaimDropERC721.sol +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -/// @author thirdweb - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -import "@thirdweb-dev/dynamic-contracts/src/presets/BaseRouter.sol"; - -import "../../../extension/Multicall.sol"; - -import "../../../extension/upgradeable/Initializable.sol"; -import "../../../extension/upgradeable/Permissions.sol"; -import "../../../extension/upgradeable/ERC2771ContextUpgradeable.sol"; - -import "../../../extension/upgradeable/init/ContractMetadataInit.sol"; -import "../../../extension/upgradeable/init/PlatformFeeInit.sol"; -import "../../../extension/upgradeable/init/RoyaltyInit.sol"; -import "../../../extension/upgradeable/init/PrimarySaleInit.sol"; -import "../../../extension/upgradeable/init/OwnableInit.sol"; -import "../../../extension/upgradeable/init/ERC721AInit.sol"; -import "../../../extension/upgradeable/init/PermissionsEnumerableInit.sol"; -import "../../../extension/upgradeable/init/ReentrancyGuardInit.sol"; - -contract BurnToClaimDropERC721 is - Initializable, - Multicall, - ERC2771ContextUpgradeable, - BaseRouter, - ContractMetadataInit, - PlatformFeeInit, - RoyaltyInit, - PrimarySaleInit, - OwnableInit, - PermissionsEnumerableInit, - ERC721AInit -{ - /*/////////////////////////////////////////////////////////////// - Constructor + initializer logic - //////////////////////////////////////////////////////////////*/ - - constructor(Extension[] memory _extensions) BaseRouter(_extensions) { - _disableInitializers(); - } - - /// @notice Initializes the contract. - function initialize( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _saleRecipient, - address _royaltyRecipient, - uint128 _royaltyBps, - uint128 _platformFeeBps, - address _platformFeeRecipient - ) external initializer { - // Initialize extensions - __BaseRouter_init(); - - // Initialize inherited contracts, most base-like -> most derived. - __ERC2771Context_init(_trustedForwarders); - __ERC721A_init(_name, _symbol); - - _setupContractURI(_contractURI); - _setupOwner(_defaultAdmin); - - _setupRoles(_defaultAdmin); - - _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - _setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - _setupPrimarySaleRecipient(_saleRecipient); - } - - /// @dev Called in the initialize function. Sets up roles. - function _setupRoles(address _defaultAdmin) internal onlyInitializing { - bytes32 _transferRole = keccak256("TRANSFER_ROLE"); - bytes32 _minterRole = keccak256("MINTER_ROLE"); - bytes32 _extensionRole = keccak256("EXTENSION_ROLE"); - bytes32 _defaultAdminRole = 0x00; - - _setupRole(_defaultAdminRole, _defaultAdmin); - _setupRole(_minterRole, _defaultAdmin); - _setupRole(_transferRole, _defaultAdmin); - _setupRole(_transferRole, address(0)); - _setupRole(_extensionRole, _defaultAdmin); - _setRoleAdmin(_extensionRole, _extensionRole); - } - - /*/////////////////////////////////////////////////////////////// - Contract identifiers - //////////////////////////////////////////////////////////////*/ - - /// @notice Returns the type of contract. - function contractType() external pure returns (bytes32) { - return bytes32("BurnToClaimDropERC721"); - } - - /// @notice Returns the contract version. - function contractVersion() external pure returns (uint8) { - return uint8(5); - } - - /*/////////////////////////////////////////////////////////////// - Internal functions - //////////////////////////////////////////////////////////////*/ - - /// @dev Returns whether all relevant permission and other checks are met before any upgrade. - function _isAuthorizedCallToUpgrade() internal view virtual override returns (bool) { - return _hasRole(keccak256("EXTENSION_ROLE"), msg.sender); - } - - /// @dev Checks whether an account holds the given role. - function _hasRole(bytes32 role, address addr) internal view returns (bool) { - PermissionsStorage.Data storage data = PermissionsStorage.data(); - return data._hasRole[role][addr]; - } - - /// @notice Returns the sender in the given execution context. - function _msgSender() - internal - view - virtual - override(ERC2771ContextUpgradeable, Multicall) - returns (address sender) - { - return ERC2771ContextUpgradeable._msgSender(); - } -} diff --git a/contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Logic.sol b/contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Logic.sol deleted file mode 100644 index 80d1411f6..000000000 --- a/contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Logic.sol +++ /dev/null @@ -1,362 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -/// @author thirdweb - -// $$\ $$\ $$\ $$\ $$\ -// $$ | $$ | \__| $$ | $$ | -// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ -// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ -// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | -// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | -// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | -// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ - -import { BurnToClaimDrop721Storage } from "./BurnToClaimDrop721Storage.sol"; - -import "../../../../lib/Strings.sol"; -import "../../../../lib/CurrencyTransferLib.sol"; - -import { IERC2981 } from "../../../../eip/interface/IERC2981.sol"; -import { Context, ERC721AUpgradeable, ERC721AStorage } from "../../../../eip/ERC721AUpgradeable.sol"; - -import { IERC2771Context } from "../../../../extension/interface/IERC2771Context.sol"; - -import { ERC2771ContextUpgradeable } from "../../../../extension/upgradeable/ERC2771ContextUpgradeable.sol"; -import { DelayedReveal } from "../../../../extension/upgradeable/DelayedReveal.sol"; -import { PrimarySale } from "../../../../extension/upgradeable/PrimarySale.sol"; -import { PlatformFee } from "../../../../extension/upgradeable/PlatformFee.sol"; -import { Royalty, IERC165 } from "../../../../extension/upgradeable/Royalty.sol"; -import { LazyMint } from "../../../../extension/upgradeable/LazyMint.sol"; -import { Drop } from "../../../../extension/upgradeable/Drop.sol"; -import { ContractMetadata } from "../../../../extension/upgradeable/ContractMetadata.sol"; -import { Ownable } from "../../../../extension/upgradeable/Ownable.sol"; -import { PermissionsStorage } from "../../../../extension/upgradeable/Permissions.sol"; -import { BurnToClaim, BurnToClaimStorage } from "../../../../extension/upgradeable/BurnToClaim.sol"; -import { ReentrancyGuard } from "../../../../extension/upgradeable/ReentrancyGuard.sol"; - -contract BurnToClaimDrop721Logic is - ContractMetadata, - PlatformFee, - Royalty, - PrimarySale, - Ownable, - BurnToClaim, - DelayedReveal, - LazyMint, - Drop, - ERC2771ContextUpgradeable, - ERC721AUpgradeable, - ReentrancyGuard -{ - using Strings for uint256; - - /*/////////////////////////////////////////////////////////////// - Constants - //////////////////////////////////////////////////////////////*/ - - /// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles. - bytes32 private constant DEFAULT_ADMIN_ROLE = 0x00; - /// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted. - bytes32 private constant TRANSFER_ROLE = keccak256("TRANSFER_ROLE"); - /// @dev Only MINTER_ROLE holders can sign off on `MintRequest`s and lazy mint tokens. - bytes32 private constant MINTER_ROLE = keccak256("MINTER_ROLE"); - - /// @dev Max bps in the thirdweb system. - uint256 private constant MAX_BPS = 10_000; - - /*/////////////////////////////////////////////////////////////// - Events - //////////////////////////////////////////////////////////////*/ - - /// @dev Emitted when the global max NFTs that can be minted is updated. - event MaxTotalMintedUpdated(uint256 maxTotalMinted); - - /*/////////////////////////////////////////////////////////////// - ERC 165 / 721 / 2981 logic - //////////////////////////////////////////////////////////////*/ - - /** - * @notice Returns the URI for a given tokenId. - * @dev The URI, for a given tokenId, is returned once it is lazy minted, even if it might not be actually minted. (See `LazyMint`) - */ - function tokenURI(uint256 _tokenId) public view override returns (string memory) { - (uint256 batchId, ) = _getBatchId(_tokenId); - string memory batchUri = _getBaseURI(_tokenId); - - if (isEncryptedBatch(batchId)) { - return string(abi.encodePacked(batchUri, "0")); - } else { - return string(abi.encodePacked(batchUri, _tokenId.toString())); - } - } - - /// @notice See ERC 165 - function supportsInterface( - bytes4 interfaceId - ) public view virtual override(ERC721AUpgradeable, IERC165) returns (bool) { - return super.supportsInterface(interfaceId) || type(IERC2981).interfaceId == interfaceId; - } - - /*/////////////////////////////////////////////////////////////// - Lazy minting + delayed-reveal logic - //////////////////////////////////////////////////////////////*/ - - /** - * @notice Lets an account with `MINTER_ROLE` lazy mint 'n' NFTs. - * The URIs for each token is the provided `_baseURIForTokens` + `{tokenId}`. - */ - function lazyMint( - uint256 _amount, - string calldata _baseURIForTokens, - bytes calldata _data - ) public override returns (uint256) { - uint256 nextId = nextTokenIdToLazyMint(); - if (_data.length > 0) { - (bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(_data, (bytes, bytes32)); - if (encryptedURI.length != 0 && provenanceHash != "") { - _setEncryptedData(nextId + _amount, _data); - } - } - - return super.lazyMint(_amount, _baseURIForTokens, _data); - } - - /// @notice Lets an account with `MINTER_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs. - function reveal(uint256 _index, bytes calldata _key) external returns (string memory revealedURI) { - require(_hasRole(MINTER_ROLE, _msgSender()), "not minter."); - uint256 batchId = getBatchIdAtIndex(_index); - revealedURI = getRevealURI(batchId, _key); - - _setEncryptedData(batchId, ""); - _setBaseURI(batchId, revealedURI); - - emit TokenURIRevealed(_index, revealedURI); - } - - /*/////////////////////////////////////////////////////////////// - Claiming lazy minted tokens logic - //////////////////////////////////////////////////////////////*/ - - /// @notice Claim lazy minted tokens after burning required tokens from origin contract. - function burnAndClaim(uint256 _burnTokenId, uint256 _quantity) external payable nonReentrant { - _checkTokenSupply(_quantity); - - // Verify and burn tokens on origin contract - address _tokenOwner = _dropMsgSender(); - verifyBurnToClaim(_tokenOwner, _burnTokenId, _quantity); - _burnTokensOnOrigin(_tokenOwner, _burnTokenId, _quantity); - - // Collect price - _collectPriceOnClaim( - address(0), - _quantity, - _burnToClaimStorage().burnToClaimInfo.currency, - _burnToClaimStorage().burnToClaimInfo.mintPriceForNewToken - ); - - // Mint tokens. - _safeMint(_tokenOwner, _quantity); - - // emit event - emit TokensBurnedAndClaimed( - _burnToClaimStorage().burnToClaimInfo.originContractAddress, - _tokenOwner, - _burnTokenId, - _quantity - ); - } - - /*/////////////////////////////////////////////////////////////// - Setter functions - //////////////////////////////////////////////////////////////*/ - - /// @notice Lets a contract admin set the global maximum NFTs that can be minted. - function setMaxTotalMinted(uint256 _maxTotalMinted) external { - require(_hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "not admin."); - - BurnToClaimDrop721Storage.Data storage data = BurnToClaimDrop721Storage.burnToClaimDrop721Storage(); - data.maxTotalMinted = _maxTotalMinted; - emit MaxTotalMintedUpdated(_maxTotalMinted); - } - - /*/////////////////////////////////////////////////////////////// - Internal functions - //////////////////////////////////////////////////////////////*/ - - /// @dev Check if given quantity is available for minting. - function _checkTokenSupply(uint256 _quantity) internal view { - uint256 _maxTotalMinted = maxTotalMinted(); - uint256 currentTotalMinted = totalMinted(); - - require(currentTotalMinted + _quantity <= nextTokenIdToLazyMint(), "!Tokens"); - require( - _maxTotalMinted == 0 || currentTotalMinted + _quantity <= _maxTotalMinted, - "exceed max total mint cap." - ); - } - - /// @dev Runs before every `claim` function call. - function _beforeClaim( - address, - uint256 _quantity, - address, - uint256, - AllowlistProof calldata, - bytes memory - ) internal view override { - _checkTokenSupply(_quantity); - } - - /// @dev Collects and distributes the primary sale value of NFTs being claimed. - function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal override { - if (_pricePerToken == 0) { - require(msg.value == 0, "!Value"); - return; - } - - (address platformFeeRecipient, uint16 platformFeeBps) = getPlatformFeeInfo(); - - address saleRecipient = _primarySaleRecipient == address(0) ? primarySaleRecipient() : _primarySaleRecipient; - - uint256 totalPrice = _quantityToClaim * _pricePerToken; - uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS; - - bool validMsgValue; - if (_currency == CurrencyTransferLib.NATIVE_TOKEN) { - validMsgValue = msg.value == totalPrice; - } else { - validMsgValue = msg.value == 0; - } - require(validMsgValue, "Invalid msg value"); - - CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees); - CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice - platformFees); - } - - /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim( - address _to, - uint256 _quantityBeingClaimed - ) internal override returns (uint256 startTokenId) { - ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage(); - startTokenId = data._currentIndex; - _safeMint(_to, _quantityBeingClaimed); - } - - /// @dev Checks whether platform fee info can be set in the given execution context. - function _canSetPlatformFeeInfo() internal view override returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /// @dev Checks whether primary sale recipient can be set in the given execution context. - function _canSetPrimarySaleRecipient() internal view override returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /// @dev Checks whether owner can be set in the given execution context. - function _canSetOwner() internal view override returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /// @dev Checks whether royalty info can be set in the given execution context. - function _canSetRoyaltyInfo() internal view override returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /// @dev Checks whether contract metadata can be set in the given execution context. - function _canSetContractURI() internal view override returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /// @dev Checks whether platform fee info can be set in the given execution context. - function _canSetClaimConditions() internal view override returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /// @dev Returns whether lazy minting can be done in the given execution context. - function _canLazyMint() internal view virtual override returns (bool) { - return _hasRole(MINTER_ROLE, _msgSender()); - } - - /// @dev Returns whether burn-to-claim info can be set in the given execution context. - function _canSetBurnToClaim() internal view virtual override returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - /** - * @notice Returns the total amount of tokens minted in the contract. - */ - function totalMinted() public view returns (uint256) { - ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage(); - unchecked { - return data._currentIndex - _startTokenId(); - } - } - - /// @notice The tokenId of the next NFT that will be minted / lazy minted. - function nextTokenIdToMint() external view returns (uint256) { - return nextTokenIdToLazyMint(); - } - - /// @notice The next token ID of the NFT that can be claimed. - function nextTokenIdToClaim() external view returns (uint256) { - ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage(); - return data._currentIndex; - } - - /// @notice Global max total NFTs that can be minted. - function maxTotalMinted() public view returns (uint256) { - BurnToClaimDrop721Storage.Data storage data = BurnToClaimDrop721Storage.burnToClaimDrop721Storage(); - return data.maxTotalMinted; - } - - /// @notice Burns `tokenId`. See {ERC721-_burn}. - function burn(uint256 tokenId) external virtual { - // note: ERC721AUpgradeable's `_burn(uint256,bool)` internally checks for token approvals. - _burn(tokenId, true); - } - - /// @dev See {ERC721-_beforeTokenTransfer}. - function _beforeTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity - ) internal virtual override { - super._beforeTokenTransfers(from, to, startTokenId, quantity); - - // if transfer is restricted on the contract, we still want to allow burning and minting - if (!_hasRole(TRANSFER_ROLE, address(0)) && from != address(0) && to != address(0)) { - if (!_hasRole(TRANSFER_ROLE, from) && !_hasRole(TRANSFER_ROLE, to)) { - revert("!Transfer-Role"); - } - } - } - - function _hasRole(bytes32 role, address addr) internal view returns (bool) { - PermissionsStorage.Data storage data = PermissionsStorage.data(); - return data._hasRole[role][addr]; - } - - function _dropMsgSender() internal view virtual override returns (address) { - return _msgSender(); - } - - function _msgSender() internal view virtual override(Context, ERC2771ContextUpgradeable) returns (address) { - return ERC2771ContextUpgradeable._msgSender(); - } - - function _msgData() internal view virtual override(Context, ERC2771ContextUpgradeable) returns (bytes calldata) { - return ERC2771ContextUpgradeable._msgData(); - } -} diff --git a/contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Storage.sol b/contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Storage.sol deleted file mode 100644 index 05d8a350e..000000000 --- a/contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Storage.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -library BurnToClaimDrop721Storage { - /// @custom:storage-location erc7201:burn.to.claim.drop.721.storage - /// @dev keccak256(abi.encode(uint256(keccak256("burn.to.claim.drop.721.storage")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 public constant BURN_TO_CLAIM_DROP_721_STORAGE_POSITION = - 0x3107fcf7768de14f3c3441e6960e7a1659b448f798b4e6665bf2dc61db3ea300; - - struct Data { - /// @dev Global max total NFTs that can be minted. - uint256 maxTotalMinted; - } - - function burnToClaimDrop721Storage() internal pure returns (Data storage burnToClaimDrop721Data) { - bytes32 position = BURN_TO_CLAIM_DROP_721_STORAGE_POSITION; - assembly { - burnToClaimDrop721Data.slot := position - } - } -} diff --git a/contracts/prebuilts/unaudited/contract-builder/CoreRouter.sol b/contracts/prebuilts/unaudited/contract-builder/CoreRouter.sol deleted file mode 100644 index d504362f2..000000000 --- a/contracts/prebuilts/unaudited/contract-builder/CoreRouter.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: MIT -// @author: thirdweb (https://github.com/thirdweb-dev/dynamic-contracts) - -pragma solidity ^0.8.0; - -// Interface -import "lib/dynamic-contracts/src/presets/BaseRouter.sol"; - -// Core -import "lib/dynamic-contracts/src/core/Router.sol"; - -// Utils -import "lib/dynamic-contracts/src/lib/StringSet.sol"; -import "./extension/PermissionOverride.sol"; - -// Fixed extensions -import "../../../extension/Ownable.sol"; -import "../../../extension/ContractMetadata.sol"; - -/** - * //////////// - * - * NOTE: This contract is a work in progress, and has not been audited. - * - * //////////// - */ - -contract CoreRouter is BaseRouter, ContractMetadata, Ownable { - using StringSet for StringSet.Set; - - /*/////////////////////////////////////////////////////////////// - Constructor - //////////////////////////////////////////////////////////////*/ - - constructor(address _owner, Extension[] memory _extensions) BaseRouter(_extensions) { - // Initialize extensions - __BaseRouter_init(); - - _setupOwner(_owner); - } - - /*/////////////////////////////////////////////////////////////// - Internal functions - //////////////////////////////////////////////////////////////*/ - - /// @dev Returns whether all relevant permission and other checks are met before any upgrade. - function _isAuthorizedCallToUpgrade() internal view virtual override returns (bool) { - return msg.sender == owner(); - } - - /// @dev Returns whether contract metadata can be set in the given execution context. - function _canSetContractURI() internal view virtual override returns (bool) { - return msg.sender == owner(); - } - - /// @dev Returns whether owner can be set in the given execution context. - function _canSetOwner() internal view virtual override returns (bool) { - return msg.sender == owner(); - } -} diff --git a/contracts/prebuilts/unaudited/contract-builder/extension/PermissionOverride.sol b/contracts/prebuilts/unaudited/contract-builder/extension/PermissionOverride.sol deleted file mode 100644 index ba5ba5dcf..000000000 --- a/contracts/prebuilts/unaudited/contract-builder/extension/PermissionOverride.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -/// @author thirdweb - -/** - * //////////// - * - * NOTE: This contract is a work in progress, and has not been audited. - * - * //////////// - */ - -library PermissionsStorage { - bytes32 public constant PERMISSIONS_STORAGE_POSITION = keccak256("permissions.storage"); - - struct Data { - /// @dev Map from keccak256 hash of a role => a map from address => whether address has role. - mapping(bytes32 => mapping(address => bool)) _hasRole; - /// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}. - mapping(bytes32 => bytes32) _getRoleAdmin; - } - - function permissionsStorage() internal pure returns (Data storage permissionsData) { - bytes32 position = PERMISSIONS_STORAGE_POSITION; - assembly { - permissionsData.slot := position - } - } -} - -contract PermissionOverrideCoreRouter { - bytes32 private constant DEFAULT_ADMIN_ROLE = 0x00; - bytes32 private constant EXTENSION_ROLE = keccak256("EXTENSION_ROLE"); - - function canSetContractURI(address _caller) public view returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _caller); - } - - function canSetOwner(address _caller) public view returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _caller); - } - - function canSetExtension(address _caller) public view returns (bool) { - return _hasRole(DEFAULT_ADMIN_ROLE, _caller); - } - - function _hasRole(bytes32 role, address account) internal view returns (bool) { - PermissionsStorage.Data storage data = PermissionsStorage.permissionsStorage(); - return data._hasRole[role][account]; - } -} diff --git a/foundry.toml b/foundry.toml index 464eeb8d6..55e866609 100644 --- a/foundry.toml +++ b/foundry.toml @@ -29,9 +29,6 @@ optimizer = true optimizer_runs = 20 out = 'artifacts_forge' remappings = [ - '@uniswap/v3-core/contracts=lib/v3-core/contracts', - '@uniswap/v3-periphery/contracts=lib/v3-periphery/contracts', - '@uniswap/swap-router-contracts/contracts=lib/swap-router-contracts/contracts', '@chainlink/=lib/chainlink/', '@openzeppelin/contracts=lib/openzeppelin-contracts/contracts', '@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/', diff --git a/hardhat.config.ts b/hardhat.config.ts new file mode 100644 index 000000000..b8164d534 --- /dev/null +++ b/hardhat.config.ts @@ -0,0 +1,80 @@ +/** @type import('hardhat/config').HardhatUserConfig */ +// import "@nomicfoundation/hardhat-toolbox"; +import "@nomicfoundation/hardhat-foundry"; +import "@matterlabs/hardhat-zksync-deploy"; +import "@matterlabs/hardhat-zksync-solc"; +import "@matterlabs/hardhat-zksync-verify"; +import { HardhatUserConfig } from "hardhat/config"; +import dotenv from "dotenv"; + +dotenv.config(); + +const config: HardhatUserConfig = { + zksolc: { + version: "1.4.1", // Uses latest available in https://github.com/matter-labs/zksolc-bin/ + settings: { + optimizer: { + enabled: true, + runs: 20, + }, + }, + }, + // defaultNetwork: "zkSyncSepoliaTestnet", + networks: { + hardhat: { + zksync: false, + }, + zkCandySepolia: { + url: "https://sepolia.rpc.zkcandy.io", + ethNetwork: "sepolia", + zksync: true, + verifyURL: "https://sepolia.contract-verifier.zkcandy.io/contract_verification", + }, + zkSyncSepoliaTestnet: { + url: "https://sepolia.era.zksync.dev", + ethNetwork: "sepolia", // or a Sepolia RPC endpoint from Infura/Alchemy/Chainstack etc. + zksync: true, + verifyURL: "https://explorer.sepolia.era.zksync.dev/contract_verification", + }, + zkSyncMainnet: { + url: "https://mainnet.era.zksync.io", + ethNetwork: "mainnet", + zksync: true, + verifyURL: "https://zksync2-mainnet-explorer.zksync.io/contract_verification", + }, + }, + paths: { + artifacts: "./artifacts-zk", + cache: "./cache-zk", + sources: "./contracts", + tests: "./test", + }, + solidity: { + version: "0.8.23", + settings: { + // You should disable the optimizer when debugging + // https://hardhat.org/hardhat-network/#solidity-optimizer-support + optimizer: { + enabled: true, + runs: 20, + }, + metadata: { + bytecodeHash: "ipfs", + }, + outputSelection: { + "*": { + "*": [ + "metadata", + "abi", + "evm.bytecode.object", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.object", + "evm.deployedBytecode.sourceMap", + ], + }, + }, + }, + }, +}; + +export default config; diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index dc44c9f1a..fd81a96f0 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit dc44c9f1a4c3b10af99492eed84f83ed244203f6 +Subproject commit fd81a96f01cc42ef1c9a5399364968d0e07e9e90 diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable index 2d081f24c..3d4c0d574 160000 --- a/lib/openzeppelin-contracts-upgradeable +++ b/lib/openzeppelin-contracts-upgradeable @@ -1 +1 @@ -Subproject commit 2d081f24cac1a867f6f73d512f2022e1fa987854 +Subproject commit 3d4c0d5741b131c231e558d7a6213392ab3672a5 diff --git a/lib/swap-router-contracts b/lib/swap-router-contracts deleted file mode 160000 index c696aada4..000000000 --- a/lib/swap-router-contracts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c696aada49b33c8e764e6f0bd0a0a56bd8aa455f diff --git a/lib/v3-core b/lib/v3-core deleted file mode 160000 index e3589b192..000000000 --- a/lib/v3-core +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e3589b192d0be27e100cd0daaf6c97204fdb1899 diff --git a/lib/v3-periphery b/lib/v3-periphery deleted file mode 160000 index 80f26c86c..000000000 --- a/lib/v3-periphery +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 80f26c86c57b8a5e4b913f42844d4c8bd274d058 diff --git a/package.json b/package.json index 4f6f888c2..c3ae720a3 100644 --- a/package.json +++ b/package.json @@ -8,16 +8,31 @@ "/contracts/**/*.sol" ], "devDependencies": { + "@matterlabs/hardhat-zksync": "^0.1.0", + "@matterlabs/hardhat-zksync-deploy": "^1.3.0", + "@matterlabs/hardhat-zksync-solc": "^1.1.4", + "@matterlabs/zksync-contracts": "^0.6.1", + "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "@nomicfoundation/hardhat-foundry": "^1.1.1", + "@nomicfoundation/hardhat-network-helpers": "^1.0.0", + "@nomicfoundation/hardhat-toolbox": "^4.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.0", + "@nomiclabs/hardhat-etherscan": "^3.1.7", "@openzeppelin/contracts": "^4.9.6", "@openzeppelin/contracts-upgradeable": "^4.9.6", "@thirdweb-dev/dynamic-contracts": "^1.2.4", "@thirdweb-dev/merkletree": "^0.2.6", "@typechain/ethers-v5": "^10.2.1", + "@typechain/ethers-v6": "^0.5.0", + "@typechain/hardhat": "^9.0.0", + "@types/chai": "^4.2.0", "@types/fs-extra": "^9.0.13", "@types/mocha": "^9.1.1", "@types/node": "^17.0.45", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", + "chai": "^4.2.0", "dotenv": "^16.3.1", "erc721a": "3.3.0", "erc721a-upgradeable": "^3.3.0", @@ -25,18 +40,25 @@ "eslint-config-prettier": "^8.10.0", "ethers": "^5.7.2", "fs-extra": "^10.1.0", + "hardhat": "^2.19.1", + "hardhat-contract-sizer": "2.5.0", + "hardhat-gas-reporter": "^1.0.8", "keccak256": "^1.0.6", "mocha": "^9.2.2", "prettier": "^2.8.8", "prettier-plugin-solidity": "^1.2.0", + "seaport-core": "^1.6.6", + "seaport-types": "^1.6.3", "solady": "0.0.180", "solhint": "^3.6.2", "solhint-plugin-prettier": "^0.0.5", + "solidity-coverage": "^0.8.1", "ts-node": "^10.9.1", "tslib": "^2.6.2", "tsup": "^5.12.9", "typechain": "^8.3.2", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "zksync-ethers": "^5.7.0" }, "peerDependencies": { "ethers": "^5.0.0" diff --git a/scripts/deploy/zksync/airdrop-zksync.ts b/scripts/deploy/zksync/airdrop-zksync.ts new file mode 100644 index 000000000..cf4f65217 --- /dev/null +++ b/scripts/deploy/zksync/airdrop-zksync.ts @@ -0,0 +1,45 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("Airdrop")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await contractFactory.deploy(); + await contract.deployed(); + + console.log("Deployed Airdrop \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/airdrop/Airdrop.sol:Airdrop", []); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/droperc1155-zksync.ts b/scripts/deploy/zksync/droperc1155-zksync.ts new file mode 100644 index 000000000..2ba776000 --- /dev/null +++ b/scripts/deploy/zksync/droperc1155-zksync.ts @@ -0,0 +1,45 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("DropERC1155")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await contractFactory.deploy(); + await contract.deployed(); + + console.log("Deployed DropERC1155 \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/drop/DropERC1155.sol:DropERC1155", []); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/droperc20-zksync.ts b/scripts/deploy/zksync/droperc20-zksync.ts new file mode 100644 index 000000000..b28f5f367 --- /dev/null +++ b/scripts/deploy/zksync/droperc20-zksync.ts @@ -0,0 +1,45 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("DropERC20")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await contractFactory.deploy(); + await contract.deployed(); + + console.log("Deployed DropERC20 \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/drop/DropERC20.sol:DropERC20", []); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/droperc721-zksync.ts b/scripts/deploy/zksync/droperc721-zksync.ts new file mode 100644 index 000000000..fb46aec02 --- /dev/null +++ b/scripts/deploy/zksync/droperc721-zksync.ts @@ -0,0 +1,45 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("DropERC721")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await contractFactory.deploy(); + await contract.deployed(); + + console.log("Deployed DropERC721 \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/drop/DropERC721.sol:DropERC721", []); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/editionStake-zksync.ts b/scripts/deploy/zksync/editionStake-zksync.ts new file mode 100644 index 000000000..72638804c --- /dev/null +++ b/scripts/deploy/zksync/editionStake-zksync.ts @@ -0,0 +1,50 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("EditionStake")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + // WETH address as constructor param + // Zksync Mainnet: 0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91 + // Zksync Sepolia: 0x0462C05457Fed440740Ff3696bDd2D0577411e34 + const contract = await contractFactory.deploy("0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91"); + await contract.deployed(); + + console.log("Deployed EditionStake \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/staking/EditionStake.sol:EditionStake", [ + "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", + ]); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/loyaltycard-zksync.ts b/scripts/deploy/zksync/loyaltycard-zksync.ts new file mode 100644 index 000000000..d8da2858f --- /dev/null +++ b/scripts/deploy/zksync/loyaltycard-zksync.ts @@ -0,0 +1,45 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("LoyaltyCard")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await contractFactory.deploy(); + await contract.deployed(); + + console.log("Deployed LoyaltyCard \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/loyalty/LoyaltyCard.sol:LoyaltyCard", []); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/multiwrap-zksync.ts b/scripts/deploy/zksync/multiwrap-zksync.ts new file mode 100644 index 000000000..aa07b1f3e --- /dev/null +++ b/scripts/deploy/zksync/multiwrap-zksync.ts @@ -0,0 +1,50 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("Multiwrap")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + // WETH address as constructor param + // Zksync Mainnet: 0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91 + // Zksync Sepolia: 0x0462C05457Fed440740Ff3696bDd2D0577411e34 + const contract = await contractFactory.deploy("0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91"); + await contract.deployed(); + + console.log("Deployed Pack \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/multiwrap/Multiwrap.sol:Multiwrap", [ + "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", + ]); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/nftStake-zksync.ts b/scripts/deploy/zksync/nftStake-zksync.ts new file mode 100644 index 000000000..7246fbb12 --- /dev/null +++ b/scripts/deploy/zksync/nftStake-zksync.ts @@ -0,0 +1,50 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("NFTStake")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + // WETH address as constructor param + // Zksync Mainnet: 0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91 + // Zksync Sepolia: 0x0462C05457Fed440740Ff3696bDd2D0577411e34 + const contract = await contractFactory.deploy("0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91"); + await contract.deployed(); + + console.log("Deployed NFTStake \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/staking/NFTStake.sol:NFTStake", [ + "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", + ]); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/openEdition-zksync.ts b/scripts/deploy/zksync/openEdition-zksync.ts new file mode 100644 index 000000000..bd4eb1e5f --- /dev/null +++ b/scripts/deploy/zksync/openEdition-zksync.ts @@ -0,0 +1,45 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("OpenEditionERC721")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await contractFactory.deploy(); + await contract.deployed(); + + console.log("Deployed OpenEditionERC721 \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/open-edition/OpenEditionERC721.sol:OpenEditionERC721", []); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/pack-zksync.ts b/scripts/deploy/zksync/pack-zksync.ts new file mode 100644 index 000000000..ebb60d554 --- /dev/null +++ b/scripts/deploy/zksync/pack-zksync.ts @@ -0,0 +1,50 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("Pack")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + // WETH address as constructor param + // Zksync Mainnet: 0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91 + // Zksync Sepolia: 0x0462C05457Fed440740Ff3696bDd2D0577411e34 + const contract = await contractFactory.deploy("0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91"); + await contract.deployed(); + + console.log("Deployed Pack \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/pack/Pack.sol:Pack", [ + "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", + ]); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/plugin/directListingsLogic-zksync.ts b/scripts/deploy/zksync/plugin/directListingsLogic-zksync.ts new file mode 100644 index 000000000..da5d9e959 --- /dev/null +++ b/scripts/deploy/zksync/plugin/directListingsLogic-zksync.ts @@ -0,0 +1,62 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("DirectListingsLogic")) as ZkSyncArtifact; + const directListings = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + // WETH address as constructor param + // Zksync Mainnet: 0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91 + // Zksync Sepolia: 0x0462C05457Fed440740Ff3696bDd2D0577411e34 + // abstract testnet: 0x9EDCde0257F2386Ce177C3a7FCdd97787F0D841d + // lens sepolia testnet: 0xaA91D645D7a6C1aeaa5988e0547267B77d33fe16 + // xsolla testnet: 0xb0b8b267d44c64BA6dD1Daf442949887c85199f6 + // abstract mainnet: 0x3439153EB7AF838Ad19d56E1571FBD09333C2809 + const contract = await directListings.deploy("0x3439153EB7AF838Ad19d56E1571FBD09333C2809"); + await contract.deployed(); + + console.log("Deployed DirectListingsLogic \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + // deployed address zksync mainnet: 0xfaCf7A60a24E28534c643653c974540343f5ff09 + // deployed address zksync sepolia: 0x8b0DBCf5b7D01eBB0F24525CE8AB72F16CE4F8C8 + // deployed address abstract testnet: 0xab2b0451C07f35FB490A3939BD4DfE327013d2A6 + // lens sepolia testnet: 0xF73EFC402e9467ED756598193dD74ac4C1615724 + // xsolla testnet: 0x038890935747f67B45c83fe99a15B0A94AEb996c + // abstract mainnet: 0x26279882D5E93045D4FA986847CAAC048b2Bac3b + await verify( + contract.address, + "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol:DirectListingsLogic", + ["0x3439153EB7AF838Ad19d56E1571FBD09333C2809"], + ); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/plugin/englishAuctionsLogic-zksync.ts b/scripts/deploy/zksync/plugin/englishAuctionsLogic-zksync.ts new file mode 100644 index 000000000..fbfa7d28c --- /dev/null +++ b/scripts/deploy/zksync/plugin/englishAuctionsLogic-zksync.ts @@ -0,0 +1,62 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("EnglishAuctionsLogic")) as ZkSyncArtifact; + const englishAuctions = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + // WETH address as constructor param + // Zksync Mainnet: 0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91 + // Zksync Sepolia: 0x0462C05457Fed440740Ff3696bDd2D0577411e34 + // abstract testnet: 0x9EDCde0257F2386Ce177C3a7FCdd97787F0D841d + // lens sepolia testnet: 0xaA91D645D7a6C1aeaa5988e0547267B77d33fe16 + // xsolla testnet: 0xb0b8b267d44c64BA6dD1Daf442949887c85199f6 + // abstract mainnet: 0x3439153EB7AF838Ad19d56E1571FBD09333C2809 + const contract = await englishAuctions.deploy("0x3439153EB7AF838Ad19d56E1571FBD09333C2809"); + await contract.deployed(); + + console.log("Deployed EnglishAuctionsLogic \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + // deployed address zksync mainnet: 0xcd86890BC05dC3118DAC90330722b23c6cc970e2 + // deployed address zksync sepolia: 0xefE2fF8F3282Fd63898cb0A532099BA7780b459F + // deployed address abstract testnet: 0x7689AB65593EDdD140c53d697253b533F54CeA1B + // lens sepolia testnet: 0xe320543648D417858f9666C45C9723AB6355cA84 + // xsolla testnet: 0xF73EFC402e9467ED756598193dD74ac4C1615724 + // abstract mainnet: 0xf3C7d3F0AA374a2D32489929e24D3e9313Aec8bb + await verify( + contract.address, + "contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol:EnglishAuctionsLogic", + ["0x3439153EB7AF838Ad19d56E1571FBD09333C2809"], + ); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/plugin/marketplaceRouter-zksync.ts b/scripts/deploy/zksync/plugin/marketplaceRouter-zksync.ts new file mode 100644 index 000000000..153364137 --- /dev/null +++ b/scripts/deploy/zksync/plugin/marketplaceRouter-zksync.ts @@ -0,0 +1,67 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("MarketplaceV3")) as ZkSyncArtifact; + const marketplaceV3 = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + // WETH address as constructor param + // Zksync Mainnet: 0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91 + // Zksync Sepolia: 0x0462C05457Fed440740Ff3696bDd2D0577411e34 + // abstract testnet: 0x9EDCde0257F2386Ce177C3a7FCdd97787F0D841d + // lens sepolia testnet: 0xaA91D645D7a6C1aeaa5988e0547267B77d33fe16 + // xsolla testnet: 0xb0b8b267d44c64BA6dD1Daf442949887c85199f6 + // abstract mainnet: 0x3439153EB7AF838Ad19d56E1571FBD09333C2809 + const contract = await marketplaceV3.deploy( + "0x9742f5ac11958cFAd151eBF0Fc31302fA409036E", // pluginMap address + "0x0000000000000000000000000000000000000000", // royalty engine address - set to address(0) + "0x3439153EB7AF838Ad19d56E1571FBD09333C2809", // WETH address + ); + await contract.deployed(); + + console.log("Deployed MarketplaceV3 \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + + // deployed address zksync mainnet: 0xBc02441a36Bb4029Cd191b20243c2e41B862F118 + // deployed address zksync sepolia: 0x58e0F289C7dD2025eBd0696d913ECC0fdc1CC8bc + // deployed address abstract testnet: 0x2dA4Dd326A6482679547071be21f74685d730504 + // lens sepolia testnet: 0x56Abb6a3f25DCcdaDa106191053b1CC54C196DEE + // xsolla testnet: 0x9EB0830B0b10010F2a53383517A7D0B75531Bb1b + // abstract mainnet: 0x4027561E163a420c4e5Db46E07EBd581CAa8Bb62 + await verify(contract.address, "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol:MarketplaceV3", [ + "0x9742f5ac11958cFAd151eBF0Fc31302fA409036E", + "0x0000000000000000000000000000000000000000", + "0x3439153EB7AF838Ad19d56E1571FBD09333C2809", + ]); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/plugin/offersLogic-zksync.ts b/scripts/deploy/zksync/plugin/offersLogic-zksync.ts new file mode 100644 index 000000000..2535c2d0b --- /dev/null +++ b/scripts/deploy/zksync/plugin/offersLogic-zksync.ts @@ -0,0 +1,51 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("OffersLogic")) as ZkSyncArtifact; + const offers = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await offers.deploy(); + await contract.deployed(); + + console.log("Deployed OffersLogic \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + // deployed address zksync mainnet: 0x5f4964a30b86B626BCAAaCc4622CB70d76c844f2 + // deployed address zksync sepolia: 0xB89DbEe6fA8664507b0f7758bCc532817CAf6Eb2 + // deployed address abstract testnet: 0x98B25911d02851b0a39D5947ac6012efC92E6c79 + // deployed address lens testnet: 0x038890935747f67B45c83fe99a15B0A94AEb996c + // deployed address xsolla testnet: 0x4c7416f13deB20215Ab1A163B63b35E03Fa3Fae1 + // abstract mainnet: 0x56Abb6a3f25DCcdaDa106191053b1CC54C196DEE + await verify(contract.address, "contracts/prebuilts/marketplace/offers/OffersLogic.sol:OffersLogic", []); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/plugin/pluginMap-zksync.ts b/scripts/deploy/zksync/plugin/pluginMap-zksync.ts new file mode 100644 index 000000000..ad16a8e62 --- /dev/null +++ b/scripts/deploy/zksync/plugin/pluginMap-zksync.ts @@ -0,0 +1,112 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; +import { ethers } from "ethers"; + +dotenv.config(); + +type PluginMapInput = { + functionSelector: string; + functionSignature: string; + pluginAddress: string; +}; + +const getFunctionSignature = (fnInputs: any): string => { + return ( + "(" + + fnInputs + .map((i: any) => { + return i.type === "tuple" ? getFunctionSignature(i.components) : i.type; + }) + .join(",") + + ")" + ); +}; + +const generatePluginFunctions = (pluginAddress: string, pluginAbi: any): PluginMapInput[] => { + const pluginInterface = new ethers.utils.Interface(pluginAbi); + const pluginFunctions = []; + // TODO - filter out common functions like _msgSender(), contractType(), etc. + for (const fnFragment of Object.values(pluginInterface.functions)) { + const fn = pluginInterface.getFunction(fnFragment.name); + if (fn.name.startsWith("_")) { + continue; + } + pluginFunctions.push({ + functionSelector: pluginInterface.getSighash(fn), + functionSignature: fn.name + getFunctionSignature(fn.inputs), + pluginAddress: pluginAddress, + }); + } + return pluginFunctions; +}; + +async function getPluginInput() { + const DirectListingsLogicArtifact: ZkSyncArtifact = (await hre.artifacts.readArtifact( + "DirectListingsLogic", + )) as ZkSyncArtifact; + const pluginsDirectListings = generatePluginFunctions( + "0x26279882D5E93045D4FA986847CAAC048b2Bac3b", + DirectListingsLogicArtifact.abi, + ); + + const EnglishAuctionsLogicArtifact: ZkSyncArtifact = (await hre.artifacts.readArtifact( + "EnglishAuctionsLogic", + )) as ZkSyncArtifact; + const pluginsEnglishAuctions = generatePluginFunctions( + "0xf3C7d3F0AA374a2D32489929e24D3e9313Aec8bb", + EnglishAuctionsLogicArtifact.abi, + ); + + const OffersLogicABI: ZkSyncArtifact = (await hre.artifacts.readArtifact("OffersLogic")) as ZkSyncArtifact; + const pluginsOffers = generatePluginFunctions("0x56Abb6a3f25DCcdaDa106191053b1CC54C196DEE", OffersLogicABI.abi); + + return [...pluginsDirectListings, ...pluginsEnglishAuctions, ...pluginsOffers]; +} + +async function main() { + const pluginInput = await getPluginInput(); + + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("PluginMap")) as ZkSyncArtifact; + const map = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await map.deploy(pluginInput); + await contract.deployed(); + + console.log("Deployed PluginMap \n: ", contract.address); + console.log("\n"); + console.log("Verifying contract."); + + // deployed address zksync mainnet: 0x0326643B8844710065C9ce0e5326B006608E8D8d + // deployed address zksync sepolia: 0xC2f4B1B6B3d6813aBc8e55B3BAd0796526A5d633 + // deployed address abstract testnet: 0xa6344b5B22c13444Ed46709fE810108Bc14BdB2b + // deployed address lens testnet: 0x6dc0A7c9c0E79883345e2384B7619EA1D1199C3C + // xsolla testnet: 0xf415B06d4C62F03066DA67C2c6401818701EE430 + // abstract mainnet: + await verify(contract.address, "contracts/extension/plugin/PluginMap.sol:PluginMap", [pluginInput]); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/split-zksync.ts b/scripts/deploy/zksync/split-zksync.ts new file mode 100644 index 000000000..fb46aec02 --- /dev/null +++ b/scripts/deploy/zksync/split-zksync.ts @@ -0,0 +1,45 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("DropERC721")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await contractFactory.deploy(); + await contract.deployed(); + + console.log("Deployed DropERC721 \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/drop/DropERC721.sol:DropERC721", []); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/tokenStake-zksync.ts b/scripts/deploy/zksync/tokenStake-zksync.ts new file mode 100644 index 000000000..64b8a7102 --- /dev/null +++ b/scripts/deploy/zksync/tokenStake-zksync.ts @@ -0,0 +1,50 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("TokenStake")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + // WETH address as constructor param + // Zksync Mainnet: 0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91 + // Zksync Sepolia: 0x0462C05457Fed440740Ff3696bDd2D0577411e34 + const contract = await contractFactory.deploy("0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91"); + await contract.deployed(); + + console.log("Deployed TokenStake \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/staking/TokenStake.sol:TokenStake", [ + "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", + ]); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/tokenerc1155-zksync.ts b/scripts/deploy/zksync/tokenerc1155-zksync.ts new file mode 100644 index 000000000..6d4c4a9ab --- /dev/null +++ b/scripts/deploy/zksync/tokenerc1155-zksync.ts @@ -0,0 +1,45 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("TokenERC1155")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await contractFactory.deploy(); + await contract.deployed(); + + console.log("Deployed TokenERC1155 \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/token/TokenERC1155.sol:TokenERC1155", []); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/tokenerc20-zksync.ts b/scripts/deploy/zksync/tokenerc20-zksync.ts new file mode 100644 index 000000000..c38878a54 --- /dev/null +++ b/scripts/deploy/zksync/tokenerc20-zksync.ts @@ -0,0 +1,45 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("TokenERC20")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await contractFactory.deploy(); + await contract.deployed(); + + console.log("Deployed TokenERC20 \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/token/TokenERC20.sol:TokenERC20", []); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/tokenerc721-zksync.ts b/scripts/deploy/zksync/tokenerc721-zksync.ts new file mode 100644 index 000000000..f44dd8a9e --- /dev/null +++ b/scripts/deploy/zksync/tokenerc721-zksync.ts @@ -0,0 +1,45 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("TokenERC721")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await contractFactory.deploy(); + await contract.deployed(); + + console.log("Deployed TokenERC721 \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/token/TokenERC721.sol:TokenERC721", []); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/scripts/deploy/zksync/vote-zksync.ts b/scripts/deploy/zksync/vote-zksync.ts new file mode 100644 index 000000000..9f33bd185 --- /dev/null +++ b/scripts/deploy/zksync/vote-zksync.ts @@ -0,0 +1,45 @@ +import { Wallet, Provider, ContractFactory } from "zksync-ethers"; +import * as hre from "hardhat"; +import dotenv from "dotenv"; +import { ZkSyncArtifact } from "@matterlabs/hardhat-zksync-solc/dist/src/types"; + +import "@matterlabs/hardhat-zksync-node/dist/type-extensions"; +import "@matterlabs/hardhat-zksync-verify/dist/src/type-extensions"; + +dotenv.config(); + +async function main() { + const provider = new Provider(hre.network.config.url); + const wallet = new Wallet(`${process.env.TEST_PRIVATE_KEY}`, provider); + const artifact: ZkSyncArtifact = (await hre.artifacts.readArtifact("VoteERC20")) as ZkSyncArtifact; + const contractFactory = new ContractFactory(artifact.abi, artifact.bytecode, wallet, "create"); + + const contract = await contractFactory.deploy(); + await contract.deployed(); + + console.log("Deployed VoteERC20 \n: ", contract.address); + + console.log("\n"); + + console.log("Verifying contract."); + await verify(contract.address, "contracts/prebuilts/vote/VoteERC20.sol:VoteERC20", []); +} + +async function verify(address: string, contract: string, args: any[]) { + try { + return await hre.run("verify:verify", { + address: address, + contract: contract, + constructorArguments: args, + }); + } catch (e) { + console.log(address, args, e); + } +} + +main() + .then(() => process.exit(0)) + .catch(e => { + console.error(e); + process.exit(1); + }); diff --git a/src/test/ContractPublisher.t.sol b/src/test/ContractPublisher.t.sol deleted file mode 100644 index e80fe7d43..000000000 --- a/src/test/ContractPublisher.t.sol +++ /dev/null @@ -1,487 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Target contracts -import { ContractPublisher } from "contracts/infra/ContractPublisher.sol"; -import "contracts/infra/interface/IContractPublisher.sol"; -import "contracts/infra/TWRegistry.sol"; - -// Test helpers -import { BaseTest, MockContractPublisher } from "./utils/BaseTest.sol"; -import "@openzeppelin/contracts/utils/Create2.sol"; - -contract MockCustomContract { - uint256 public num; - - constructor(uint256 _num) { - num = _num; - } -} - -contract IContractPublisherData { - /// @dev Emitted when the registry is paused. - event Paused(bool isPaused); - - /// @dev Emitted when a publisher's approval of an operator is updated. - event Approved(address indexed publisher, address indexed operator, bool isApproved); - - /// @dev Emitted when a contract is published. - event ContractPublished( - address indexed operator, - address indexed publisher, - IContractPublisher.CustomContractInstance publishedContract - ); - - /// @dev Emitted when a contract is unpublished. - event ContractUnpublished(address indexed operator, address indexed publisher, string indexed contractId); - - /// @dev Emitted when a published contract is added to the public list. - event AddedContractToPublicList(address indexed publisher, string indexed contractId); - - /// @dev Emitted when a published contract is removed from the public list. - event RemovedContractToPublicList(address indexed publisher, string indexed contractId); -} - -contract ContractPublisherTest is BaseTest, IContractPublisherData { - ContractPublisher internal byoc; - TWRegistry internal twRegistry; - - address internal publisher; - address internal operator; - address internal deployerOfPublished; - - string internal publishMetadataUri = "ipfs://QmeXyz"; - string internal compilerMetadataUri = "ipfs://QmeXyz"; - - function setUp() public override { - super.setUp(); - - byoc = ContractPublisher(contractPublisher); - twRegistry = TWRegistry(registry); - - publisher = getActor(0); - operator = getActor(1); - deployerOfPublished = getActor(2); - } - - function test_publish() public { - string memory contractId = "MyContract"; - vm.prank(publisher); - byoc.publishContract( - publisher, - contractId, - publishMetadataUri, - compilerMetadataUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - - IContractPublisher.CustomContractInstance memory customContract = byoc.getPublishedContract( - publisher, - contractId - ); - - assertEq(customContract.contractId, contractId); - assertEq(customContract.publishMetadataUri, publishMetadataUri); - assertEq(customContract.bytecodeHash, keccak256(type(MockCustomContract).creationCode)); - assertEq(customContract.implementation, address(0)); - } - - // Deprecated - // function test_publish_viaOperator() public { - // string memory contractId = "MyContract"; - - // vm.prank(publisher); - // byoc.approveOperator(operator, true); - - // vm.prank(operator); - // byoc.publishContract( - // publisher, - // publishMetadataUri, - // keccak256(type(MockCustomContract).creationCode), - // address(0), - // contractId - // ); - - // IContractPublisher.CustomContractInstance memory customContract = byoc.getPublishedContract( - // publisher, - // contractId - // ); - - // assertEq(customContract.contractId, contractId); - // assertEq(customContract.publishMetadataUri, publishMetadataUri); - // assertEq(customContract.bytecodeHash, keccak256(type(MockCustomContract).creationCode)); - // assertEq(customContract.implementation, address(0)); - // } - - function test_state_setPrevPublisher() public { - // === when prevPublisher address is address(0) - vm.prank(factoryAdmin); - byoc.setPrevPublisher(IContractPublisher(address(0))); - - assertEq(byoc.getAllPublishedContracts(publisher).length, 0); - assertEq(address(byoc.prevPublisher()), address(0)); - - string memory contractId = "MyContract"; - vm.prank(publisher); - byoc.publishContract( - publisher, - contractId, - publishMetadataUri, - compilerMetadataUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - IContractPublisher.CustomContractInstance[] memory contracts = byoc.getAllPublishedContracts(publisher); - assertEq(contracts.length, 1); - assertEq(contracts[0].contractId, "MyContract"); - - // === when prevPublisher address is set to MockPublisher - address mock = address(new MockContractPublisher()); - vm.prank(factoryAdmin); - byoc.setPrevPublisher(IContractPublisher(mock)); - - contracts = byoc.getAllPublishedContracts(publisher); - assertEq(contracts.length, 2); - assertEq(address(byoc.prevPublisher()), mock); - assertEq(contracts[0].contractId, "MockContract"); - assertEq(contracts[1].contractId, "MyContract"); - } - - function test_revert_setPrevPublisher() public { - vm.expectRevert("Not authorized"); - byoc.setPrevPublisher(IContractPublisher(address(0))); - } - - function test_state_setPublisherProfileUri() public { - address user = address(0x123); - string memory uriOne = "ipfs://one"; - string memory uriTwo = "ipfs://two"; - - // user updating for self - vm.prank(user); - byoc.setPublisherProfileUri(user, uriOne); - assertEq(byoc.getPublisherProfileUri(user), uriOne); - - // random caller - vm.prank(address(0x345)); - vm.expectRevert("Registry paused or caller not authorized"); - byoc.setPublisherProfileUri(user, uriOne); - - // MIGRATION_ROLE holder updating for a user - vm.prank(factoryAdmin); - byoc.setPublisherProfileUri(user, uriTwo); - assertEq(byoc.getPublisherProfileUri(user), uriTwo); - } - - function test_state_setPublisherProfileUri_whenPaused() public { - vm.prank(factoryAdmin); - byoc.setPause(true); - address user = address(0x123); - string memory uriOne = "ipfs://one"; - string memory uriTwo = "ipfs://two"; - - // user updating for self - vm.prank(user); - vm.expectRevert("Registry paused or caller not authorized"); - byoc.setPublisherProfileUri(user, uriOne); - - // MIGRATION_ROLE holder updating for a user - vm.prank(factoryAdmin); - byoc.setPublisherProfileUri(user, uriTwo); - assertEq(byoc.getPublisherProfileUri(user), uriTwo); - } - - function test_publish_revert_unapprovedCaller() public { - string memory contractId = "MyContract"; - - vm.expectRevert("unapproved caller"); - - vm.prank(operator); - byoc.publishContract( - publisher, - contractId, - publishMetadataUri, - compilerMetadataUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - } - - function test_publish_revert_registryPaused() public { - string memory contractId = "MyContract"; - - vm.prank(factoryAdmin); - byoc.setPause(true); - - vm.expectRevert("registry paused"); - - vm.prank(publisher); - byoc.publishContract( - publisher, - contractId, - publishMetadataUri, - compilerMetadataUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - } - - function test_publish_multiple_versions() public { - string memory contractId = "MyContract"; - - vm.prank(publisher); - byoc.publishContract( - publisher, - contractId, - publishMetadataUri, - compilerMetadataUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - string[] memory resolved = byoc.getPublishedUriFromCompilerUri(compilerMetadataUri); - assertEq(resolved.length, 1); - assertEq(resolved[0], publishMetadataUri); - - string memory otherUri = "ipfs://abcd"; - vm.prank(publisher); - byoc.publishContract( - publisher, - contractId, - publishMetadataUri, - otherUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - - string[] memory resolved2 = byoc.getPublishedUriFromCompilerUri(otherUri); - assertEq(resolved2.length, 1); - assertEq(resolved2[0], publishMetadataUri); - } - - function test_read_from_linked_publisher() public { - IContractPublisher.CustomContractInstance[] memory contracts = byoc.getAllPublishedContracts(publisher); - assertEq(contracts.length, 1); - assertEq(contracts[0].contractId, "MockContract"); - - string memory contractId = "MyContract"; - vm.prank(publisher); - byoc.publishContract( - publisher, - contractId, - publishMetadataUri, - compilerMetadataUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - IContractPublisher.CustomContractInstance[] memory contracts2 = byoc.getAllPublishedContracts(publisher); - assertEq(contracts2.length, 2); - assertEq(contracts2[0].contractId, "MockContract"); - assertEq(contracts2[1].contractId, "MyContract"); - } - - // Deprecated - // function test_publish_emit_ContractPublished() public { - // string memory contractId = "MyContract"; - - // vm.prank(publisher); - // byoc.approveOperator(operator, true); - - // IContractPublisher.CustomContractInstance memory expectedCustomContract = IContractPublisher - // .CustomContractInstance({ - // contractId: contractId, - // publishTimestamp: 100, - // publishMetadataUri: publishMetadataUri, - // bytecodeHash: keccak256(type(MockCustomContract).creationCode), - // implementation: address(0) - // }); - - // vm.expectEmit(true, true, true, true); - // emit ContractPublished(operator, publisher, expectedCustomContract); - - // vm.warp(100); - // vm.prank(operator); - // byoc.publishContract( - // publisher, - // publishMetadataUri, - // keccak256(type(MockCustomContract).creationCode), - // address(0), - // contractId - // ); - // } - - function test_unpublish_state() public { - string memory contractId = "MyContract"; - - vm.startPrank(publisher); - byoc.publishContract( - publisher, - contractId, - "publish URI 1", - compilerMetadataUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - byoc.publishContract( - publisher, - contractId, - "publish URI 2", - compilerMetadataUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - byoc.publishContract( - publisher, - contractId, - "publish URI 3", - compilerMetadataUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - - vm.stopPrank(); - - IContractPublisher.CustomContractInstance[] memory allCustomContractsBefore = byoc.getPublishedContractVersions( - publisher, - contractId - ); - assertEq(allCustomContractsBefore.length, 3); - - vm.prank(publisher); - byoc.unpublishContract(publisher, contractId); - - IContractPublisher.CustomContractInstance memory customContract = byoc.getPublishedContract( - publisher, - contractId - ); - - assertEq(customContract.contractId, ""); - assertEq(customContract.publishMetadataUri, ""); - assertEq(customContract.bytecodeHash, bytes32(0)); - assertEq(customContract.implementation, address(0)); - - IContractPublisher.CustomContractInstance[] memory allCustomContracts = byoc.getPublishedContractVersions( - publisher, - contractId - ); - - assertEq(allCustomContracts.length, 0); - - vm.prank(publisher); - byoc.publishContract( - publisher, - contractId, - "publish URI 4", - compilerMetadataUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - - IContractPublisher.CustomContractInstance memory customContractRepublish = byoc.getPublishedContract( - publisher, - contractId - ); - - assertEq(customContractRepublish.contractId, contractId); - assertEq(customContractRepublish.publishMetadataUri, "publish URI 4"); - - IContractPublisher.CustomContractInstance[] memory allCustomContractsRepublish = byoc - .getPublishedContractVersions(publisher, contractId); - - assertEq(allCustomContractsRepublish.length, 1); - } - - // Deprecated - // function test_unpublish_viaOperator() public { - // string memory contractId = "MyContract"; - - // vm.prank(publisher); - // byoc.publishContract( - // publisher, - // publishMetadataUri, - // keccak256(type(MockCustomContract).creationCode), - // address(0), - // contractId - // ); - - // vm.prank(publisher); - // byoc.approveOperator(operator, true); - - // vm.prank(operator); - // byoc.unpublishContract(publisher, contractId); - - // IContractPublisher.CustomContractInstance memory customContract = byoc.getPublishedContract( - // publisher, - // contractId - // ); - - // assertEq(customContract.contractId, ""); - // assertEq(customContract.publishMetadataUri, ""); - // assertEq(customContract.bytecodeHash, bytes32(0)); - // assertEq(customContract.implementation, address(0)); - // } - - function test_unpublish_revert_unapprovedCaller() public { - string memory contractId = "MyContract"; - - vm.prank(publisher); - byoc.publishContract( - publisher, - contractId, - publishMetadataUri, - compilerMetadataUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - - vm.expectRevert("unapproved caller"); - - vm.prank(operator); - byoc.unpublishContract(publisher, contractId); - } - - function test_unpublish_revert_registryPaused() public { - string memory contractId = "MyContract"; - - vm.prank(publisher); - byoc.publishContract( - publisher, - contractId, - publishMetadataUri, - compilerMetadataUri, - keccak256(type(MockCustomContract).creationCode), - address(0) - ); - - vm.prank(factoryAdmin); - byoc.setPause(true); - - vm.expectRevert("registry paused"); - - vm.prank(publisher); - byoc.unpublishContract(publisher, contractId); - } - - // Deprecated - // function test_unpublish_emit_ContractUnpublished() public { - // string memory contractId = "MyContract"; - - // vm.prank(publisher); - // byoc.publishContract( - // publisher, - // publishMetadataUri, - // keccak256(type(MockCustomContract).creationCode), - // address(0), - // contractId - // ); - - // vm.prank(publisher); - // byoc.approveOperator(operator, true); - - // vm.expectEmit(true, true, true, true); - // emit ContractUnpublished(operator, publisher, contractId); - - // vm.prank(operator); - // byoc.unpublishContract(publisher, contractId); - // } -} diff --git a/src/test/EvolvingNFT.t.sol b/src/test/EvolvingNFT.t.sol deleted file mode 100644 index 8a695b420..000000000 --- a/src/test/EvolvingNFT.t.sol +++ /dev/null @@ -1,1208 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { IExtension } from "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { EvolvingNFT } from "contracts/prebuilts/evolving-nfts/EvolvingNFT.sol"; -import { EvolvingNFTLogic } from "contracts/prebuilts/evolving-nfts/EvolvingNFTLogic.sol"; -import { RulesEngineExtension } from "contracts/prebuilts/evolving-nfts/extension/RulesEngineExtension.sol"; - -import { IDrop } from "contracts/extension/interface/IDrop.sol"; -import { Drop } from "contracts/extension/upgradeable/Drop.sol"; -import { SharedMetadataBatch } from "contracts/extension/upgradeable/SharedMetadataBatch.sol"; -import { ISharedMetadataBatch } from "contracts/extension/interface/ISharedMetadataBatch.sol"; -import { RulesEngine, IRulesEngine } from "contracts/extension/upgradeable/RulesEngine.sol"; -import { NFTMetadataRenderer } from "contracts/lib/NFTMetadataRenderer.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { PermissionsEnumerable as DynamicPermissionsEnumerable } from "contracts/extension/upgradeable/PermissionsEnumerable.sol"; - -// Test imports -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import { Strings } from "contracts/lib/Strings.sol"; -import { Permissions } from "contracts/extension/Permissions.sol"; -import { IERC721 } from "./mocks/MockERC721.sol"; -import "./utils/BaseTest.sol"; - -contract EvolvingNFTTest is BaseTest { - using Strings for uint256; - using Strings for address; - - event SharedMetadataUpdated( - bytes32 indexed id, - string name, - string description, - string imageURI, - string animationURI - ); - - address public evolvingNFT; - - mapping(uint256 => ISharedMetadataBatch.SharedMetadataInfo) public sharedMetadataBatch; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - // Scores - uint256 private score1 = 10; - uint256 private score2 = 40; - uint256 private score3 = 100; - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - - // Setting up default extension. - IExtension.Extension memory evolvingNftExtension; - IExtension.Extension memory permissionsExtension; - IExtension.Extension memory rulesEngineExtension; - - evolvingNftExtension.metadata = IExtension.ExtensionMetadata({ - name: "EvolvingNFTLogic", - metadataURI: "ipfs://EvolvingNFTLogic", - implementation: address(new EvolvingNFTLogic()) - }); - permissionsExtension.metadata = IExtension.ExtensionMetadata({ - name: "Permissions", - metadataURI: "ipfs://Permissions", - implementation: address(new DynamicPermissionsEnumerable()) - }); - rulesEngineExtension.metadata = IExtension.ExtensionMetadata({ - name: "RulesEngine", - metadataURI: "ipfs://RulesEngine", - implementation: address(new RulesEngineExtension()) - }); - - evolvingNftExtension.functions = new IExtension.ExtensionFunction[](11); - rulesEngineExtension.functions = new IExtension.ExtensionFunction[](4); - permissionsExtension.functions = new IExtension.ExtensionFunction[](4); - - rulesEngineExtension.functions[0] = IExtension.ExtensionFunction( - RulesEngine.getScore.selector, - "getScore(address)" - ); - rulesEngineExtension.functions[1] = IExtension.ExtensionFunction( - RulesEngine.createRuleThreshold.selector, - "createRuleThreshold((address,uint8,uint256,uint256,uint256))" - ); - rulesEngineExtension.functions[2] = IExtension.ExtensionFunction( - RulesEngine.deleteRule.selector, - "deleteRule(bytes32)" - ); - rulesEngineExtension.functions[3] = IExtension.ExtensionFunction( - RulesEngine.getRulesEngineOverride.selector, - "getRulesEngineOverride()" - ); - evolvingNftExtension.functions[0] = IExtension.ExtensionFunction( - IDrop.claim.selector, - "claim(address,uint256,address,uint256,(bytes32[],uint256,uint256,address),bytes)" - ); - evolvingNftExtension.functions[1] = IExtension.ExtensionFunction( - SharedMetadataBatch.setSharedMetadata.selector, - "setSharedMetadata((string,string,string,string),bytes32)" - ); - evolvingNftExtension.functions[2] = IExtension.ExtensionFunction( - IDrop.setClaimConditions.selector, - "setClaimConditions((uint256,uint256,uint256,uint256,bytes32,uint256,address,string)[],bool)" - ); - evolvingNftExtension.functions[3] = IExtension.ExtensionFunction( - EvolvingNFTLogic.tokenURI.selector, - "tokenURI(uint256)" - ); - evolvingNftExtension.functions[4] = IExtension.ExtensionFunction( - IERC721Upgradeable.transferFrom.selector, - "transferFrom(address,address,uint256)" - ); - evolvingNftExtension.functions[5] = IExtension.ExtensionFunction(IERC721.ownerOf.selector, "ownerOf(uint256)"); - evolvingNftExtension.functions[6] = IExtension.ExtensionFunction( - Drop.getSupplyClaimedByWallet.selector, - "getSupplyClaimedByWallet(uint256,address)" - ); - evolvingNftExtension.functions[7] = IExtension.ExtensionFunction( - Drop.getActiveClaimConditionId.selector, - "getActiveClaimConditionId()" - ); - evolvingNftExtension.functions[8] = IExtension.ExtensionFunction( - Drop.getClaimConditionById.selector, - "getClaimConditionById(uint256)" - ); - evolvingNftExtension.functions[9] = IExtension.ExtensionFunction( - Drop.claimCondition.selector, - "claimCondition()" - ); - evolvingNftExtension.functions[10] = IExtension.ExtensionFunction( - SharedMetadataBatch.deleteSharedMetadata.selector, - "deleteSharedMetadata(bytes32)" - ); - permissionsExtension.functions[0] = IExtension.ExtensionFunction( - Permissions.renounceRole.selector, - "renounceRole(bytes32,address)" - ); - permissionsExtension.functions[1] = IExtension.ExtensionFunction( - Permissions.revokeRole.selector, - "revokeRole(bytes32,address)" - ); - permissionsExtension.functions[2] = IExtension.ExtensionFunction( - Permissions.grantRole.selector, - "grantRole(bytes32,address)" - ); - permissionsExtension.functions[3] = IExtension.ExtensionFunction( - Permissions.hasRole.selector, - "hasRole(bytes32,address)" - ); - - IExtension.Extension[] memory extensions = new IExtension.Extension[](3); - extensions[0] = evolvingNftExtension; - extensions[1] = permissionsExtension; - extensions[2] = rulesEngineExtension; - - address evolvingNftImpl = address(new EvolvingNFT(extensions)); - - vm.prank(deployer); - evolvingNFT = address( - new TWProxy( - evolvingNftImpl, - abi.encodeCall( - EvolvingNFT.initialize, - (deployer, NAME, SYMBOL, CONTRACT_URI, forwarders(), saleRecipient, royaltyRecipient, royaltyBps) - ) - ) - ); - - assertEq(Permissions(evolvingNFT).hasRole(0x00, deployer), true); - - sharedMetadataBatch[0] = ISharedMetadataBatch.SharedMetadataInfo({ - name: "Default", - description: "Default metadata", - imageURI: "https://default.com/1", - animationURI: "https://default.com/1" - }); - - sharedMetadataBatch[score1] = ISharedMetadataBatch.SharedMetadataInfo({ - name: "Test 1", - description: "Test 1", - imageURI: "https://test.com/1", - animationURI: "https://test.com/1" - }); - - sharedMetadataBatch[score1 + score2] = ISharedMetadataBatch.SharedMetadataInfo({ - name: "Test 2", - description: "Test 2", - imageURI: "https://test.com/2", - animationURI: "https://test.com/2" - }); - - sharedMetadataBatch[score1 + score2 + score3] = ISharedMetadataBatch.SharedMetadataInfo({ - name: "Test 3", - description: "Test 3", - imageURI: "https://test.com/3", - animationURI: "https://test.com/3" - }); - - vm.deal(deployer, 1_000 ether); - } - - /*/////////////////////////////////////////////////////////////// - Rules test - //////////////////////////////////////////////////////////////*/ - - function test_state_evolvingNFT() public { - /** - * Set shared metadata for the following scores: - * - * default: `0` - * NFT owner owns no relevant tokens. - * score_1: `10` - * NFT owner owns 10 `MockERC20` tokens. - * score_1 + score_2: `50` - * NFT owner additionally owns 1 `MockERC721` NFT. - * score_1 + score_2 + score_3: `150` - * NFT owner addtionally owns 5 `MockERC1155` NFTs of tokenID 3. - */ - - // Set shared metadata - vm.startPrank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[score1], bytes32(score1)); - SharedMetadataBatch(evolvingNFT).setSharedMetadata( - sharedMetadataBatch[score1 + score2], - bytes32(score1 + score2) - ); - SharedMetadataBatch(evolvingNFT).setSharedMetadata( - sharedMetadataBatch[score1 + score2 + score3], - bytes32(score1 + score2 + score3) - ); - vm.stopPrank(); - - // Set rules - vm.prank(deployer); - RulesEngine(evolvingNFT).createRuleThreshold( - IRulesEngine.RuleTypeThreshold({ - token: address(erc20), - tokenType: IRulesEngine.TokenType.ERC20, - tokenId: 0, - balance: 10, - score: score1 - }) - ); - vm.prank(deployer); - RulesEngine(evolvingNFT).createRuleThreshold( - IRulesEngine.RuleTypeThreshold({ - token: address(erc721), - tokenType: IRulesEngine.TokenType.ERC721, - tokenId: 0, - balance: 1, - score: score2 - }) - ); - vm.prank(deployer); - RulesEngine(evolvingNFT).createRuleThreshold( - IRulesEngine.RuleTypeThreshold({ - token: address(erc1155), - tokenType: IRulesEngine.TokenType.ERC1155, - tokenId: 3, - balance: 5, - score: score3 - }) - ); - - // `Receiver` mints token - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 0; - conditions[0].currency = address(erc20); - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - vm.prank(receiver, receiver); - IDrop(evolvingNFT).claim(receiver, 1, address(erc20), 0, alp, ""); // claims for free, because allowlist price is 0 - - // NFT should return default metadata. - string memory uri0 = EvolvingNFTLogic(evolvingNFT).tokenURI(1); - assertEq( - uri0, - NFTMetadataRenderer.createMetadataEdition({ - name: sharedMetadataBatch[0].name, - description: sharedMetadataBatch[0].description, - imageURI: sharedMetadataBatch[0].imageURI, - animationURI: sharedMetadataBatch[0].animationURI, - tokenOfEdition: 1 - }) - ); - - // NFT should return 1st tier of metadata. - vm.prank(deployer); - erc20.mint(receiver, 10 ether); - assertEq(RulesEngine(evolvingNFT).getScore(receiver), uint256(bytes32(score1))); - - string memory uri1 = EvolvingNFTLogic(evolvingNFT).tokenURI(1); - assertEq( - uri1, - NFTMetadataRenderer.createMetadataEdition({ - name: sharedMetadataBatch[score1].name, - description: sharedMetadataBatch[score1].description, - imageURI: sharedMetadataBatch[score1].imageURI, - animationURI: sharedMetadataBatch[score1].animationURI, - tokenOfEdition: 1 - }) - ); - - // NFT should return 2nd tier of metadata. - vm.prank(deployer); - erc721.mint(receiver, 1); - assertEq(RulesEngine(evolvingNFT).getScore(receiver), uint256(bytes32(score1 + score2))); - - string memory uri2 = EvolvingNFTLogic(evolvingNFT).tokenURI(1); - assertEq( - uri2, - NFTMetadataRenderer.createMetadataEdition({ - name: sharedMetadataBatch[score1 + score2].name, - description: sharedMetadataBatch[score1 + score2].description, - imageURI: sharedMetadataBatch[score1 + score2].imageURI, - animationURI: sharedMetadataBatch[score1 + score2].animationURI, - tokenOfEdition: 1 - }) - ); - - // NFT should return 3rd tier of metadata. - vm.prank(deployer); - erc1155.mint(receiver, 3, 5, ""); - assertEq(RulesEngine(evolvingNFT).getScore(receiver), uint256(bytes32(score1 + score2 + score3))); - - string memory uri3 = EvolvingNFTLogic(evolvingNFT).tokenURI(1); - assertEq( - uri3, - NFTMetadataRenderer.createMetadataEdition({ - name: sharedMetadataBatch[score1 + score2 + score3].name, - description: sharedMetadataBatch[score1 + score2 + score3].description, - imageURI: sharedMetadataBatch[score1 + score2 + score3].imageURI, - animationURI: sharedMetadataBatch[score1 + score2 + score3].animationURI, - tokenOfEdition: 1 - }) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc. - //////////////////////////////////////////////////////////////*/ - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_revert_nonHolder_renounceRole() public { - address caller = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(caller); - vm.expectRevert( - abi.encodePacked( - "Permissions: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - - Permissions(evolvingNFT).renounceRole(role, caller); - } - - /** - * note: Tests whether contract reverts when a role admin revokes a role for a non-holder. - */ - function test_revert_revokeRoleForNonHolder() public { - address target = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(deployer); - vm.expectRevert( - abi.encodePacked( - "Permissions: account ", - Strings.toHexString(uint160(target), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - - Permissions(evolvingNFT).revokeRole(role, target); - } - - /** - * @dev Tests whether contract reverts when a role is granted to an existent role holder. - */ - function test_revert_grant_role_to_account_with_role() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - - Permissions(evolvingNFT).grantRole(role, receiver); - - vm.expectRevert("Can only grant to non holders"); - Permissions(evolvingNFT).grantRole(role, receiver); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_grant_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - // check if admin and address(0) have transfer role in the beginning - bool checkAddressZero = Permissions(evolvingNFT).hasRole(role, address(0)); - bool checkAdmin = Permissions(evolvingNFT).hasRole(role, deployer); - assertTrue(checkAddressZero); - assertTrue(checkAdmin); - - // check if transfer role can be granted to a non-holder - address receiver = getActor(0); - vm.startPrank(deployer); - Permissions(evolvingNFT).grantRole(role, receiver); - - // expect revert when granting to a holder - vm.expectRevert("Can only grant to non holders"); - Permissions(evolvingNFT).grantRole(role, receiver); - - // check if receiver has transfer role - bool checkReceiver = Permissions(evolvingNFT).hasRole(role, receiver); - assertTrue(checkReceiver); - - // check if role is correctly revoked - Permissions(evolvingNFT).revokeRole(role, receiver); - checkReceiver = Permissions(evolvingNFT).hasRole(role, receiver); - assertFalse(checkReceiver); - Permissions(evolvingNFT).revokeRole(role, address(0)); - checkAddressZero = Permissions(evolvingNFT).hasRole(role, address(0)); - assertFalse(checkAddressZero); - - vm.stopPrank(); - } - - /** - * note: Testing transfer of tokens when transfer-role is restricted - */ - function test_claim_transferRole() public { - assertEq(Permissions(evolvingNFT).hasRole(0x00, deployer), true); - - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - IDrop(evolvingNFT).claim(receiver, 1, address(0), 0, alp, ""); - - // revoke transfer role from address(0) - vm.prank(deployer); - Permissions(evolvingNFT).revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.prank(receiver); - vm.expectRevert(bytes("!T")); - IERC721(evolvingNFT).transferFrom(receiver, address(123), 1); - } - - function test_claimCondition_with_startTimestamp() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].startTimestamp = 100; - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - vm.warp(99); - vm.prank(getActor(5), getActor(5)); - vm.expectRevert("!CONDITION."); - IDrop(evolvingNFT).claim(receiver, 1, address(0), 0, alp, ""); - - vm.warp(100); - vm.prank(getActor(4), getActor(4)); - IDrop(evolvingNFT).claim(receiver, 1, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - Set Shared Metadata Tests - //////////////////////////////////////////////////////////////*/ - - /* - * note: Testing state changes; set shared metadata for tokens. - */ - function test_state_sharedMetadata() public { - // SET METADATA - vm.prank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[score1], bytes32(0)); - - // CLAIM 1 TOKEN - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - IDrop(evolvingNFT).claim(receiver, 100, address(erc20), 0, alp, ""); - - string memory uri = EvolvingNFTLogic(evolvingNFT).tokenURI(1); - assertEq( - uri, - NFTMetadataRenderer.createMetadataEdition({ - name: sharedMetadataBatch[score1].name, - description: sharedMetadataBatch[score1].description, - imageURI: sharedMetadataBatch[score1].imageURI, - animationURI: sharedMetadataBatch[score1].animationURI, - tokenOfEdition: 1 - }) - ); - } - - /** - * note: Testing revert condition; an address without MINTER_ROLE calls setSharedMetadata function. - */ - function test_revert_setSharedMetadata_MINTER_ROLE() public { - vm.expectRevert(); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - } - - /** - * note: Testing event emission; shared metadata set. - */ - function test_event_setSharedMetadata_SharedMetadataUpdated() public { - vm.startPrank(deployer); - - vm.expectEmit(false, false, false, false); - emit SharedMetadataUpdated( - bytes32(0), - sharedMetadataBatch[score1].name, - sharedMetadataBatch[score1].description, - sharedMetadataBatch[score1].imageURI, - sharedMetadataBatch[score1].animationURI - ); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[score1], bytes32(0)); - - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Claim Tests - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing revert condition; exceed max claimable supply. - */ - function test_revert_claimCondition_exceedMaxClaimableSupply() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 200; - - vm.prank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - IDrop(evolvingNFT).claim(receiver, 100, address(0), 0, alp, ""); - - vm.expectRevert("!MaxSupply"); - vm.prank(getActor(6), getActor(6)); - IDrop(evolvingNFT).claim(receiver, 1, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction when no allowlist present. - */ - function test_fuzz_claim_noAllowlist(uint256 x) public { - vm.assume(x != 0); - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - bytes memory errorQty = "!Qty"; - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(errorQty); - IDrop(evolvingNFT).claim(receiver, 0, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(errorQty); - IDrop(evolvingNFT).claim(receiver, 101, address(0), 0, alp, ""); - - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(errorQty); - IDrop(evolvingNFT).claim(receiver, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to 0 - */ - function test_state_claim_allowlisted_SetQuantityZeroPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - IDrop(evolvingNFT).claim(receiver, 100, address(erc20), 0, alp, ""); // claims for free, because allowlist price is 0 - assertEq( - Drop(evolvingNFT).getSupplyClaimedByWallet(Drop(evolvingNFT).getActiveClaimConditionId(), receiver), - 100 - ); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to non-zero value - */ - function test_state_claim_allowlisted_SetQuantityPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "5"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 5; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - vm.prank(receiver, receiver); - vm.expectRevert("!PriceOrCurrency"); - IDrop(evolvingNFT).claim(receiver, 100, address(erc20), 0, alp, ""); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(evolvingNFT, 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - IDrop(evolvingNFT).claim(receiver, 100, address(erc20), 5, alp, ""); - assertEq( - Drop(evolvingNFT).getSupplyClaimedByWallet(Drop(evolvingNFT).getActiveClaimConditionId(), receiver), - 100 - ); - assertEq(erc20.balanceOf(receiver), 10000 - 500); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price not set; should default to general price and currency - */ - function test_state_claim_allowlisted_SetQuantityDefaultPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = Strings.toString(type(uint256).max); // this implies that general price is applicable - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = type(uint256).max; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(evolvingNFT, 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - IDrop(evolvingNFT).claim(receiver, 100, address(erc20), 10, alp, ""); - assertEq( - Drop(evolvingNFT).getSupplyClaimedByWallet(Drop(evolvingNFT).getActiveClaimConditionId(), receiver), - 100 - ); - assertEq(erc20.balanceOf(receiver), 10000 - 1000); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to 0 => should default to general limit - * - allowlist price set to some value different than general price - */ - function test_state_claim_allowlisted_DefaultQuantitySomePrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "0"; // this implies that general limit is applicable - inputs[3] = "5"; - inputs[4] = "0x0000000000000000000000000000000000000000"; // general currency will be applicable - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 0; - alp.pricePerToken = 5; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(evolvingNFT, 10000); - - bytes memory errorQty = "!Qty"; - vm.prank(receiver, receiver); - vm.expectRevert(errorQty); - IDrop(evolvingNFT).claim(receiver, 100, address(erc20), 5, alp, ""); // trying to claim more than general limit - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - IDrop(evolvingNFT).claim(receiver, 10, address(erc20), 5, alp, ""); - assertEq( - Drop(evolvingNFT).getSupplyClaimedByWallet(Drop(evolvingNFT).getActiveClaimConditionId(), receiver), - 10 - ); - assertEq(erc20.balanceOf(receiver), 10000 - 50); - } - - function test_fuzz_claim_merkleProof(uint256 x) public { - vm.assume(x > 10 && x < 500); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(x); - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - alp.pricePerToken = 0; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - // bytes32[] memory proofs = new bytes32[](0); - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = x; - conditions[0].quantityLimitPerWallet = 1; - conditions[0].merkleRoot = root; - - vm.prank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - IDrop(evolvingNFT).claim(receiver, x - 5, address(0), 0, alp, ""); - assertEq( - Drop(evolvingNFT).getSupplyClaimedByWallet(Drop(evolvingNFT).getActiveClaimConditionId(), receiver), - x - 5 - ); - - bytes memory errorQty = "!Qty"; - - vm.prank(receiver, receiver); - vm.expectRevert(errorQty); - IDrop(evolvingNFT).claim(receiver, 6, address(0), 0, alp, ""); - - vm.prank(receiver, receiver); - IDrop(evolvingNFT).claim(receiver, 5, address(0), 0, alp, ""); - assertEq( - Drop(evolvingNFT).getSupplyClaimedByWallet(Drop(evolvingNFT).getActiveClaimConditionId(), receiver), - x - ); - - vm.prank(receiver, receiver); - vm.expectRevert(errorQty); - IDrop(evolvingNFT).claim(receiver, 5, address(0), 0, alp, ""); // quantity limit already claimed - } - - /** - * note: Testing state changes; reset eligibility of claim conditions and claiming again for same condition id. - */ - function test_state_claimCondition_resetEligibility() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - IDrop(evolvingNFT).claim(receiver, 100, address(0), 0, alp, ""); - - bytes memory errorQty = "!Qty"; - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(errorQty); - IDrop(evolvingNFT).claim(receiver, 100, address(0), 0, alp, ""); - - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - IDrop(evolvingNFT).claim(receiver, 100, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - setClaimConditions - //////////////////////////////////////////////////////////////*/ - - function test_claimCondition_startIdAndCount() public { - vm.startPrank(deployer); - - uint256 currentStartId = 0; - uint256 count = 0; - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](2); - conditions[0].startTimestamp = 0; - conditions[0].maxClaimableSupply = 10; - conditions[1].startTimestamp = 1; - conditions[1].maxClaimableSupply = 10; - - IDrop(evolvingNFT).setClaimConditions(conditions, false); - (currentStartId, count) = Drop(evolvingNFT).claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - IDrop(evolvingNFT).setClaimConditions(conditions, false); - (currentStartId, count) = Drop(evolvingNFT).claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - IDrop(evolvingNFT).setClaimConditions(conditions, true); - (currentStartId, count) = Drop(evolvingNFT).claimCondition(); - assertEq(currentStartId, 2); - assertEq(count, 2); - - IDrop(evolvingNFT).setClaimConditions(conditions, true); - (currentStartId, count) = Drop(evolvingNFT).claimCondition(); - assertEq(currentStartId, 4); - assertEq(count, 2); - } - - function test_claimCondition_startPhase() public { - vm.startPrank(deployer); - - uint256 activeConditionId = 0; - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](3); - conditions[0].startTimestamp = 10; - conditions[0].maxClaimableSupply = 11; - conditions[0].quantityLimitPerWallet = 12; - conditions[1].startTimestamp = 20; - conditions[1].maxClaimableSupply = 21; - conditions[1].quantityLimitPerWallet = 22; - conditions[2].startTimestamp = 30; - conditions[2].maxClaimableSupply = 31; - conditions[2].quantityLimitPerWallet = 32; - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - vm.expectRevert("!CONDITION."); - Drop(evolvingNFT).getActiveClaimConditionId(); - - vm.warp(10); - activeConditionId = Drop(evolvingNFT).getActiveClaimConditionId(); - assertEq(activeConditionId, 0); - assertEq(Drop(evolvingNFT).getClaimConditionById(activeConditionId).startTimestamp, 10); - assertEq(Drop(evolvingNFT).getClaimConditionById(activeConditionId).maxClaimableSupply, 11); - assertEq(Drop(evolvingNFT).getClaimConditionById(activeConditionId).quantityLimitPerWallet, 12); - - vm.warp(20); - activeConditionId = Drop(evolvingNFT).getActiveClaimConditionId(); - assertEq(activeConditionId, 1); - assertEq(Drop(evolvingNFT).getClaimConditionById(activeConditionId).startTimestamp, 20); - assertEq(Drop(evolvingNFT).getClaimConditionById(activeConditionId).maxClaimableSupply, 21); - assertEq(Drop(evolvingNFT).getClaimConditionById(activeConditionId).quantityLimitPerWallet, 22); - - vm.warp(30); - activeConditionId = Drop(evolvingNFT).getActiveClaimConditionId(); - assertEq(activeConditionId, 2); - assertEq(Drop(evolvingNFT).getClaimConditionById(activeConditionId).startTimestamp, 30); - assertEq(Drop(evolvingNFT).getClaimConditionById(activeConditionId).maxClaimableSupply, 31); - assertEq(Drop(evolvingNFT).getClaimConditionById(activeConditionId).quantityLimitPerWallet, 32); - - vm.warp(40); - assertEq(Drop(evolvingNFT).getActiveClaimConditionId(), 2); - } - - /*/////////////////////////////////////////////////////////////// - Audit POC tests - //////////////////////////////////////////////////////////////*/ - - function test_state_incorrectTokenUri() public { - // Set shared metadata - vm.startPrank(deployer); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[0], bytes32(0)); - SharedMetadataBatch(evolvingNFT).setSharedMetadata(sharedMetadataBatch[score1], bytes32(score1)); - SharedMetadataBatch(evolvingNFT).setSharedMetadata( - sharedMetadataBatch[score1 + score2], - bytes32(score1 + score2) - ); - SharedMetadataBatch(evolvingNFT).setSharedMetadata( - sharedMetadataBatch[score1 + score2 + score3], - bytes32(score1 + score2 + score3) - ); - - // Delete metadata at index "score1" - // Now the order of metadata ids is: 0, 150, 50 - SharedMetadataBatch(evolvingNFT).deleteSharedMetadata(bytes32(score1)); - vm.stopPrank(); - - // Set rules - vm.prank(deployer); - RulesEngine(evolvingNFT).createRuleThreshold( - IRulesEngine.RuleTypeThreshold({ - token: address(erc20), - tokenType: IRulesEngine.TokenType.ERC20, - tokenId: 0, - balance: 10, - score: score1 + score2 + score3 - }) - ); - - // `Receiver` mints token - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 0; - conditions[0].currency = address(erc20); - vm.prank(deployer); - IDrop(evolvingNFT).setClaimConditions(conditions, false); - - vm.prank(receiver, receiver); - IDrop(evolvingNFT).claim(receiver, 1, address(erc20), 0, alp, ""); // claims for free, because allowlist price is 0 - - // NFT should return metadata of a rule at "score1 + score2 + score3" - // It used to return metadata for "score1 + score2", but now this is fixed. - erc20.mint(receiver, 10 ether); - string memory uri = EvolvingNFTLogic(evolvingNFT).tokenURI(1); - assertEq( - uri, - NFTMetadataRenderer.createMetadataEdition({ - name: sharedMetadataBatch[score1 + score2 + score3].name, - description: sharedMetadataBatch[score1 + score2 + score3].description, - imageURI: sharedMetadataBatch[score1 + score2 + score3].imageURI, - animationURI: sharedMetadataBatch[score1 + score2 + score3].animationURI, - tokenOfEdition: 1 - }) - ); - } -} diff --git a/src/test/Forwarder.t.sol b/src/test/Forwarder.t.sol deleted file mode 100644 index 953c5e756..000000000 --- a/src/test/Forwarder.t.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { Forwarder } from "contracts/infra/forwarder/Forwarder.sol"; -import { ForwarderConsumer } from "contracts/infra/forwarder/ForwarderConsumer.sol"; - -import "./utils/BaseTest.sol"; - -contract ForwarderTest is BaseTest { - ForwarderConsumer public consumer; - - uint256 public userPKey = 1020; - address public user; - address public relayer = address(0x4567); - - bytes32 internal typehashForwardRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - user = vm.addr(userPKey); - consumer = new ForwarderConsumer(forwarders()); - - typehashForwardRequest = keccak256( - "ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data)" - ); - nameHash = keccak256(bytes("GSNv2 Forwarder")); - versionHash = keccak256(bytes("0.0.1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, forwarder)); - - vm.label(user, "End user"); - vm.label(forwarder, "Forwarder"); - vm.label(relayer, "Relayer"); - vm.label(address(consumer), "Consumer"); - } - - /*/////////////////////////////////////////////////////////////// - Regular `Forwarder`: chainId in typehash - //////////////////////////////////////////////////////////////*/ - - function signForwarderRequest( - Forwarder.ForwardRequest memory forwardRequest, - uint256 privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashForwardRequest, - forwardRequest.from, - forwardRequest.to, - forwardRequest.value, - forwardRequest.gas, - forwardRequest.nonce, - keccak256(forwardRequest.data) - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); - bytes memory signature = abi.encodePacked(r, s, v); - - return signature; - } - - function test_state_forwarder() public { - Forwarder.ForwardRequest memory forwardRequest; - - forwardRequest.from = user; - forwardRequest.to = address(consumer); - forwardRequest.value = 0; - forwardRequest.gas = 100_000; - forwardRequest.nonce = Forwarder(forwarder).getNonce(user); - forwardRequest.data = abi.encodeCall(ForwarderConsumer.setCaller, ()); - - bytes memory signature = signForwarderRequest(forwardRequest, userPKey); - vm.prank(relayer); - Forwarder(forwarder).execute(forwardRequest, signature); - - assertEq(consumer.caller(), user); - } -} diff --git a/src/test/ForwarderChainlessDomain.t.sol b/src/test/ForwarderChainlessDomain.t.sol deleted file mode 100644 index bf4561aba..000000000 --- a/src/test/ForwarderChainlessDomain.t.sol +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { ForwarderConsumer } from "contracts/infra/forwarder/ForwarderConsumer.sol"; -import { ForwarderChainlessDomain } from "contracts/infra/forwarder/ForwarderChainlessDomain.sol"; - -import "./utils/BaseTest.sol"; - -contract ForwarderChainlessDomainTest is BaseTest { - address[] public forwarderChainlessDomain; - ForwarderConsumer public consumer; - - uint256 public userPKey = 1020; - address public user; - address public relayer = address(0x4567); - - bytes32 internal typehashForwardRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - user = vm.addr(userPKey); - consumer = new ForwarderConsumer(forwarders()); - - forwarderChainlessDomain.push(address(new ForwarderChainlessDomain())); - consumer = new ForwarderConsumer(forwarderChainlessDomain); - - typehashForwardRequest = keccak256( - "ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data,uint256 chainid)" - ); - nameHash = keccak256(bytes("GSNv2 Forwarder")); - versionHash = keccak256(bytes("0.0.1")); - typehashEip712 = keccak256("EIP712Domain(string name,string version,address verifyingContract)"); - domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, forwarderChainlessDomain[0])); - - vm.label(user, "End user"); - vm.label(forwarder, "Forwarder"); - vm.label(relayer, "Relayer"); - vm.label(address(consumer), "Consumer"); - } - - /*/////////////////////////////////////////////////////////////// - Updated `Forwarder`: chainId in ForwardRequest, not typehash. - //////////////////////////////////////////////////////////////*/ - - function signForwarderRequest( - ForwarderChainlessDomain.ForwardRequest memory forwardRequest, - uint256 privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashForwardRequest, - forwardRequest.from, - forwardRequest.to, - forwardRequest.value, - forwardRequest.gas, - forwardRequest.nonce, - keccak256(forwardRequest.data), - forwardRequest.chainid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); - bytes memory signature = abi.encodePacked(r, s, v); - - return signature; - } - - function test_state_forwarderChainlessDomain() public { - ForwarderChainlessDomain.ForwardRequest memory forwardRequest; - - forwardRequest.from = user; - forwardRequest.to = address(consumer); - forwardRequest.value = 0; - forwardRequest.gas = 100_000; - forwardRequest.nonce = ForwarderChainlessDomain(forwarderChainlessDomain[0]).getNonce(user); - forwardRequest.data = abi.encodeCall(ForwarderConsumer.setCaller, ()); - forwardRequest.chainid = block.chainid; - - bytes memory signature = signForwarderRequest(forwardRequest, userPKey); - vm.prank(relayer); - ForwarderChainlessDomain(forwarderChainlessDomain[0]).execute(forwardRequest, signature); - - assertEq(consumer.caller(), user); - } -} diff --git a/src/test/LoyaltyCard.t.sol b/src/test/LoyaltyCard.t.sol deleted file mode 100644 index f9e26498b..000000000 --- a/src/test/LoyaltyCard.t.sol +++ /dev/null @@ -1,356 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./utils/BaseTest.sol"; -import "contracts/infra/TWProxy.sol"; -import { Strings } from "contracts/lib/Strings.sol"; -import { LoyaltyCard, NFTMetadata } from "contracts/prebuilts/loyalty/LoyaltyCard.sol"; - -contract LoyaltyCardTest is BaseTest { - LoyaltyCard internal loyaltyCard; - using Strings for uint256; - - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - LoyaltyCard.MintRequest _mintrequest; - bytes _signature; - - address recipient; - - function setUp() public override { - super.setUp(); - - address loyaltyCardImpl = address(new LoyaltyCard()); - - vm.prank(signer); - loyaltyCard = LoyaltyCard( - address( - new TWProxy( - loyaltyCardImpl, - abi.encodeCall( - LoyaltyCard.initialize, - ( - signer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ); - - recipient = address(0x123); - erc20.mint(recipient, 1_000); - vm.deal(recipient, 1_000); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,string uri,uint256 quantity,uint256 pricePerToken,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("SignatureMintERC721")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(loyaltyCard)) - ); - - _mintrequest.to = recipient; - _mintrequest.royaltyRecipient = royaltyRecipient; - _mintrequest.royaltyBps = royaltyBps; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.uri = "ipfs://"; - _mintrequest.quantity = 1; - _mintrequest.pricePerToken = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - LoyaltyCard.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - keccak256(bytes(_request.uri)), - _request.quantity, - _request.pricePerToken, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintTo` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintTo() public { - string memory _tokenURI = "tokenURI"; - - uint256 nextTokenId = loyaltyCard.nextTokenIdToMint(); - uint256 currentTotalSupply = loyaltyCard.totalSupply(); - uint256 currentBalanceOfRecipient = loyaltyCard.balanceOf(recipient); - - vm.prank(signer); - loyaltyCard.mintTo(recipient, _tokenURI); - - assertEq(loyaltyCard.nextTokenIdToMint(), nextTokenId + 1); - assertEq(loyaltyCard.tokenURI(nextTokenId), _tokenURI); - assertEq(loyaltyCard.totalSupply(), currentTotalSupply + 1); - assertEq(loyaltyCard.balanceOf(recipient), currentBalanceOfRecipient + 1); - assertEq(loyaltyCard.ownerOf(nextTokenId), recipient); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `setTokenURI` - //////////////////////////////////////////////////////////////*/ - - function test_state_setTokenURI() public { - string memory _tokenURI = "tokenURI"; - - vm.prank(signer); - uint256 tokenIdMinted = loyaltyCard.mintTo(recipient, _tokenURI); - - assertEq(_tokenURI, loyaltyCard.tokenURI(tokenIdMinted)); - - assertEq(loyaltyCard.hasRole(keccak256("METADATA_ROLE"), signer), true); - - string memory newURI = "newURI"; - - vm.prank(signer); - loyaltyCard.setTokenURI(tokenIdMinted, newURI); - - assertEq(newURI, loyaltyCard.tokenURI(tokenIdMinted)); - - vm.prank(signer); - loyaltyCard.renounceRole(keccak256("METADATA_ROLE"), signer); - - vm.expectRevert(); - vm.prank(signer); - loyaltyCard.setTokenURI(tokenIdMinted, _tokenURI); - - vm.expectRevert(); - vm.prank(signer); - loyaltyCard.grantRole(keccak256("METADATA_ROLE"), signer); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: cancel / revoke loyalty - //////////////////////////////////////////////////////////////*/ - - function test_state_cancelLoyalty() public { - string memory _tokenURI = "tokenURI"; - - vm.prank(signer); - uint256 tokenIdMinted = loyaltyCard.mintTo(recipient, _tokenURI); - - assertEq(loyaltyCard.ownerOf(tokenIdMinted), recipient); - - vm.prank(recipient); - loyaltyCard.setApprovalForAll(signer, true); - - vm.prank(signer); - loyaltyCard.cancel(tokenIdMinted); - - vm.expectRevert(); - loyaltyCard.ownerOf(tokenIdMinted); - } - - function test_state_revokeLoyalty() public { - string memory _tokenURI = "tokenURI"; - - vm.prank(signer); - uint256 tokenIdMinted = loyaltyCard.mintTo(recipient, _tokenURI); - - assertEq(loyaltyCard.ownerOf(tokenIdMinted), recipient); - - address burner = address(0x123456); - vm.prank(signer); - loyaltyCard.grantRole(keccak256("REVOKE_ROLE"), burner); - - vm.prank(signer); - loyaltyCard.renounceRole(keccak256("REVOKE_ROLE"), signer); - - vm.expectRevert(); - vm.prank(signer); - loyaltyCard.revoke(tokenIdMinted); - - vm.prank(burner); - loyaltyCard.revoke(tokenIdMinted); - - vm.expectRevert(); - loyaltyCard.ownerOf(tokenIdMinted); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintWithSignature` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintWithSignature_ZeroPrice() public { - vm.warp(1000); - - uint256 nextTokenId = loyaltyCard.nextTokenIdToMint(); - uint256 currentTotalSupply = loyaltyCard.totalSupply(); - uint256 currentBalanceOfRecipient = loyaltyCard.balanceOf(recipient); - - loyaltyCard.mintWithSignature(_mintrequest, _signature); - - assertEq(loyaltyCard.nextTokenIdToMint(), nextTokenId + _mintrequest.quantity); - assertEq(loyaltyCard.tokenURI(nextTokenId), string(abi.encodePacked(_mintrequest.uri))); - assertEq(loyaltyCard.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(loyaltyCard.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - assertEq(loyaltyCard.ownerOf(nextTokenId), recipient); - } - - function test_state_mintWithSignature_NonZeroPrice_ERC20() public { - vm.warp(1000); - - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - erc20.approve(address(loyaltyCard), 1); - - uint256 nextTokenId = loyaltyCard.nextTokenIdToMint(); - uint256 currentTotalSupply = loyaltyCard.totalSupply(); - uint256 currentBalanceOfRecipient = loyaltyCard.balanceOf(recipient); - - vm.prank(recipient); - loyaltyCard.mintWithSignature(_mintrequest, _signature); - - assertEq(loyaltyCard.nextTokenIdToMint(), nextTokenId + _mintrequest.quantity); - assertEq(loyaltyCard.tokenURI(nextTokenId), string(abi.encodePacked(_mintrequest.uri))); - assertEq(loyaltyCard.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(loyaltyCard.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - assertEq(loyaltyCard.ownerOf(nextTokenId), recipient); - } - - function test_state_mintWithSignature_NonZeroPrice_NativeToken() public { - vm.warp(1000); - - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - uint256 nextTokenId = loyaltyCard.nextTokenIdToMint(); - uint256 currentTotalSupply = loyaltyCard.totalSupply(); - uint256 currentBalanceOfRecipient = loyaltyCard.balanceOf(recipient); - - vm.deal(recipient, 1); - - vm.prank(recipient); - loyaltyCard.mintWithSignature{ value: 1 }(_mintrequest, _signature); - - assertEq(loyaltyCard.nextTokenIdToMint(), nextTokenId + _mintrequest.quantity); - assertEq(loyaltyCard.tokenURI(nextTokenId), string(abi.encodePacked(_mintrequest.uri))); - assertEq(loyaltyCard.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(loyaltyCard.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - assertEq(loyaltyCard.ownerOf(nextTokenId), recipient); - } - - function test_revert_mintWithSignature_InvalidMsgValue() public { - vm.warp(1000); - - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("Invalid msg value"); - loyaltyCard.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_ZeroQty() public { - vm.warp(1000); - - _mintrequest.quantity = 0; - _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("LoyaltyCard: only 1 NFT can be minted at a time."); - loyaltyCard.mintWithSignature(_mintrequest, _signature); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `setTokenURI` - //////////////////////////////////////////////////////////////*/ - - function test_setTokenURI_state() public { - string memory uri = "uri_string"; - - vm.prank(signer); - loyaltyCard.setTokenURI(0, uri); - - string memory _tokenURI = loyaltyCard.tokenURI(0); - - assertEq(_tokenURI, uri); - } - - function test_setTokenURI_revert_NotAuthorized() public { - string memory uri = "uri_string"; - - vm.expectRevert(abi.encodeWithSelector(NFTMetadata.NFTMetadataUnauthorized.selector)); - vm.prank(address(0x1)); - loyaltyCard.setTokenURI(0, uri); - } - - function test_setTokenURI_revert_Frozen() public { - string memory uri = "uri_string"; - - vm.startPrank(signer); - loyaltyCard.freezeMetadata(); - - vm.expectRevert(abi.encodeWithSelector(NFTMetadata.NFTMetadataFrozen.selector, 0)); - loyaltyCard.setTokenURI(0, uri); - } - - /*/////////////////////////////////////////////////////////////// - Audit fixes tests - //////////////////////////////////////////////////////////////*/ - - function test_audit_quantity_not_1() public { - vm.warp(1000); - _mintrequest.pricePerToken = 1; - _mintrequest.quantity = 5; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - erc20.approve(address(loyaltyCard), 5); - - vm.prank(recipient); - vm.expectRevert("LoyaltyCard: only 1 NFT can be minted at a time."); - loyaltyCard.mintWithSignature(_mintrequest, _signature); - } -} diff --git a/src/test/LoyaltyPoints.t.sol b/src/test/LoyaltyPoints.t.sol deleted file mode 100644 index c557091bf..000000000 --- a/src/test/LoyaltyPoints.t.sol +++ /dev/null @@ -1,265 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./utils/BaseTest.sol"; -import "contracts/infra/TWProxy.sol"; -import { Strings } from "contracts/lib/Strings.sol"; -import { LoyaltyPoints } from "contracts/prebuilts/unaudited/loyalty/LoyaltyPoints.sol"; - -contract LoyaltyPointsTest is BaseTest { - LoyaltyPoints internal loyaltyPoints; - using Strings for uint256; - - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - LoyaltyPoints.MintRequest _mintrequest; - bytes _signature; - - address recipient; - - function setUp() public override { - super.setUp(); - - address loyaltyPointsImpl = address(new LoyaltyPoints()); - - vm.prank(signer); - loyaltyPoints = LoyaltyPoints( - address( - new TWProxy( - loyaltyPointsImpl, - abi.encodeCall( - LoyaltyPoints.initialize, - ( - signer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ); - - recipient = address(0x123); - erc20.mint(recipient, 1_000); - vm.deal(recipient, 1_000); - - typehashMintRequest = keccak256( - "MintRequest(address to,address primarySaleRecipient,uint256 quantity,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes(NAME)); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(loyaltyPoints)) - ); - - _mintrequest.to = recipient; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.quantity = 1 ether; - _mintrequest.price = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - LoyaltyPoints.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.primarySaleRecipient, - _request.quantity, - _request.price, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintTo` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintTo() public { - uint256 amount = 1 ether; - - uint256 currentTotalSupply = loyaltyPoints.totalSupply(); - uint256 currentBalanceOfRecipient = loyaltyPoints.balanceOf(recipient); - - vm.prank(signer); - loyaltyPoints.mintTo(recipient, amount); - - assertEq(loyaltyPoints.totalSupply(), currentTotalSupply + amount); - assertEq(loyaltyPoints.balanceOf(recipient), currentBalanceOfRecipient + amount); - - assertEq(loyaltyPoints.getTotalMintedInLifetime(recipient), amount); - - vm.prank(signer); - loyaltyPoints.mintTo(recipient, amount); - assertEq(loyaltyPoints.getTotalMintedInLifetime(recipient), amount * 2); - - vm.prank(recipient); - loyaltyPoints.cancel(recipient, amount); - assertEq(loyaltyPoints.getTotalMintedInLifetime(recipient), amount * 2); - - vm.prank(signer); - loyaltyPoints.revoke(recipient, amount); - assertEq(loyaltyPoints.getTotalMintedInLifetime(recipient), amount * 2); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: cancel / revoke loyalty - //////////////////////////////////////////////////////////////*/ - - function test_state_cancelLoyalty() public { - uint256 amount = 10 ether; - - vm.prank(signer); - loyaltyPoints.mintTo(recipient, amount); - - assertEq(loyaltyPoints.balanceOf(recipient), amount); - - uint256 amountToCancel = 1 ether; - - vm.prank(recipient); - loyaltyPoints.approve(signer, amountToCancel); - - vm.prank(signer); - loyaltyPoints.cancel(recipient, amountToCancel); - assertEq(loyaltyPoints.balanceOf(recipient), amount - amountToCancel); - } - - function test_state_revokeLoyalty() public { - uint256 amount = 10 ether; - - vm.prank(signer); - loyaltyPoints.mintTo(recipient, amount); - - assertEq(loyaltyPoints.balanceOf(recipient), amount); - - address burner = address(0x123456); - vm.prank(signer); - loyaltyPoints.grantRole(keccak256("REVOKE_ROLE"), burner); - - vm.prank(signer); - loyaltyPoints.renounceRole(keccak256("REVOKE_ROLE"), signer); - - uint256 amountToRevoke = 1 ether; - - vm.expectRevert(); - vm.prank(signer); - loyaltyPoints.revoke(recipient, amountToRevoke); - - vm.prank(burner); - loyaltyPoints.revoke(recipient, amountToRevoke); - assertEq(loyaltyPoints.balanceOf(recipient), amount - amountToRevoke); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintWithSignature` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintWithSignature_ZeroPrice() public { - vm.warp(1000); - - uint256 currentTotalSupply = loyaltyPoints.totalSupply(); - uint256 currentBalanceOfRecipient = loyaltyPoints.balanceOf(recipient); - - loyaltyPoints.mintWithSignature(_mintrequest, _signature); - - assertEq(loyaltyPoints.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(loyaltyPoints.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - } - - function test_state_mintWithSignature_NonZeroPrice_ERC20() public { - vm.warp(1000); - - _mintrequest.price = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - erc20.approve(address(loyaltyPoints), 1); - - uint256 currentTotalSupply = loyaltyPoints.totalSupply(); - uint256 currentBalanceOfRecipient = loyaltyPoints.balanceOf(recipient); - uint256 currentCurrencyBalOfRecipient = erc20.balanceOf(recipient); - - vm.prank(recipient); - loyaltyPoints.mintWithSignature(_mintrequest, _signature); - - assertEq(loyaltyPoints.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(loyaltyPoints.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - assertEq(erc20.balanceOf(recipient), currentCurrencyBalOfRecipient - _mintrequest.price); - } - - function test_state_mintWithSignature_NonZeroPrice_NativeToken() public { - vm.warp(1000); - - _mintrequest.price = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - uint256 currentTotalSupply = loyaltyPoints.totalSupply(); - uint256 currentBalanceOfRecipient = loyaltyPoints.balanceOf(recipient); - - vm.deal(recipient, 1); - uint256 currentCurrencyBalOfRecipient = recipient.balance; - - vm.prank(recipient); - loyaltyPoints.mintWithSignature{ value: 1 }(_mintrequest, _signature); - - assertEq(loyaltyPoints.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(loyaltyPoints.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - assertEq(recipient.balance, currentCurrencyBalOfRecipient - _mintrequest.price); - } - - function test_revert_mintWithSignature_InvalidMsgValue() public { - vm.warp(1000); - - _mintrequest.price = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("Invalid msg value"); - loyaltyPoints.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_ZeroQty() public { - vm.warp(1000); - - _mintrequest.quantity = 0; - _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("Minting zero qty"); - loyaltyPoints.mintWithSignature(_mintrequest, _signature); - } -} diff --git a/src/test/Multicall.t.sol b/src/test/Multicall.t.sol deleted file mode 100644 index 3b9bbfb93..000000000 --- a/src/test/Multicall.t.sol +++ /dev/null @@ -1,260 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "@std/Test.sol"; - -import { Multicall } from "contracts/extension/Multicall.sol"; -import { Forwarder } from "contracts/infra/forwarder/Forwarder.sol"; -import { ERC2771Context } from "contracts/extension/upgradeable/ERC2771Context.sol"; -import { TokenERC721 } from "contracts/prebuilts/token/TokenERC721.sol"; -import { Strings } from "contracts/lib/Strings.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MockMulticallForwarderConsumer is Multicall, ERC2771Context { - event Increment(address caller); - mapping(address => uint256) public counter; - - constructor(address[] memory trustedForwarders) ERC2771Context(trustedForwarders) {} - - function increment() external { - counter[_msgSender()]++; - emit Increment(_msgSender()); - } - - function _msgSender() internal view override(Multicall, ERC2771Context) returns (address sender) { - return ERC2771Context._msgSender(); - } -} - -contract MulticallTest is Test { - // Target (mock) contract - address internal consumer; - TokenERC721 internal token; - - address internal user1; - uint256 internal user1Pkey = 100; - - address internal user2; - uint256 internal user2Pkey = 200; - - // Forwarder details - Forwarder internal forwarder; - - bytes32 internal typehashForwardRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - function setUp() public { - user1 = vm.addr(user1Pkey); - user2 = vm.addr(user2Pkey); - - // Deploy forwarder - forwarder = new Forwarder(); - - // Deploy consumer - address[] memory forwarders = new address[](1); - forwarders[0] = address(forwarder); - consumer = address(new MockMulticallForwarderConsumer(forwarders)); - - // Deploy `TokenERC721` - address impl = address(new TokenERC721()); - token = TokenERC721( - address( - new TWProxy( - impl, - abi.encodeWithSelector( - TokenERC721.initialize.selector, - user1, - "name", - "SYMBOL", - "ipfs://", - forwarders, - user1, - user1, - 0, - 0, - user1 - ) - ) - ) - ); - - // Setup forwarder details - typehashForwardRequest = keccak256( - "ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data)" - ); - nameHash = keccak256(bytes("GSNv2 Forwarder")); - versionHash = keccak256(bytes("0.0.1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(forwarder)) - ); - - vm.label(user1, "USER_1"); - vm.label(user2, "USER_2"); - vm.label(address(forwarder), "FORWARDER"); - vm.label(address(consumer), "CONSUMER"); - } - - function _signForwarderRequest( - Forwarder.ForwardRequest memory forwardRequest, - uint256 privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashForwardRequest, - forwardRequest.from, - forwardRequest.to, - forwardRequest.value, - forwardRequest.gas, - forwardRequest.nonce, - keccak256(forwardRequest.data) - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); - bytes memory signature = abi.encodePacked(r, s, v); - - return signature; - } - - function test_multicall_viaDirectCall() public { - // Make 3 calls to `increment` within a multicall - bytes[] memory calls = new bytes[](3); - calls[0] = abi.encodeWithSelector(MockMulticallForwarderConsumer.increment.selector); - calls[1] = abi.encodeWithSelector(MockMulticallForwarderConsumer.increment.selector); - calls[2] = abi.encodeWithSelector(MockMulticallForwarderConsumer.increment.selector); - - // CASE 1: multicall without using forwarder. Should increment counter for the caller i.e. `msg.sender`. - - assertEq(MockMulticallForwarderConsumer(consumer).counter(user1), 0); - - vm.prank(user1); - Multicall(consumer).multicall(calls); - - assertEq(MockMulticallForwarderConsumer(consumer).counter(user1), 3); // counter incremented! - } - - function test_multicall_viaForwarder() public { - // Make 3 calls to `increment` within a multicall - bytes[] memory calls = new bytes[](3); - calls[0] = abi.encodeWithSelector(MockMulticallForwarderConsumer.increment.selector); - calls[1] = abi.encodeWithSelector(MockMulticallForwarderConsumer.increment.selector); - calls[2] = abi.encodeWithSelector(MockMulticallForwarderConsumer.increment.selector); - - // CASE 2: multicall with using forwarder. Should increment counter for the signer of the forwarder request. - - bytes memory multicallData = abi.encodeWithSelector(Multicall.multicall.selector, calls); - - Forwarder.ForwardRequest memory forwardRequest; - - forwardRequest.from = user1; - forwardRequest.to = address(consumer); - forwardRequest.value = 0; - forwardRequest.gas = 100_000; - forwardRequest.nonce = Forwarder(forwarder).getNonce(user1); - forwardRequest.data = multicallData; - - bytes memory signature = _signForwarderRequest(forwardRequest, user1Pkey); - - Forwarder(forwarder).execute(forwardRequest, signature); - - assertEq(MockMulticallForwarderConsumer(consumer).counter(user1), 3); // counter incremented! - } - - function test_multicall_viaForwarder_attemptSpoof() public { - // Make 3 calls to `increment` within a multicall - bytes[] memory callsSpoof = new bytes[](3); - callsSpoof[0] = abi.encodePacked( - abi.encodeWithSelector(MockMulticallForwarderConsumer.increment.selector), - user1 - ); - callsSpoof[1] = abi.encodePacked( - abi.encodeWithSelector(MockMulticallForwarderConsumer.increment.selector), - user1 - ); - callsSpoof[2] = abi.encodePacked( - abi.encodeWithSelector(MockMulticallForwarderConsumer.increment.selector), - user1 - ); - - // CASE 3: attempting to spoof address by manually appending address to multicall data arg. - // - // This attempt fails because `multicall` enforces original forwarder request signer - // as the `_msgSender()`. - - bytes memory multicallDataSpoof = abi.encodeWithSelector(Multicall.multicall.selector, callsSpoof); - - // user2 spoofing as user1 - Forwarder.ForwardRequest memory forwardRequestSpoof; - - forwardRequestSpoof.from = user2; - forwardRequestSpoof.to = address(consumer); - forwardRequestSpoof.value = 0; - forwardRequestSpoof.gas = 100_000; - forwardRequestSpoof.nonce = Forwarder(forwarder).getNonce(user2); - forwardRequestSpoof.data = multicallDataSpoof; - - bytes memory signatureSpoof = _signForwarderRequest(forwardRequestSpoof, user2Pkey); - - // vm.expectRevert(); - Forwarder(forwarder).execute(forwardRequestSpoof, signatureSpoof); - - assertEq(MockMulticallForwarderConsumer(consumer).counter(user1), 0); // counter unchanged! - assertEq(MockMulticallForwarderConsumer(consumer).counter(user2), 3); // counter incremented for forwarder request signer! - } - - function test_multicall_tokenerc721_viaForwarder_attemptSpoof() public { - // User1 is admin on `token` - assertTrue(token.hasRole(keccak256("MINTER_ROLE"), user1)); - - // token ID `0` has no owner - vm.expectRevert("ERC721: invalid token ID"); - token.ownerOf(0); - - // Make call to `mintTo` within a multicall - bytes[] memory callsSpoof = new bytes[](1); - callsSpoof[0] = abi.encodePacked( - abi.encodeWithSelector(TokenERC721.mintTo.selector, user2, "metadataURI"), - user1 - ); - // CASE: attempting to spoof address by manually appending address to multicall data arg. - // - // This attempt fails because `multicall` enforces original forwarder request signer - // as the `_msgSender()`. - - bytes memory multicallDataSpoof = abi.encodeWithSelector(Multicall.multicall.selector, callsSpoof); - - // user2 spoofing as user1 - Forwarder.ForwardRequest memory forwardRequestSpoof; - - forwardRequestSpoof.from = user2; - forwardRequestSpoof.to = address(token); - forwardRequestSpoof.value = 0; - forwardRequestSpoof.gas = 100_000; - forwardRequestSpoof.nonce = Forwarder(forwarder).getNonce(user2); - forwardRequestSpoof.data = multicallDataSpoof; - - bytes memory signatureSpoof = _signForwarderRequest(forwardRequestSpoof, user2Pkey); - - // Minter role check occurs on user2 i.e. signer of the forwarder request, and not user1 i.e. the address user2 attempts to spoof. - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(user2), 20), - " is missing role ", - Strings.toHexString(uint256(keccak256("MINTER_ROLE")), 32) - ) - ); - Forwarder(forwarder).execute(forwardRequestSpoof, signatureSpoof); - - // token ID `0` still has no owner - vm.expectRevert("ERC721: invalid token ID"); - token.ownerOf(0); - } -} diff --git a/src/test/Multiwrap.t.sol b/src/test/Multiwrap.t.sol deleted file mode 100644 index de0dd7bf8..000000000 --- a/src/test/Multiwrap.t.sol +++ /dev/null @@ -1,844 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { Multiwrap } from "contracts/prebuilts/multiwrap/Multiwrap.sol"; -import { ITokenBundle } from "contracts/extension/interface/ITokenBundle.sol"; -import { CurrencyTransferLib } from "contracts/lib/CurrencyTransferLib.sol"; - -// Test imports -import { MockERC20 } from "./mocks/MockERC20.sol"; -import { Strings } from "contracts/lib/Strings.sol"; -import { Wallet } from "./utils/Wallet.sol"; -import "./utils/BaseTest.sol"; - -contract MultiwrapReentrant is MockERC20, ITokenBundle { - Multiwrap internal multiwrap; - uint256 internal tokenIdOfWrapped = 0; - - constructor(address payable _multiwrap) { - multiwrap = Multiwrap(_multiwrap); - } - - function transferFrom(address from, address to, uint256 amount) public override returns (bool) { - multiwrap.unwrap(0, address(this)); - return super.transferFrom(from, to, amount); - } -} - -contract MultiwrapTest is BaseTest { - /// @dev Emitted when tokens are wrapped. - event TokensWrapped( - address indexed wrapper, - address indexed recipientOfWrappedToken, - uint256 indexed tokenIdOfWrappedToken, - ITokenBundle.Token[] wrappedContents - ); - - /// @dev Emitted when tokens are unwrapped. - event TokensUnwrapped( - address indexed unwrapper, - address indexed recipientOfWrappedContents, - uint256 indexed tokenIdOfWrappedToken - ); - - /*/////////////////////////////////////////////////////////////// - Setup - //////////////////////////////////////////////////////////////*/ - - Multiwrap internal multiwrap; - - Wallet internal tokenOwner; - string internal uriForWrappedToken; - ITokenBundle.Token[] internal wrappedContent; - - function setUp() public override { - super.setUp(); - - // Get target contract - multiwrap = Multiwrap(payable(getContract("Multiwrap"))); - - // Set test vars - tokenOwner = getWallet(); - uriForWrappedToken = "ipfs://baseURI/"; - - wrappedContent.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }) - ); - wrappedContent.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 0, - totalAmount: 1 - }) - ); - wrappedContent.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 0, - totalAmount: 100 - }) - ); - - // Mint tokens-to-wrap to `tokenOwner` - erc20.mint(address(tokenOwner), 10 ether); - erc721.mint(address(tokenOwner), 1); - erc1155.mint(address(tokenOwner), 0, 100); - - // Token owner approves `Multiwrap` to transfer tokens. - tokenOwner.setAllowanceERC20(address(erc20), address(multiwrap), type(uint256).max); - tokenOwner.setApprovalForAllERC721(address(erc721), address(multiwrap), true); - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(multiwrap), true); - - // Grant MINTER_ROLE / requisite wrapping permissions to `tokenOwer` - vm.prank(deployer); - multiwrap.grantRole(keccak256("MINTER_ROLE"), address(tokenOwner)); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc. - //////////////////////////////////////////////////////////////*/ - - /** - * note: Tests whether contract revert when a non-holder renounces a role. - */ - function test_revert_nonHolder_renounceRole() public { - address caller = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(caller); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, caller, role)); - - multiwrap.renounceRole(role, caller); - } - - /** - * note: Tests whether contract revert when a role admin revokes a role for a non-holder. - */ - function test_revert_revokeRoleForNonHolder() public { - address target = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(deployer); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, target, role)); - - multiwrap.revokeRole(role, target); - } - - /** - * Unit tests for relevant functions: - * - `wrap` - * - `unwrap` - */ - - /*/////////////////////////////////////////////////////////////// - Unit tests: `wrap` - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing state changes; token owner calls `wrap` to wrap owned tokens. - */ - function test_state_wrap() public { - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - - assertEq(expectedIdForWrappedToken + 1, multiwrap.nextTokenIdToMint()); - - ITokenBundle.Token[] memory contentsOfWrappedToken = multiwrap.getWrappedContents(expectedIdForWrappedToken); - assertEq(contentsOfWrappedToken.length, wrappedContent.length); - for (uint256 i = 0; i < contentsOfWrappedToken.length; i += 1) { - assertEq(contentsOfWrappedToken[i].assetContract, wrappedContent[i].assetContract); - assertEq(uint256(contentsOfWrappedToken[i].tokenType), uint256(wrappedContent[i].tokenType)); - assertEq(contentsOfWrappedToken[i].tokenId, wrappedContent[i].tokenId); - assertEq(contentsOfWrappedToken[i].totalAmount, wrappedContent[i].totalAmount); - } - - assertEq(uriForWrappedToken, multiwrap.tokenURI(expectedIdForWrappedToken)); - } - - /* - * note: Testing state changes; token owner calls `wrap` to wrap native tokens. - */ - function test_state_wrap_nativeTokens() public { - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - ITokenBundle.Token[] memory nativeTokenContentToWrap = new ITokenBundle.Token[](1); - - vm.deal(address(tokenOwner), 100 ether); - nativeTokenContentToWrap[0] = ITokenBundle.Token({ - assetContract: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }); - - vm.prank(address(tokenOwner)); - multiwrap.wrap{ value: 10 ether }(nativeTokenContentToWrap, uriForWrappedToken, recipient); - - assertEq(expectedIdForWrappedToken + 1, multiwrap.nextTokenIdToMint()); - - ITokenBundle.Token[] memory contentsOfWrappedToken = multiwrap.getWrappedContents(expectedIdForWrappedToken); - assertEq(contentsOfWrappedToken.length, nativeTokenContentToWrap.length); - for (uint256 i = 0; i < contentsOfWrappedToken.length; i += 1) { - assertEq(contentsOfWrappedToken[i].assetContract, nativeTokenContentToWrap[i].assetContract); - assertEq(uint256(contentsOfWrappedToken[i].tokenType), uint256(nativeTokenContentToWrap[i].tokenType)); - assertEq(contentsOfWrappedToken[i].tokenId, nativeTokenContentToWrap[i].tokenId); - assertEq(contentsOfWrappedToken[i].totalAmount, nativeTokenContentToWrap[i].totalAmount); - } - - assertEq(uriForWrappedToken, multiwrap.tokenURI(expectedIdForWrappedToken)); - } - - /** - * note: Testing state changes; token owner calls `wrap` to wrap owned tokens. - * Only assets with ASSET_ROLE can be wrapped. - */ - function test_state_wrap_withAssetRoleRestriction() public { - // ===== setup ===== - - vm.startPrank(deployer); - multiwrap.revokeRole(keccak256("ASSET_ROLE"), address(0)); - - for (uint256 i = 0; i < wrappedContent.length; i += 1) { - multiwrap.grantRole(keccak256("ASSET_ROLE"), wrappedContent[i].assetContract); - } - - vm.stopPrank(); - - // ===== target test content ===== - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - - assertEq(expectedIdForWrappedToken + 1, multiwrap.nextTokenIdToMint()); - - ITokenBundle.Token[] memory contentsOfWrappedToken = multiwrap.getWrappedContents(expectedIdForWrappedToken); - assertEq(contentsOfWrappedToken.length, wrappedContent.length); - for (uint256 i = 0; i < contentsOfWrappedToken.length; i += 1) { - assertEq(contentsOfWrappedToken[i].assetContract, wrappedContent[i].assetContract); - assertEq(uint256(contentsOfWrappedToken[i].tokenType), uint256(wrappedContent[i].tokenType)); - assertEq(contentsOfWrappedToken[i].tokenId, wrappedContent[i].tokenId); - assertEq(contentsOfWrappedToken[i].totalAmount, wrappedContent[i].totalAmount); - } - - assertEq(uriForWrappedToken, multiwrap.tokenURI(expectedIdForWrappedToken)); - } - - /** - * note: Testing event emission; token owner calls `wrap` to wrap owned tokens. - */ - function test_event_wrap_TokensWrapped() public { - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - - vm.expectEmit(true, true, true, true); - emit TokensWrapped(address(tokenOwner), recipient, expectedIdForWrappedToken, wrappedContent); - - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - } - - /** - * note: Testing token balances; token owner calls `wrap` to wrap owned tokens. - */ - function test_balances_wrap() public { - // ERC20 balance - assertEq(erc20.balanceOf(address(tokenOwner)), 10 ether); - assertEq(erc20.balanceOf(address(multiwrap)), 0); - - // ERC721 balance - assertEq(erc721.ownerOf(0), address(tokenOwner)); - - // ERC1155 balance - assertEq(erc1155.balanceOf(address(tokenOwner), 0), 100); - assertEq(erc1155.balanceOf(address(multiwrap), 0), 0); - - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - - // ERC20 balance - assertEq(erc20.balanceOf(address(tokenOwner)), 0); - assertEq(erc20.balanceOf(address(multiwrap)), 10 ether); - - // ERC721 balance - assertEq(erc721.ownerOf(0), address(multiwrap)); - - // ERC1155 balance - assertEq(erc1155.balanceOf(address(tokenOwner), 0), 0); - assertEq(erc1155.balanceOf(address(multiwrap), 0), 100); - - // Multiwrap wrapped token balance - assertEq(multiwrap.ownerOf(expectedIdForWrappedToken), recipient); - } - - /** - * note: Testing revert condition; token owner calls `wrap` to wrap owned tokens. - */ - function test_revert_wrap_reentrancy() public { - MultiwrapReentrant reentrant = new MultiwrapReentrant(payable(address(multiwrap))); - ITokenBundle.Token[] memory reentrantContentToWrap = new ITokenBundle.Token[](1); - - reentrant.mint(address(tokenOwner), 10 ether); - reentrantContentToWrap[0] = ITokenBundle.Token({ - assetContract: address(reentrant), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }); - - tokenOwner.setAllowanceERC20(address(reentrant), address(multiwrap), 10 ether); - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert("ReentrancyGuard: reentrant call"); - multiwrap.wrap(reentrantContentToWrap, uriForWrappedToken, recipient); - } - - /** - * note: Testing revert condition; token owner calls `wrap` to wrap owned tokens. - * Only assets with ASSET_ROLE can be wrapped, but assets being wrapped don't have that role. - */ - function test_revert_wrap_access_ASSET_ROLE() public { - vm.prank(deployer); - multiwrap.revokeRole(keccak256("ASSET_ROLE"), address(0)); - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - address(erc20), - keccak256("ASSET_ROLE") - ) - ); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - } - - /** - * note: Testing revert condition; token owner calls `wrap` to wrap owned tokens, without MINTER_ROLE. - */ - function test_revert_wrap_access_MINTER_ROLE() public { - vm.prank(address(tokenOwner)); - multiwrap.renounceRole(keccak256("MINTER_ROLE"), address(tokenOwner)); - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - address(tokenOwner), - keccak256("MINTER_ROLE") - ) - ); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - } - - /** - * note: Testing revert condition; token owner calls `wrap` with insufficient value when wrapping native tokens. - */ - function test_revert_wrap_nativeTokens_insufficientValue() public { - address recipient = address(0x123); - - ITokenBundle.Token[] memory nativeTokenContentToWrap = new ITokenBundle.Token[](1); - - vm.deal(address(tokenOwner), 100 ether); - nativeTokenContentToWrap[0] = ITokenBundle.Token({ - assetContract: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }); - - vm.prank(address(tokenOwner)); - vm.expectRevert( - abi.encodeWithSelector(CurrencyTransferLib.CurrencyTransferLibMismatchedValue.selector, 0, 10 ether) - ); - multiwrap.wrap(nativeTokenContentToWrap, uriForWrappedToken, recipient); - } - - /** - * note: Testing revert condition; token owner calls `wrap` to wrap native tokens, but with multiple instances in `tokensToWrap` array. - */ - function test_balances_wrap_nativeTokens_multipleInstances() public { - address recipient = address(0x123); - - ITokenBundle.Token[] memory nativeTokenContentToWrap = new ITokenBundle.Token[](2); - - vm.deal(address(tokenOwner), 100 ether); - nativeTokenContentToWrap[0] = ITokenBundle.Token({ - assetContract: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 5 ether - }); - nativeTokenContentToWrap[1] = ITokenBundle.Token({ - assetContract: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 5 ether - }); - - vm.prank(address(tokenOwner)); - multiwrap.wrap{ value: 10 ether }(nativeTokenContentToWrap, uriForWrappedToken, recipient); - - assertEq(weth.balanceOf(address(multiwrap)), 10 ether); - } - - /** - * note: Testing revert condition; token owner calls `wrap` to wrap un-owned ERC20 tokens. - */ - function test_revert_wrap_notOwner_ERC20() public { - tokenOwner.transferERC20(address(erc20), address(0x12), 10 ether); - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert("ERC20: transfer amount exceeds balance"); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - } - - /** - * note: Testing revert condition; token owner calls `wrap` to wrap un-owned ERC721 tokens. - */ - function test_revert_wrap_notOwner_ERC721() public { - tokenOwner.transferERC721(address(erc721), address(0x12), 0); - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert("ERC721: caller is not token owner or approved"); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - } - - /** - * note: Testing revert condition; token owner calls `wrap` to wrap un-owned ERC1155 tokens. - */ - function test_revert_wrap_notOwner_ERC1155() public { - tokenOwner.transferERC1155(address(erc1155), address(0x12), 0, 100, ""); - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert("ERC1155: insufficient balance for transfer"); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - } - - /** - * note: Testing revert condition; token owner calls `wrap` to wrap un-owned ERC20 tokens. - */ - function test_revert_wrap_notApprovedTransfer_ERC20() public { - tokenOwner.setAllowanceERC20(address(erc20), address(multiwrap), 0); - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert("ERC20: insufficient allowance"); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - } - - /** - * note: Testing revert condition; token owner calls `wrap` to wrap un-owned ERC721 tokens. - */ - function test_revert_wrap_notApprovedTransfer_ERC721() public { - tokenOwner.setApprovalForAllERC721(address(erc721), address(multiwrap), false); - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert("ERC721: caller is not token owner or approved"); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - } - - /** - * note: Testing revert condition; token owner calls `wrap` to wrap un-owned ERC1155 tokens. - */ - function test_revert_wrap_notApprovedTransfer_ERC1155() public { - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(multiwrap), false); - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert("ERC1155: caller is not token owner or approved"); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - } - - function test_revert_wrap_noTokensToWrap() public { - ITokenBundle.Token[] memory emptyContent; - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert("!Tokens"); - multiwrap.wrap(emptyContent, uriForWrappedToken, recipient); - } - - function test_revert_wrap_nativeTokens_insufficientValueProvided_multipleInstances() public { - address recipient = address(0x123); - - ITokenBundle.Token[] memory nativeTokenContentToWrap = new ITokenBundle.Token[](2); - - vm.deal(address(tokenOwner), 100 ether); - vm.deal(address(multiwrap), 10 ether); - nativeTokenContentToWrap[0] = ITokenBundle.Token({ - assetContract: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }); - nativeTokenContentToWrap[1] = ITokenBundle.Token({ - assetContract: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }); - - vm.prank(address(tokenOwner)); - vm.expectRevert( - abi.encodeWithSelector(CurrencyTransferLib.CurrencyTransferLibMismatchedValue.selector, 10 ether, 20 ether) - ); - multiwrap.wrap{ value: 10 ether }(nativeTokenContentToWrap, uriForWrappedToken, recipient); - - assertEq(address(multiwrap).balance, 10 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `unwrap` - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing state changes; wrapped token owner calls `unwrap` to unwrap underlying tokens. - */ - function test_state_unwrap() public { - // ===== setup: wrap tokens ===== - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - - // ===== target test content ===== - - vm.prank(recipient); - multiwrap.unwrap(expectedIdForWrappedToken, recipient); - - vm.expectRevert("ERC721: invalid token ID"); - multiwrap.ownerOf(expectedIdForWrappedToken); - - assertEq(uriForWrappedToken, multiwrap.tokenURI(expectedIdForWrappedToken)); - assertEq(0, multiwrap.getTokenCountOfBundle(expectedIdForWrappedToken)); - - ITokenBundle.Token[] memory contentsOfWrappedToken = multiwrap.getWrappedContents(expectedIdForWrappedToken); - assertEq(contentsOfWrappedToken.length, 0); - } - - /** - * note: Testing state changes; wrapped token owner calls `unwrap` to unwrap native tokens. - */ - function test_state_unwrap_nativeTokens() public { - // ===== setup: wrap tokens ===== - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - ITokenBundle.Token[] memory nativeTokenContentToWrap = new ITokenBundle.Token[](1); - - vm.deal(address(tokenOwner), 100 ether); - nativeTokenContentToWrap[0] = ITokenBundle.Token({ - assetContract: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }); - - vm.prank(address(tokenOwner)); - multiwrap.wrap{ value: 10 ether }(nativeTokenContentToWrap, uriForWrappedToken, recipient); - - // ===== target test content ===== - - assertEq(address(recipient).balance, 0); - - vm.prank(recipient); - // it fails here and it shouldn't - multiwrap.unwrap(expectedIdForWrappedToken, recipient); - - assertEq(address(recipient).balance, 10 ether); - } - - /** - * note: Testing state changes; wrapped token owner calls `unwrap` to unwrap underlying tokens. - */ - function test_state_unwrap_approvedCaller() public { - // ===== setup: wrap tokens ===== - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - - // ===== target test content ===== - - address approvedCaller = address(0x12); - - vm.prank(recipient); - multiwrap.setApprovalForAll(approvedCaller, true); - - vm.prank(approvedCaller); - multiwrap.unwrap(expectedIdForWrappedToken, recipient); - - vm.expectRevert("ERC721: invalid token ID"); - multiwrap.ownerOf(expectedIdForWrappedToken); - - assertEq(uriForWrappedToken, multiwrap.tokenURI(expectedIdForWrappedToken)); - assertEq(0, multiwrap.getTokenCountOfBundle(expectedIdForWrappedToken)); - - ITokenBundle.Token[] memory contentsOfWrappedToken = multiwrap.getWrappedContents(expectedIdForWrappedToken); - assertEq(contentsOfWrappedToken.length, 0); - } - - function test_event_unwrap_TokensUnwrapped() public { - // ===== setup: wrap tokens ===== - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - - // ===== target test content ===== - - vm.prank(recipient); - - vm.expectEmit(true, true, true, true); - emit TokensUnwrapped(recipient, recipient, expectedIdForWrappedToken); - - multiwrap.unwrap(expectedIdForWrappedToken, recipient); - } - - function test_balances_unwrap() public { - // ===== setup: wrap tokens ===== - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - - // ===== target test content ===== - - // ERC20 balance - assertEq(erc20.balanceOf(address(recipient)), 0); - assertEq(erc20.balanceOf(address(multiwrap)), 10 ether); - - // ERC721 balance - assertEq(erc721.ownerOf(0), address(multiwrap)); - - // ERC1155 balance - assertEq(erc1155.balanceOf(address(recipient), 0), 0); - assertEq(erc1155.balanceOf(address(multiwrap), 0), 100); - - vm.prank(recipient); - multiwrap.unwrap(expectedIdForWrappedToken, recipient); - - // ERC20 balance - assertEq(erc20.balanceOf(address(recipient)), 10 ether); - assertEq(erc20.balanceOf(address(multiwrap)), 0); - - // ERC721 balance - assertEq(erc721.ownerOf(0), address(recipient)); - - // ERC1155 balance - assertEq(erc1155.balanceOf(address(recipient), 0), 100); - assertEq(erc1155.balanceOf(address(multiwrap), 0), 0); - } - - function test_revert_unwrap_invalidTokenId() public { - // ===== setup: wrap tokens ===== - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - - // ===== target test content ===== - - vm.prank(recipient); - vm.expectRevert("wrapped NFT DNE."); - multiwrap.unwrap(expectedIdForWrappedToken + 1, recipient); - } - - function test_revert_unwrap_unapprovedCaller() public { - // ===== setup: wrap tokens ===== - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - - // ===== target test content ===== - - vm.prank(address(0x12)); - vm.expectRevert("caller not approved for unwrapping."); - multiwrap.unwrap(expectedIdForWrappedToken, recipient); - } - - function test_revert_unwrap_notOwner() public { - // ===== setup: wrap tokens ===== - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - - // ===== target test content ===== - - vm.prank(recipient); - multiwrap.transferFrom(recipient, address(0x12), 0); - - vm.prank(recipient); - vm.expectRevert("caller not approved for unwrapping."); - multiwrap.unwrap(expectedIdForWrappedToken, recipient); - } - - function test_revert_unwrap_access_UNWRAP_ROLE() public { - // ===== setup: wrap tokens ===== - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - - // ===== target test content ===== - - vm.prank(deployer); - multiwrap.revokeRole(keccak256("UNWRAP_ROLE"), address(0)); - - vm.prank(recipient); - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - recipient, - keccak256("UNWRAP_ROLE") - ) - ); - multiwrap.unwrap(expectedIdForWrappedToken, recipient); - } - - /** - * Fuzz testing: - * - Wrapping and unwrapping arbitrary kinds of tokens - */ - - uint256 internal constant MAX_TOKENS = 1000; - - function getTokensToWrap(uint256 x) internal returns (ITokenBundle.Token[] memory tokensToWrap) { - uint256 len = x % MAX_TOKENS; - tokensToWrap = new ITokenBundle.Token[](len); - - for (uint256 i = 0; i < len; i += 1) { - uint256 random = uint256(keccak256(abi.encodePacked(len + i))) % MAX_TOKENS; - uint256 selector = random % 3; - - if (selector == 0) { - tokensToWrap[i] = ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: random - }); - - erc20.mint(address(tokenOwner), tokensToWrap[i].totalAmount); - } else if (selector == 1) { - uint256 tokenId = erc721.nextTokenIdToMint(); - - tokensToWrap[i] = ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: tokenId, - totalAmount: 1 - }); - - erc721.mint(address(tokenOwner), 1); - } else if (selector == 2) { - tokensToWrap[i] = ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: random, - totalAmount: random - }); - - erc1155.mint(address(tokenOwner), tokensToWrap[i].tokenId, tokensToWrap[i].totalAmount); - } - } - } - - function test_fuzz_state_wrap(uint256 x) public { - ITokenBundle.Token[] memory tokensToWrap = getTokensToWrap(x); - if (tokensToWrap.length == 0) { - return; - } - - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(tokensToWrap, uriForWrappedToken, recipient); - - assertEq(expectedIdForWrappedToken + 1, multiwrap.nextTokenIdToMint()); - - ITokenBundle.Token[] memory contentsOfWrappedToken = multiwrap.getWrappedContents(expectedIdForWrappedToken); - assertEq(contentsOfWrappedToken.length, tokensToWrap.length); - for (uint256 i = 0; i < contentsOfWrappedToken.length; i += 1) { - assertEq(contentsOfWrappedToken[i].assetContract, tokensToWrap[i].assetContract); - assertEq(uint256(contentsOfWrappedToken[i].tokenType), uint256(tokensToWrap[i].tokenType)); - assertEq(contentsOfWrappedToken[i].tokenId, tokensToWrap[i].tokenId); - assertEq(contentsOfWrappedToken[i].totalAmount, tokensToWrap[i].totalAmount); - } - - assertEq(uriForWrappedToken, multiwrap.tokenURI(expectedIdForWrappedToken)); - } - - function test_fuzz_state_unwrap(uint256 x) public { - // ===== setup: wrap tokens ===== - - ITokenBundle.Token[] memory tokensToWrap = getTokensToWrap(x); - if (tokensToWrap.length == 0) { - return; - } - - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(tokensToWrap, uriForWrappedToken, recipient); - - // ===== target test content ===== - - vm.prank(recipient); - multiwrap.unwrap(expectedIdForWrappedToken, recipient); - - vm.expectRevert("ERC721: invalid token ID"); - multiwrap.ownerOf(expectedIdForWrappedToken); - - assertEq(uriForWrappedToken, multiwrap.tokenURI(expectedIdForWrappedToken)); - assertEq(0, multiwrap.getTokenCountOfBundle(expectedIdForWrappedToken)); - - ITokenBundle.Token[] memory contentsOfWrappedToken = multiwrap.getWrappedContents(expectedIdForWrappedToken); - assertEq(contentsOfWrappedToken.length, 0); - } -} diff --git a/src/test/OpenEditionERC721.t.sol b/src/test/OpenEditionERC721.t.sol deleted file mode 100644 index 2b9a63db7..000000000 --- a/src/test/OpenEditionERC721.t.sol +++ /dev/null @@ -1,790 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { ERC721AUpgradeable, OpenEditionERC721, ISharedMetadata } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; -import { Drop } from "contracts/extension/Drop.sol"; -import { LazyMint } from "contracts/extension/LazyMint.sol"; -import { OpenEditionERC721 } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; -import { NFTMetadataRenderer } from "contracts/lib/NFTMetadataRenderer.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "./utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract OpenEditionERC721Test is BaseTest { - using Strings for uint256; - using Strings for address; - - event SharedMetadataUpdated(string name, string description, string imageURI, string animationURI); - - OpenEditionERC721 public openEdition; - ISharedMetadata.SharedMetadataInfo public sharedMetadata; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - address openEditionImpl = address(new OpenEditionERC721()); - - vm.prank(deployer); - openEdition = OpenEditionERC721( - address( - new TWProxy( - openEditionImpl, - abi.encodeCall( - OpenEditionERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps - ) - ) - ) - ) - ); - - sharedMetadata = ISharedMetadata.SharedMetadataInfo({ - name: "Test", - description: "Test", - imageURI: "https://test.com", - animationURI: "https://test.com" - }); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc. - //////////////////////////////////////////////////////////////*/ - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_revert_nonHolder_renounceRole() public { - address caller = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(caller); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, caller, role)); - - openEdition.renounceRole(role, caller); - } - - /** - * note: Tests whether contract reverts when a role admin revokes a role for a non-holder. - */ - function test_revert_revokeRoleForNonHolder() public { - address target = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(deployer); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, target, role)); - - openEdition.revokeRole(role, target); - } - - /** - * @dev Tests whether contract reverts when a role is granted to an existent role holder. - */ - function test_revert_grant_role_to_account_with_role() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - - openEdition.grantRole(role, receiver); - - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsAlreadyGranted.selector, receiver, role)); - openEdition.grantRole(role, receiver); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_grant_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - // check if admin and address(0) have transfer role in the beginning - bool checkAddressZero = openEdition.hasRole(role, address(0)); - bool checkAdmin = openEdition.hasRole(role, deployer); - assertTrue(checkAddressZero); - assertTrue(checkAdmin); - - // check if transfer role can be granted to a non-holder - address receiver = getActor(0); - vm.startPrank(deployer); - openEdition.grantRole(role, receiver); - - // expect revert when granting to a holder - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsAlreadyGranted.selector, receiver, role)); - openEdition.grantRole(role, receiver); - - // check if receiver has transfer role - bool checkReceiver = openEdition.hasRole(role, receiver); - assertTrue(checkReceiver); - - // check if role is correctly revoked - openEdition.revokeRole(role, receiver); - checkReceiver = openEdition.hasRole(role, receiver); - assertFalse(checkReceiver); - openEdition.revokeRole(role, address(0)); - checkAddressZero = openEdition.hasRole(role, address(0)); - assertFalse(checkAddressZero); - - vm.stopPrank(); - } - - /** - * note: Testing transfer of tokens when transfer-role is restricted - */ - function test_claim_transferRole() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - OpenEditionERC721.AllowlistProof memory alp; - alp.proof = proofs; - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - openEdition.claim(receiver, 1, address(0), 0, alp, ""); - - // revoke transfer role from address(0) - vm.prank(deployer); - openEdition.revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.startPrank(receiver); - vm.expectRevert(bytes("!T")); - openEdition.transferFrom(receiver, address(123), 1); - } - - function test_claimCondition_with_startTimestamp() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - OpenEditionERC721.AllowlistProof memory alp; - alp.proof = proofs; - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); - conditions[0].startTimestamp = 100; - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.warp(99); - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(abi.encodeWithSelector(Drop.DropNoActiveCondition.selector)); - openEdition.claim(receiver, 1, address(0), 0, alp, ""); - - vm.warp(100); - vm.prank(getActor(4), getActor(4)); - openEdition.claim(receiver, 1, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - Set Shared Metadata Tests - //////////////////////////////////////////////////////////////*/ - - /* - * note: Testing state changes; set shared metadata for tokens. - */ - function test_state_sharedMetadata() public { - // SET METADATA - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - - // CLAIM 1 TOKEN - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - openEdition.claim(receiver, 100, address(erc20), 0, alp, ""); - - string memory uri = openEdition.tokenURI(1); - assertEq( - uri, - NFTMetadataRenderer.createMetadataEdition({ - name: sharedMetadata.name, - description: sharedMetadata.description, - imageURI: sharedMetadata.imageURI, - animationURI: sharedMetadata.animationURI, - tokenOfEdition: 1 - }) - ); - } - - /** - * note: Testing revert condition; an address without MINTER_ROLE calls setSharedMetadata function. - */ - function test_revert_setSharedMetadata_MINTER_ROLE() public { - vm.expectRevert(); - openEdition.setSharedMetadata(sharedMetadata); - } - - /** - * note: Testing event emission; shared metadata set. - */ - function test_event_setSharedMetadata_SharedMetadataUpdated() public { - vm.startPrank(deployer); - - vm.expectEmit(true, false, false, true); - emit SharedMetadataUpdated( - sharedMetadata.name, - sharedMetadata.description, - sharedMetadata.imageURI, - sharedMetadata.animationURI - ); - openEdition.setSharedMetadata(sharedMetadata); - - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Claim Tests - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing revert condition; exceed max claimable supply. - */ - function test_revert_claimCondition_exceedMaxClaimableSupply() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - OpenEditionERC721.AllowlistProof memory alp; - alp.proof = proofs; - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 200; - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - openEdition.claim(receiver, 100, address(0), 0, alp, ""); - - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedMaxSupply.selector, conditions[0].maxClaimableSupply, 101) - ); - vm.prank(getActor(6), getActor(6)); - openEdition.claim(receiver, 1, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction when no allowlist present. - */ - function test_fuzz_claim_noAllowlist(uint256 x) public { - vm.assume(x != 0); - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - OpenEditionERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 0) - ); - openEdition.claim(receiver, 0, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 101) - ); - openEdition.claim(receiver, 101, address(0), 0, alp, ""); - - vm.prank(deployer); - openEdition.setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 101) - ); - openEdition.claim(receiver, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to 0 - */ - function test_state_claim_allowlisted_SetQuantityZeroPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - openEdition.claim(receiver, 100, address(erc20), 0, alp, ""); // claims for free, because allowlist price is 0 - assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), 100); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to non-zero value - */ - function test_state_claim_allowlisted_SetQuantityPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "5"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 5; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.prank(receiver, receiver); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimInvalidTokenPrice.selector, address(erc20), 0, address(erc20), 5) - ); - openEdition.claim(receiver, 100, address(erc20), 0, alp, ""); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(openEdition), 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - openEdition.claim(receiver, 100, address(erc20), 5, alp, ""); - assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), 100); - assertEq(erc20.balanceOf(receiver), 10000 - 500); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price not set; should default to general price and currency - */ - function test_state_claim_allowlisted_SetQuantityDefaultPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = Strings.toString(type(uint256).max); // this implies that general price is applicable - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = type(uint256).max; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(openEdition), 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - openEdition.claim(receiver, 100, address(erc20), 10, alp, ""); - assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), 100); - assertEq(erc20.balanceOf(receiver), 10000 - 1000); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to 0 => should default to general limit - * - allowlist price set to some value different than general price - */ - function test_state_claim_allowlisted_DefaultQuantitySomePrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "0"; // this implies that general limit is applicable - inputs[3] = "5"; - inputs[4] = "0x0000000000000000000000000000000000000000"; // general currency will be applicable - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 0; - alp.pricePerToken = 5; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(openEdition), 10000); - - vm.prank(receiver, receiver); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 100) - ); - openEdition.claim(receiver, 100, address(erc20), 5, alp, ""); // trying to claim more than general limit - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - openEdition.claim(receiver, 10, address(erc20), 5, alp, ""); - assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), 10); - assertEq(erc20.balanceOf(receiver), 10000 - 50); - } - - function test_fuzz_claim_merkleProof(uint256 x) public { - vm.assume(x > 10 && x < 500); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(x); - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - alp.pricePerToken = 0; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - // bytes32[] memory proofs = new bytes32[](0); - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = x; - conditions[0].quantityLimitPerWallet = 1; - conditions[0].merkleRoot = root; - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - openEdition.claim(receiver, x - 5, address(0), 0, alp, ""); - assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), x - 5); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, x, x + 1)); - openEdition.claim(receiver, 6, address(0), 0, alp, ""); - - vm.prank(receiver, receiver); - openEdition.claim(receiver, 5, address(0), 0, alp, ""); - assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), x); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, x, x + 5)); - openEdition.claim(receiver, 5, address(0), 0, alp, ""); // quantity limit already claimed - } - - /** - * note: Testing state changes; reset eligibility of claim conditions and claiming again for same condition id. - */ - function test_state_claimCondition_resetEligibility() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - OpenEditionERC721.AllowlistProof memory alp; - alp.proof = proofs; - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - openEdition.claim(receiver, 100, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 200) - ); - openEdition.claim(receiver, 100, address(0), 0, alp, ""); - - vm.prank(deployer); - openEdition.setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - openEdition.claim(receiver, 100, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - setClaimConditions - //////////////////////////////////////////////////////////////*/ - - function test_claimCondition_startIdAndCount() public { - vm.startPrank(deployer); - - uint256 currentStartId = 0; - uint256 count = 0; - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](2); - conditions[0].startTimestamp = 0; - conditions[0].maxClaimableSupply = 10; - conditions[1].startTimestamp = 1; - conditions[1].maxClaimableSupply = 10; - - openEdition.setClaimConditions(conditions, false); - (currentStartId, count) = openEdition.claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - openEdition.setClaimConditions(conditions, false); - (currentStartId, count) = openEdition.claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - openEdition.setClaimConditions(conditions, true); - (currentStartId, count) = openEdition.claimCondition(); - assertEq(currentStartId, 2); - assertEq(count, 2); - - openEdition.setClaimConditions(conditions, true); - (currentStartId, count) = openEdition.claimCondition(); - assertEq(currentStartId, 4); - assertEq(count, 2); - } - - function test_claimCondition_startPhase() public { - vm.startPrank(deployer); - - uint256 activeConditionId = 0; - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](3); - conditions[0].startTimestamp = 10; - conditions[0].maxClaimableSupply = 11; - conditions[0].quantityLimitPerWallet = 12; - conditions[1].startTimestamp = 20; - conditions[1].maxClaimableSupply = 21; - conditions[1].quantityLimitPerWallet = 22; - conditions[2].startTimestamp = 30; - conditions[2].maxClaimableSupply = 31; - conditions[2].quantityLimitPerWallet = 32; - openEdition.setClaimConditions(conditions, false); - - vm.expectRevert(abi.encodeWithSelector(Drop.DropNoActiveCondition.selector)); - openEdition.getActiveClaimConditionId(); - - vm.warp(10); - activeConditionId = openEdition.getActiveClaimConditionId(); - assertEq(activeConditionId, 0); - assertEq(openEdition.getClaimConditionById(activeConditionId).startTimestamp, 10); - assertEq(openEdition.getClaimConditionById(activeConditionId).maxClaimableSupply, 11); - assertEq(openEdition.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 12); - - vm.warp(20); - activeConditionId = openEdition.getActiveClaimConditionId(); - assertEq(activeConditionId, 1); - assertEq(openEdition.getClaimConditionById(activeConditionId).startTimestamp, 20); - assertEq(openEdition.getClaimConditionById(activeConditionId).maxClaimableSupply, 21); - assertEq(openEdition.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 22); - - vm.warp(30); - activeConditionId = openEdition.getActiveClaimConditionId(); - assertEq(activeConditionId, 2); - assertEq(openEdition.getClaimConditionById(activeConditionId).startTimestamp, 30); - assertEq(openEdition.getClaimConditionById(activeConditionId).maxClaimableSupply, 31); - assertEq(openEdition.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 32); - - vm.warp(40); - assertEq(openEdition.getActiveClaimConditionId(), 2); - } -} diff --git a/src/test/OpenEditionERC721FlatFee.t.sol b/src/test/OpenEditionERC721FlatFee.t.sol deleted file mode 100644 index 38de7326c..000000000 --- a/src/test/OpenEditionERC721FlatFee.t.sol +++ /dev/null @@ -1,792 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { ERC721AUpgradeable, OpenEditionERC721FlatFee, ISharedMetadata } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; -import { Drop } from "contracts/extension/Drop.sol"; -import { LazyMint } from "contracts/extension/LazyMint.sol"; -import { OpenEditionERC721FlatFee } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; -import { NFTMetadataRenderer } from "contracts/lib/NFTMetadataRenderer.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "./utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract OpenEditionERC721FlatFeeTest is BaseTest { - using Strings for uint256; - using Strings for address; - - event SharedMetadataUpdated(string name, string description, string imageURI, string animationURI); - - OpenEditionERC721FlatFee public openEdition; - ISharedMetadata.SharedMetadataInfo public sharedMetadata; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - address openEditionImpl = address(new OpenEditionERC721FlatFee()); - - vm.prank(deployer); - openEdition = OpenEditionERC721FlatFee( - address( - new TWProxy( - openEditionImpl, - abi.encodeCall( - OpenEditionERC721FlatFee.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ); - - sharedMetadata = ISharedMetadata.SharedMetadataInfo({ - name: "Test", - description: "Test", - imageURI: "https://test.com", - animationURI: "https://test.com" - }); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc. - //////////////////////////////////////////////////////////////*/ - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_revert_nonHolder_renounceRole() public { - address caller = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(caller); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, caller, role)); - - openEdition.renounceRole(role, caller); - } - - /** - * note: Tests whether contract reverts when a role admin revokes a role for a non-holder. - */ - function test_revert_revokeRoleForNonHolder() public { - address target = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(deployer); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, target, role)); - - openEdition.revokeRole(role, target); - } - - /** - * @dev Tests whether contract reverts when a role is granted to an existent role holder. - */ - function test_revert_grant_role_to_account_with_role() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - - openEdition.grantRole(role, receiver); - - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsAlreadyGranted.selector, receiver, role)); - openEdition.grantRole(role, receiver); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_grant_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - // check if admin and address(0) have transfer role in the beginning - bool checkAddressZero = openEdition.hasRole(role, address(0)); - bool checkAdmin = openEdition.hasRole(role, deployer); - assertTrue(checkAddressZero); - assertTrue(checkAdmin); - - // check if transfer role can be granted to a non-holder - address receiver = getActor(0); - vm.startPrank(deployer); - openEdition.grantRole(role, receiver); - - // expect revert when granting to a holder - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsAlreadyGranted.selector, receiver, role)); - openEdition.grantRole(role, receiver); - - // check if receiver has transfer role - bool checkReceiver = openEdition.hasRole(role, receiver); - assertTrue(checkReceiver); - - // check if role is correctly revoked - openEdition.revokeRole(role, receiver); - checkReceiver = openEdition.hasRole(role, receiver); - assertFalse(checkReceiver); - openEdition.revokeRole(role, address(0)); - checkAddressZero = openEdition.hasRole(role, address(0)); - assertFalse(checkAddressZero); - - vm.stopPrank(); - } - - /** - * note: Testing transfer of tokens when transfer-role is restricted - */ - function test_claim_transferRole() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - OpenEditionERC721FlatFee.AllowlistProof memory alp; - alp.proof = proofs; - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - openEdition.claim(receiver, 1, address(0), 0, alp, ""); - - // revoke transfer role from address(0) - vm.prank(deployer); - openEdition.revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.startPrank(receiver); - vm.expectRevert(bytes("!T")); - openEdition.transferFrom(receiver, address(123), 1); - } - - function test_claimCondition_with_startTimestamp() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - OpenEditionERC721FlatFee.AllowlistProof memory alp; - alp.proof = proofs; - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); - conditions[0].startTimestamp = 100; - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.warp(99); - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(abi.encodeWithSelector(Drop.DropNoActiveCondition.selector)); - openEdition.claim(receiver, 1, address(0), 0, alp, ""); - - vm.warp(100); - vm.prank(getActor(4), getActor(4)); - openEdition.claim(receiver, 1, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - Set Shared Metadata Tests - //////////////////////////////////////////////////////////////*/ - - /* - * note: Testing state changes; set shared metadata for tokens. - */ - function test_state_sharedMetadata() public { - // SET METADATA - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - - // CLAIM 1 TOKEN - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721FlatFee.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - openEdition.claim(receiver, 100, address(erc20), 0, alp, ""); - - string memory uri = openEdition.tokenURI(1); - assertEq( - uri, - NFTMetadataRenderer.createMetadataEdition({ - name: sharedMetadata.name, - description: sharedMetadata.description, - imageURI: sharedMetadata.imageURI, - animationURI: sharedMetadata.animationURI, - tokenOfEdition: 1 - }) - ); - } - - /** - * note: Testing revert condition; an address without MINTER_ROLE calls setSharedMetadata function. - */ - function test_revert_setSharedMetadata_MINTER_ROLE() public { - vm.expectRevert(); - openEdition.setSharedMetadata(sharedMetadata); - } - - /** - * note: Testing event emission; shared metadata set. - */ - function test_event_setSharedMetadata_SharedMetadataUpdated() public { - vm.startPrank(deployer); - - vm.expectEmit(true, false, false, true); - emit SharedMetadataUpdated( - sharedMetadata.name, - sharedMetadata.description, - sharedMetadata.imageURI, - sharedMetadata.animationURI - ); - openEdition.setSharedMetadata(sharedMetadata); - - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Claim Tests - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing revert condition; exceed max claimable supply. - */ - function test_revert_claimCondition_exceedMaxClaimableSupply() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - OpenEditionERC721FlatFee.AllowlistProof memory alp; - alp.proof = proofs; - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 200; - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - openEdition.claim(receiver, 100, address(0), 0, alp, ""); - - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedMaxSupply.selector, conditions[0].maxClaimableSupply, 101) - ); - vm.prank(getActor(6), getActor(6)); - openEdition.claim(receiver, 1, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction when no allowlist present. - */ - function test_fuzz_claim_noAllowlist(uint256 x) public { - vm.assume(x != 0); - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - OpenEditionERC721FlatFee.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 0) - ); - openEdition.claim(receiver, 0, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 101) - ); - openEdition.claim(receiver, 101, address(0), 0, alp, ""); - - vm.prank(deployer); - openEdition.setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 101) - ); - openEdition.claim(receiver, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to 0 - */ - function test_state_claim_allowlisted_SetQuantityZeroPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721FlatFee.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - openEdition.claim(receiver, 100, address(erc20), 0, alp, ""); // claims for free, because allowlist price is 0 - assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), 100); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to non-zero value - */ - function test_state_claim_allowlisted_SetQuantityPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "5"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721FlatFee.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 5; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.prank(receiver, receiver); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimInvalidTokenPrice.selector, address(erc20), 0, address(erc20), 5) - ); - openEdition.claim(receiver, 100, address(erc20), 0, alp, ""); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(openEdition), 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - openEdition.claim(receiver, 100, address(erc20), 5, alp, ""); - assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), 100); - assertEq(erc20.balanceOf(receiver), 10000 - 500); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price not set; should default to general price and currency - */ - function test_state_claim_allowlisted_SetQuantityDefaultPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = Strings.toString(type(uint256).max); // this implies that general price is applicable - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721FlatFee.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = type(uint256).max; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(openEdition), 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - openEdition.claim(receiver, 100, address(erc20), 10, alp, ""); - assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), 100); - assertEq(erc20.balanceOf(receiver), 10000 - 1000); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to 0 => should default to general limit - * - allowlist price set to some value different than general price - */ - function test_state_claim_allowlisted_DefaultQuantitySomePrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "0"; // this implies that general limit is applicable - inputs[3] = "5"; - inputs[4] = "0x0000000000000000000000000000000000000000"; // general currency will be applicable - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721FlatFee.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 0; - alp.pricePerToken = 5; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(openEdition), 10000); - - vm.prank(receiver, receiver); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 100) - ); - openEdition.claim(receiver, 100, address(erc20), 5, alp, ""); // trying to claim more than general limit - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - openEdition.claim(receiver, 10, address(erc20), 5, alp, ""); - assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), 10); - assertEq(erc20.balanceOf(receiver), 10000 - 50); - } - - function test_fuzz_claim_merkleProof(uint256 x) public { - vm.assume(x > 10 && x < 500); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(x); - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721FlatFee.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - alp.pricePerToken = 0; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - // bytes32[] memory proofs = new bytes32[](0); - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); - conditions[0].maxClaimableSupply = x; - conditions[0].quantityLimitPerWallet = 1; - conditions[0].merkleRoot = root; - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - openEdition.claim(receiver, x - 5, address(0), 0, alp, ""); - assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), x - 5); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, x, x + 1)); - openEdition.claim(receiver, 6, address(0), 0, alp, ""); - - vm.prank(receiver, receiver); - openEdition.claim(receiver, 5, address(0), 0, alp, ""); - assertEq(openEdition.getSupplyClaimedByWallet(openEdition.getActiveClaimConditionId(), receiver), x); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, x, x + 5)); - openEdition.claim(receiver, 5, address(0), 0, alp, ""); // quantity limit already claimed - } - - /** - * note: Testing state changes; reset eligibility of claim conditions and claiming again for same condition id. - */ - function test_state_claimCondition_resetEligibility() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - OpenEditionERC721FlatFee.AllowlistProof memory alp; - alp.proof = proofs; - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - openEdition.claim(receiver, 100, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 200) - ); - openEdition.claim(receiver, 100, address(0), 0, alp, ""); - - vm.prank(deployer); - openEdition.setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - openEdition.claim(receiver, 100, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - setClaimConditions - //////////////////////////////////////////////////////////////*/ - - function test_claimCondition_startIdAndCount() public { - vm.startPrank(deployer); - - uint256 currentStartId = 0; - uint256 count = 0; - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](2); - conditions[0].startTimestamp = 0; - conditions[0].maxClaimableSupply = 10; - conditions[1].startTimestamp = 1; - conditions[1].maxClaimableSupply = 10; - - openEdition.setClaimConditions(conditions, false); - (currentStartId, count) = openEdition.claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - openEdition.setClaimConditions(conditions, false); - (currentStartId, count) = openEdition.claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - openEdition.setClaimConditions(conditions, true); - (currentStartId, count) = openEdition.claimCondition(); - assertEq(currentStartId, 2); - assertEq(count, 2); - - openEdition.setClaimConditions(conditions, true); - (currentStartId, count) = openEdition.claimCondition(); - assertEq(currentStartId, 4); - assertEq(count, 2); - } - - function test_claimCondition_startPhase() public { - vm.startPrank(deployer); - - uint256 activeConditionId = 0; - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](3); - conditions[0].startTimestamp = 10; - conditions[0].maxClaimableSupply = 11; - conditions[0].quantityLimitPerWallet = 12; - conditions[1].startTimestamp = 20; - conditions[1].maxClaimableSupply = 21; - conditions[1].quantityLimitPerWallet = 22; - conditions[2].startTimestamp = 30; - conditions[2].maxClaimableSupply = 31; - conditions[2].quantityLimitPerWallet = 32; - openEdition.setClaimConditions(conditions, false); - - vm.expectRevert(abi.encodeWithSelector(Drop.DropNoActiveCondition.selector)); - openEdition.getActiveClaimConditionId(); - - vm.warp(10); - activeConditionId = openEdition.getActiveClaimConditionId(); - assertEq(activeConditionId, 0); - assertEq(openEdition.getClaimConditionById(activeConditionId).startTimestamp, 10); - assertEq(openEdition.getClaimConditionById(activeConditionId).maxClaimableSupply, 11); - assertEq(openEdition.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 12); - - vm.warp(20); - activeConditionId = openEdition.getActiveClaimConditionId(); - assertEq(activeConditionId, 1); - assertEq(openEdition.getClaimConditionById(activeConditionId).startTimestamp, 20); - assertEq(openEdition.getClaimConditionById(activeConditionId).maxClaimableSupply, 21); - assertEq(openEdition.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 22); - - vm.warp(30); - activeConditionId = openEdition.getActiveClaimConditionId(); - assertEq(activeConditionId, 2); - assertEq(openEdition.getClaimConditionById(activeConditionId).startTimestamp, 30); - assertEq(openEdition.getClaimConditionById(activeConditionId).maxClaimableSupply, 31); - assertEq(openEdition.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 32); - - vm.warp(40); - assertEq(openEdition.getActiveClaimConditionId(), 2); - } -} diff --git a/src/test/SignatureDrop.t.sol b/src/test/SignatureDrop.t.sol deleted file mode 100644 index f26f82022..000000000 --- a/src/test/SignatureDrop.t.sol +++ /dev/null @@ -1,1370 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { SignatureDrop, DropSinglePhase, Permissions, LazyMint, BatchMintMetadata, DelayedReveal, IDropSinglePhase, IDelayedReveal, ISignatureMintERC721, ERC721AUpgradeable, IPermissions, ILazyMint } from "contracts/prebuilts/signature-drop/SignatureDrop.sol"; -import { SignatureMintERC721 } from "contracts/extension/SignatureMintERC721.sol"; - -// Test imports -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "./utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract SignatureDropTest is BaseTest { - using Strings for uint256; - using Strings for address; - - event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI); - event TokenURIRevealed(uint256 indexed index, string revealedURI); - event TokensMintedWithSignature( - address indexed signer, - address indexed mintedTo, - uint256 indexed tokenIdMinted, - SignatureDrop.MintRequest mintRequest - ); - - SignatureDrop public sigdrop; - address internal deployerSigner; - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - deployerSigner = signer; - sigdrop = SignatureDrop(getContract("SignatureDrop")); - - erc20.mint(deployerSigner, 1_000 ether); - vm.deal(deployerSigner, 1_000 ether); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,string uri,uint256 quantity,uint256 pricePerToken,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("SignatureMintERC721")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(sigdrop))); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc. - //////////////////////////////////////////////////////////////*/ - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_revert_nonHolder_renounceRole() public { - address caller = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(caller); - vm.expectRevert(); - - sigdrop.renounceRole(role, caller); - } - - /** - * note: Tests whether contract reverts when a role admin revokes a role for a non-holder. - */ - function test_revert_revokeRoleForNonHolder() public { - address target = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(deployerSigner); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, target, role)); - - sigdrop.revokeRole(role, target); - } - - /** - * @dev Tests whether contract reverts when a role is granted to an existent role holder. - */ - function test_revert_grant_role_to_account_with_role() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployerSigner); - - sigdrop.grantRole(role, receiver); - - vm.expectRevert(); - sigdrop.grantRole(role, receiver); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_grant_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - // check if admin and address(0) have transfer role in the beginning - bool checkAddressZero = sigdrop.hasRole(role, address(0)); - bool checkAdmin = sigdrop.hasRole(role, deployerSigner); - assertTrue(checkAddressZero); - assertTrue(checkAdmin); - - // check if transfer role can be granted to a non-holder - address receiver = getActor(0); - vm.startPrank(deployerSigner); - sigdrop.grantRole(role, receiver); - - // expect revert when granting to a holder - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsAlreadyGranted.selector, receiver, role)); - sigdrop.grantRole(role, receiver); - - // check if receiver has transfer role - bool checkReceiver = sigdrop.hasRole(role, receiver); - assertTrue(checkReceiver); - - // check if role is correctly revoked - sigdrop.revokeRole(role, receiver); - checkReceiver = sigdrop.hasRole(role, receiver); - assertFalse(checkReceiver); - sigdrop.revokeRole(role, address(0)); - checkAddressZero = sigdrop.hasRole(role, address(0)); - assertFalse(checkAddressZero); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_getRoleMember_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - uint256 roleMemberCount = sigdrop.getRoleMemberCount(role); - assertEq(roleMemberCount, 2); - - address roleMember = sigdrop.getRoleMember(role, 1); - assertEq(roleMember, address(0)); - - vm.startPrank(deployerSigner); - sigdrop.grantRole(role, address(2)); - sigdrop.grantRole(role, address(3)); - sigdrop.grantRole(role, address(4)); - - roleMemberCount = sigdrop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(sigdrop.getRoleMember(role, i)); - } - console.log(""); - - sigdrop.revokeRole(role, address(2)); - roleMemberCount = sigdrop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(sigdrop.getRoleMember(role, i)); - } - console.log(""); - - sigdrop.revokeRole(role, address(0)); - roleMemberCount = sigdrop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(sigdrop.getRoleMember(role, i)); - } - console.log(""); - - sigdrop.grantRole(role, address(5)); - roleMemberCount = sigdrop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(sigdrop.getRoleMember(role, i)); - } - console.log(""); - - sigdrop.grantRole(role, address(0)); - roleMemberCount = sigdrop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(sigdrop.getRoleMember(role, i)); - } - console.log(""); - - sigdrop.grantRole(role, address(6)); - roleMemberCount = sigdrop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(sigdrop.getRoleMember(role, i)); - } - console.log(""); - } - - /** - * note: Testing transfer of tokens when transfer-role is restricted - */ - function test_claim_transferRole() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - SignatureDrop.AllowlistProof memory alp; - alp.proof = proofs; - - SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - vm.prank(deployerSigner); - sigdrop.setClaimConditions(conditions[0], false); - - vm.prank(getActor(5), getActor(5)); - sigdrop.claim(receiver, 1, address(0), 0, alp, ""); - - // revoke transfer role from address(0) - vm.prank(deployerSigner); - sigdrop.revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.startPrank(receiver); - vm.expectRevert("!Transfer-Role"); - sigdrop.transferFrom(receiver, address(123), 0); - } - - /** - * @dev Tests whether role member count is incremented correctly. - */ - function test_member_count_incremented_properly_when_role_granted() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployerSigner); - uint256 roleMemberCount = sigdrop.getRoleMemberCount(role); - - assertEq(roleMemberCount, 0); - - sigdrop.grantRole(role, receiver); - - assertEq(sigdrop.getRoleMemberCount(role), 1); - - vm.stopPrank(); - } - - function test_claimCondition_with_startTimestamp() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - SignatureDrop.AllowlistProof memory alp; - alp.proof = proofs; - - SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - conditions[0].startTimestamp = 100; - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.prank(deployerSigner); - sigdrop.setClaimConditions(conditions[0], false); - - vm.warp(100); - vm.prank(getActor(4), getActor(4)); - sigdrop.claim(receiver, 1, address(0), 0, alp, ""); - - vm.warp(99); - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector( - DropSinglePhase.DropClaimNotStarted.selector, - conditions[0].startTimestamp, - block.timestamp - ) - ); - sigdrop.claim(receiver, 1, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - Lazy Mint Tests - //////////////////////////////////////////////////////////////*/ - - /* - * note: Testing state changes; lazy mint a batch of tokens with no encrypted base URI. - */ - function test_state_lazyMint_noEncryptedURI() public { - uint256 amountToLazyMint = 100; - string memory baseURI = "ipfs://"; - - uint256 nextTokenIdToMintBefore = sigdrop.nextTokenIdToMint(); - - vm.startPrank(deployerSigner); - uint256 batchId = sigdrop.lazyMint(amountToLazyMint, baseURI, emptyEncodedBytes); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, sigdrop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = sigdrop.tokenURI(i); - console.log(uri); - assertEq(uri, string(abi.encodePacked(baseURI, i.toString()))); - } - - vm.stopPrank(); - } - - /* - * note: Testing state changes; lazy mint a batch of tokens with encrypted base URI. - */ - function test_state_lazyMint_withEncryptedURI() public { - uint256 amountToLazyMint = 100; - string memory baseURI = "ipfs://"; - bytes memory encryptedBaseURI = "encryptedBaseURI://"; - bytes32 provenanceHash = bytes32("whatever"); - - uint256 nextTokenIdToMintBefore = sigdrop.nextTokenIdToMint(); - - vm.startPrank(deployerSigner); - uint256 batchId = sigdrop.lazyMint(amountToLazyMint, baseURI, abi.encode(encryptedBaseURI, provenanceHash)); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, sigdrop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = sigdrop.tokenURI(1); - assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - } - - vm.stopPrank(); - } - - /** - * note: Testing revert condition; an address without MINTER_ROLE calls lazyMint function. - */ - function test_revert_lazyMint_MINTER_ROLE() public { - bytes32 _minterRole = keccak256("MINTER_ROLE"); - - vm.prank(deployerSigner); - sigdrop.grantRole(_minterRole, address(0x345)); - - vm.prank(address(0x345)); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.prank(address(0x567)); - vm.expectRevert(abi.encodeWithSelector(LazyMint.LazyMintUnauthorized.selector)); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - } - - /* - * note: Testing revert condition; calling tokenURI for invalid batch id. - */ - function test_revert_lazyMint_URIForNonLazyMintedToken() public { - vm.startPrank(deployerSigner); - - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidTokenId.selector, 100)); - sigdrop.tokenURI(100); - - vm.stopPrank(); - } - - /** - * note: Testing event emission; tokens lazy minted. - */ - function test_event_lazyMint_TokensLazyMinted() public { - vm.startPrank(deployerSigner); - - vm.expectEmit(true, false, false, true); - emit TokensLazyMinted(0, 99, "ipfs://", emptyEncodedBytes); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.stopPrank(); - } - - /* - * note: Fuzz testing state changes; lazy mint a batch of tokens with no encrypted base URI. - */ - function test_fuzz_lazyMint_noEncryptedURI(uint256 x) public { - vm.assume(x > 0); - - uint256 amountToLazyMint = x; - string memory baseURI = "ipfs://"; - - uint256 nextTokenIdToMintBefore = sigdrop.nextTokenIdToMint(); - - vm.startPrank(deployerSigner); - uint256 batchId = sigdrop.lazyMint(amountToLazyMint, baseURI, emptyEncodedBytes); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, sigdrop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - string memory uri = sigdrop.tokenURI(0); - assertEq(uri, string(abi.encodePacked(baseURI, uint256(0).toString()))); - - uri = sigdrop.tokenURI(x - 1); - assertEq(uri, string(abi.encodePacked(baseURI, uint256(x - 1).toString()))); - - /** - * note: this loop takes too long to run with fuzz tests. - */ - // for(uint256 i = 0; i < amountToLazyMint; i += 1) { - // string memory uri = sigdrop.tokenURI(i); - // console.log(uri); - // assertEq(uri, string(abi.encodePacked(baseURI, i.toString()))); - // } - - vm.stopPrank(); - } - - /* - * note: Fuzz testing state changes; lazy mint a batch of tokens with encrypted base URI. - */ - function test_fuzz_lazyMint_withEncryptedURI(uint256 x) public { - vm.assume(x > 0); - - uint256 amountToLazyMint = x; - string memory baseURI = "ipfs://"; - bytes memory encryptedBaseURI = "encryptedBaseURI://"; - bytes32 provenanceHash = bytes32("whatever"); - - uint256 nextTokenIdToMintBefore = sigdrop.nextTokenIdToMint(); - - vm.startPrank(deployerSigner); - uint256 batchId = sigdrop.lazyMint(amountToLazyMint, baseURI, abi.encode(encryptedBaseURI, provenanceHash)); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, sigdrop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - string memory uri = sigdrop.tokenURI(0); - assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - - uri = sigdrop.tokenURI(x - 1); - assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - - /** - * note: this loop takes too long to run with fuzz tests. - */ - // for(uint256 i = 0; i < amountToLazyMint; i += 1) { - // string memory uri = sigdrop.tokenURI(1); - // assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - // } - - vm.stopPrank(); - } - - /* - * note: Fuzz testing; a batch of tokens, and nextTokenIdToMint - */ - function test_fuzz_lazyMint_batchMintAndNextTokenIdToMint(uint256 x) public { - vm.assume(x > 0); - vm.startPrank(deployerSigner); - - if (x == 0) { - vm.expectRevert("Zero amount"); - } - sigdrop.lazyMint(x, "ipfs://", emptyEncodedBytes); - - uint256 slot = stdstore.target(address(sigdrop)).sig("nextTokenIdToMint()").find(); - bytes32 loc = bytes32(slot); - uint256 nextTokenIdToMint = uint256(vm.load(address(sigdrop), loc)); - - assertEq(nextTokenIdToMint, x); - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Delayed Reveal Tests - //////////////////////////////////////////////////////////////*/ - - /* - * note: Testing state changes; URI revealed for a batch of tokens. - */ - function test_state_reveal() public { - vm.startPrank(deployerSigner); - - bytes memory key = "key"; - uint256 amountToLazyMint = 100; - bytes memory secretURI = "ipfs://"; - string memory placeholderURI = "ipfs://"; - bytes memory encryptedURI = sigdrop.encryptDecrypt(secretURI, key); - bytes32 provenanceHash = keccak256(abi.encodePacked(secretURI, key, block.chainid)); - - sigdrop.lazyMint(amountToLazyMint, placeholderURI, abi.encode(encryptedURI, provenanceHash)); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = sigdrop.tokenURI(i); - assertEq(uri, string(abi.encodePacked(placeholderURI, "0"))); - } - - string memory revealedURI = sigdrop.reveal(0, key); - assertEq(revealedURI, string(secretURI)); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = sigdrop.tokenURI(i); - assertEq(uri, string(abi.encodePacked(secretURI, i.toString()))); - } - - vm.stopPrank(); - } - - /** - * note: Testing revert condition; an address without MINTER_ROLE calls reveal function. - */ - function test_revert_reveal_MINTER_ROLE() public { - bytes memory key = "key"; - bytes memory encryptedURI = sigdrop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.prank(deployerSigner); - sigdrop.reveal(0, "key"); - - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - address(this), - keccak256("MINTER_ROLE") - ) - ); - sigdrop.reveal(0, "key"); - } - - /* - * note: Testing revert condition; trying to reveal URI for non-existent batch. - */ - function test_revert_reveal_revealingNonExistentBatch() public { - vm.startPrank(deployerSigner); - - bytes memory key = "key"; - bytes memory encryptedURI = sigdrop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - sigdrop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - sigdrop.reveal(0, "key"); - - console.log(sigdrop.getBaseURICount()); - - sigdrop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidBatchId.selector, 2)); - sigdrop.reveal(2, "key"); - - vm.stopPrank(); - } - - /* - * note: Testing revert condition; already revealed URI. - */ - function test_revert_delayedReveal_alreadyRevealed() public { - vm.startPrank(deployerSigner); - - bytes memory key = "key"; - bytes memory encryptedURI = sigdrop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - sigdrop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - sigdrop.reveal(0, "key"); - - vm.expectRevert(abi.encodeWithSelector(DelayedReveal.DelayedRevealNothingToReveal.selector)); - sigdrop.reveal(0, "key"); - - vm.stopPrank(); - } - - /* - * note: Testing state changes; revealing URI with an incorrect key. - */ - function test_revert_reveal_incorrectKey() public { - vm.startPrank(deployerSigner); - - bytes memory key = "key"; - bytes memory encryptedURI = sigdrop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - sigdrop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.expectRevert(); - string memory revealedURI = sigdrop.reveal(0, "keyy"); - - vm.stopPrank(); - } - - /** - * note: Testing event emission; TokenURIRevealed. - */ - function test_event_reveal_TokenURIRevealed() public { - vm.startPrank(deployerSigner); - - bytes memory key = "key"; - bytes memory encryptedURI = sigdrop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - sigdrop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.expectEmit(true, false, false, true); - emit TokenURIRevealed(0, "ipfs://"); - sigdrop.reveal(0, "key"); - - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Signature Mint Tests - //////////////////////////////////////////////////////////////*/ - - function signMintRequest( - SignatureDrop.MintRequest memory mintrequest, - uint256 privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - mintrequest.to, - mintrequest.royaltyRecipient, - mintrequest.royaltyBps, - mintrequest.primarySaleRecipient, - keccak256(bytes(mintrequest.uri)), - mintrequest.quantity, - mintrequest.pricePerToken, - mintrequest.currency, - mintrequest.validityStartTimestamp, - mintrequest.validityEndTimestamp, - mintrequest.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); - bytes memory signature = abi.encodePacked(r, s, v); - - return signature; - } - - /* - * note: Testing state changes; minting with signature, for a given price and currency. - */ - function test_state_mintWithSignature() public { - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - uint256 id = 0; - SignatureDrop.MintRequest memory mintrequest; - - mintrequest.to = address(0x567); - mintrequest.royaltyRecipient = address(2); - mintrequest.royaltyBps = 0; - mintrequest.primarySaleRecipient = address(deployer); - mintrequest.uri = "ipfs://"; - mintrequest.quantity = 1; - mintrequest.pricePerToken = 1; - mintrequest.currency = address(erc20); - mintrequest.validityStartTimestamp = 1000; - mintrequest.validityEndTimestamp = 2000; - mintrequest.uid = bytes32(id); - - // Test with ERC20 currency - { - uint256 totalSupplyBefore = sigdrop.totalSupply(); - - bytes memory signature = signMintRequest(mintrequest, privateKey); - vm.startPrank(deployerSigner); - vm.warp(1000); - erc20.approve(address(sigdrop), 1); - vm.expectEmit(true, true, true, false); - emit TokensMintedWithSignature(deployerSigner, address(0x567), 0, mintrequest); - sigdrop.mintWithSignature(mintrequest, signature); - vm.stopPrank(); - - assertEq(totalSupplyBefore + mintrequest.quantity, sigdrop.totalSupply()); - } - - // Test with native token currency - { - uint256 totalSupplyBefore = sigdrop.totalSupply(); - - mintrequest.currency = address(NATIVE_TOKEN); - id = 1; - mintrequest.uid = bytes32(id); - - bytes memory signature = signMintRequest(mintrequest, privateKey); - vm.startPrank(address(deployerSigner)); - vm.warp(1000); - sigdrop.mintWithSignature{ value: mintrequest.pricePerToken }(mintrequest, signature); - vm.stopPrank(); - - assertEq(totalSupplyBefore + mintrequest.quantity, sigdrop.totalSupply()); - } - } - - /* - * note: Testing state changes; minting with signature, for a given price and currency. - */ - function test_state_mintWithSignature_UpdateRoyaltyAndSaleInfo() public { - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - uint256 id = 0; - SignatureDrop.MintRequest memory mintrequest; - - mintrequest.to = address(0x567); - mintrequest.royaltyRecipient = address(0x567); - mintrequest.royaltyBps = 100; - mintrequest.primarySaleRecipient = address(0x567); - mintrequest.uri = "ipfs://"; - mintrequest.quantity = 1; - mintrequest.pricePerToken = 1 ether; - mintrequest.currency = address(erc20); - mintrequest.validityStartTimestamp = 1000; - mintrequest.validityEndTimestamp = 2000; - mintrequest.uid = bytes32(id); - - // Test with ERC20 currency - { - erc20.mint(address(0x345), 1 ether); - uint256 totalSupplyBefore = sigdrop.totalSupply(); - - bytes memory signature = signMintRequest(mintrequest, privateKey); - vm.startPrank(address(0x345)); - vm.warp(1000); - erc20.approve(address(sigdrop), 1 ether); - vm.expectEmit(true, true, true, true); - emit TokensMintedWithSignature(deployerSigner, address(0x567), 0, mintrequest); - sigdrop.mintWithSignature(mintrequest, signature); - vm.stopPrank(); - - assertEq(totalSupplyBefore + mintrequest.quantity, sigdrop.totalSupply()); - - (address _royaltyRecipient, uint16 _royaltyBps) = sigdrop.getRoyaltyInfoForToken(0); - assertEq(_royaltyRecipient, address(0x567)); - assertEq(_royaltyBps, 100); - - uint256 totalPrice = 1 * 1 ether; - uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS; - assertEq(erc20.balanceOf(address(0x567)), totalPrice - platformFees); - } - - // Test with native token currency - { - vm.deal(address(0x345), 1 ether); - uint256 totalSupplyBefore = sigdrop.totalSupply(); - - mintrequest.currency = address(NATIVE_TOKEN); - id = 1; - mintrequest.uid = bytes32(id); - - bytes memory signature = signMintRequest(mintrequest, privateKey); - vm.startPrank(address(0x345)); - vm.warp(1000); - sigdrop.mintWithSignature{ value: mintrequest.pricePerToken }(mintrequest, signature); - vm.stopPrank(); - - assertEq(totalSupplyBefore + mintrequest.quantity, sigdrop.totalSupply()); - - (address _royaltyRecipient, uint16 _royaltyBps) = sigdrop.getRoyaltyInfoForToken(0); - assertEq(_royaltyRecipient, address(0x567)); - assertEq(_royaltyBps, 100); - - uint256 totalPrice = 1 * 1 ether; - uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS; - assertEq(address(0x567).balance, totalPrice - platformFees); - } - } - - /** - * note: Testing revert condition; invalid signature. - */ - function test_revert_mintWithSignature_unapprovedSigner() public { - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - uint256 id = 0; - - SignatureDrop.MintRequest memory mintrequest; - mintrequest.to = address(0x567); - mintrequest.royaltyRecipient = address(2); - mintrequest.royaltyBps = 0; - mintrequest.primarySaleRecipient = address(deployer); - mintrequest.uri = "ipfs://"; - mintrequest.quantity = 1; - mintrequest.pricePerToken = 0; - mintrequest.currency = address(3); - mintrequest.validityStartTimestamp = 1000; - mintrequest.validityEndTimestamp = 2000; - mintrequest.uid = bytes32(id); - - bytes memory signature = signMintRequest(mintrequest, privateKey); - vm.warp(1000); - vm.prank(deployerSigner); - sigdrop.mintWithSignature(mintrequest, signature); - - signature = signMintRequest(mintrequest, 4321); - vm.expectRevert(abi.encodeWithSelector(SignatureMintERC721.SignatureMintInvalidSigner.selector)); - sigdrop.mintWithSignature(mintrequest, signature); - } - - /** - * note: Testing revert condition; minting zero tokens. - */ - function test_revert_mintWithSignature_zeroQuantity() public { - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - uint256 id = 0; - - SignatureDrop.MintRequest memory mintrequest; - mintrequest.to = address(0x567); - mintrequest.royaltyRecipient = address(2); - mintrequest.royaltyBps = 0; - mintrequest.primarySaleRecipient = address(deployer); - mintrequest.uri = "ipfs://"; - mintrequest.quantity = 0; - mintrequest.pricePerToken = 0; - mintrequest.currency = address(3); - mintrequest.validityStartTimestamp = 1000; - mintrequest.validityEndTimestamp = 2000; - mintrequest.uid = bytes32(id); - - bytes memory signature = signMintRequest(mintrequest, privateKey); - vm.warp(1000); - - vm.prank(deployerSigner); - vm.expectRevert(abi.encodeWithSelector(SignatureMintERC721.SignatureMintInvalidQuantity.selector)); - sigdrop.mintWithSignature(mintrequest, signature); - } - - /** - * note: Testing revert condition; not enough minted tokens. - */ - function test_revert_mintWithSignature_notEnoughMintedTokens() public { - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - uint256 id = 0; - - SignatureDrop.MintRequest memory mintrequest; - mintrequest.to = address(0); - mintrequest.royaltyRecipient = address(2); - mintrequest.royaltyBps = 0; - mintrequest.primarySaleRecipient = address(deployer); - mintrequest.uri = "ipfs://"; - mintrequest.quantity = 101; - mintrequest.pricePerToken = 0; - mintrequest.currency = address(3); - mintrequest.validityStartTimestamp = 1000; - mintrequest.validityEndTimestamp = 2000; - mintrequest.uid = bytes32(id); - - bytes memory signature = signMintRequest(mintrequest, privateKey); - vm.warp(1000); - vm.expectRevert("!Tokens"); - sigdrop.mintWithSignature(mintrequest, signature); - } - - /** - * note: Testing revert condition; sent value is not equal to price. - */ - function test_revert_mintWithSignature_notSentAmountRequired() public { - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - uint256 id = 0; - SignatureDrop.MintRequest memory mintrequest; - - mintrequest.to = address(0x567); - mintrequest.royaltyRecipient = address(2); - mintrequest.royaltyBps = 0; - mintrequest.primarySaleRecipient = address(deployer); - mintrequest.uri = "ipfs://"; - mintrequest.quantity = 1; - mintrequest.pricePerToken = 1; - mintrequest.currency = address(3); - mintrequest.validityStartTimestamp = 1000; - mintrequest.validityEndTimestamp = 2000; - mintrequest.uid = bytes32(id); - { - mintrequest.currency = address(NATIVE_TOKEN); - bytes memory signature = signMintRequest(mintrequest, privateKey); - vm.startPrank(address(deployerSigner)); - vm.warp(mintrequest.validityStartTimestamp); - vm.expectRevert("!Price"); - sigdrop.mintWithSignature{ value: 2 }(mintrequest, signature); - vm.stopPrank(); - } - } - - /** - * note: Testing token balances; checking balance and owner of tokens after minting with signature. - */ - function test_balances_mintWithSignature() public { - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - uint256 id = 0; - SignatureDrop.MintRequest memory mintrequest; - - mintrequest.to = address(0x567); - mintrequest.royaltyRecipient = address(2); - mintrequest.royaltyBps = 0; - mintrequest.primarySaleRecipient = address(deployer); - mintrequest.uri = "ipfs://"; - mintrequest.quantity = 1; - mintrequest.pricePerToken = 1; - mintrequest.currency = address(erc20); - mintrequest.validityStartTimestamp = 1000; - mintrequest.validityEndTimestamp = 2000; - mintrequest.uid = bytes32(id); - - { - uint256 currencyBalBefore = erc20.balanceOf(deployerSigner); - - bytes memory signature = signMintRequest(mintrequest, privateKey); - vm.startPrank(deployerSigner); - vm.warp(1000); - erc20.approve(address(sigdrop), 1); - sigdrop.mintWithSignature(mintrequest, signature); - vm.stopPrank(); - - uint256 balance = sigdrop.balanceOf(address(0x567)); - assertEq(balance, 1); - - address owner = sigdrop.ownerOf(0); - assertEq(address(0x567), owner); - - assertEq( - currencyBalBefore - mintrequest.pricePerToken * mintrequest.quantity, - erc20.balanceOf(deployerSigner) - ); - - vm.expectRevert(abi.encodeWithSelector(IERC721AUpgradeable.OwnerQueryForNonexistentToken.selector)); - owner = sigdrop.ownerOf(1); - } - } - - /* - * note: Testing state changes; minting with signature, for a given price and currency. - */ - function mintWithSignature(SignatureDrop.MintRequest memory mintrequest) internal { - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - uint256 id = 0; - - { - bytes memory signature = signMintRequest(mintrequest, privateKey); - vm.startPrank(deployerSigner); - vm.warp(mintrequest.validityStartTimestamp); - erc20.approve(address(sigdrop), 1); - sigdrop.mintWithSignature(mintrequest, signature); - vm.stopPrank(); - } - - { - mintrequest.currency = address(NATIVE_TOKEN); - id = 1; - mintrequest.uid = bytes32(id); - bytes memory signature = signMintRequest(mintrequest, privateKey); - vm.startPrank(address(deployerSigner)); - vm.warp(mintrequest.validityStartTimestamp); - sigdrop.mintWithSignature{ value: mintrequest.pricePerToken }(mintrequest, signature); - vm.stopPrank(); - } - } - - function test_fuzz_mintWithSignature(uint128 x, uint128 y) public { - if (x < y) { - uint256 id = 0; - SignatureDrop.MintRequest memory mintrequest; - - mintrequest.to = address(0x567); - mintrequest.royaltyRecipient = address(2); - mintrequest.royaltyBps = 0; - mintrequest.primarySaleRecipient = address(deployer); - mintrequest.uri = "ipfs://"; - mintrequest.quantity = 1; - mintrequest.pricePerToken = 1; - mintrequest.currency = address(erc20); - mintrequest.validityStartTimestamp = x; - mintrequest.validityEndTimestamp = y; - mintrequest.uid = bytes32(id); - - mintWithSignature(mintrequest); - } - } - - /*/////////////////////////////////////////////////////////////// - Claim Tests - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing revert condition; not enough minted tokens. - */ - function test_revert_claimCondition_notEnoughMintedTokens() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - SignatureDrop.AllowlistProof memory alp; - alp.proof = proofs; - - SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - vm.prank(deployerSigner); - sigdrop.setClaimConditions(conditions[0], false); - - vm.expectRevert("!Tokens"); - vm.prank(getActor(6), getActor(6)); - sigdrop.claim(receiver, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing revert condition; exceed max claimable supply. - */ - function test_revert_claimCondition_exceedMaxClaimableSupply() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - SignatureDrop.AllowlistProof memory alp; - alp.proof = proofs; - - SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployerSigner); - sigdrop.lazyMint(200, "ipfs://", emptyEncodedBytes); - vm.prank(deployerSigner); - sigdrop.setClaimConditions(conditions[0], false); - - vm.prank(getActor(5), getActor(5)); - sigdrop.claim(receiver, 100, address(0), 0, alp, ""); - - vm.expectRevert( - abi.encodeWithSelector( - DropSinglePhase.DropClaimExceedMaxSupply.selector, - conditions[0].maxClaimableSupply, - 101 - ) - ); - vm.prank(getActor(6), getActor(6)); - sigdrop.claim(receiver, 1, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction when no allowlist present. - */ - function test_fuzz_claim_noAllowlist(uint256 x) public { - vm.assume(x != 0); - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - SignatureDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - - SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployerSigner); - sigdrop.lazyMint(500, "ipfs://", emptyEncodedBytes); - - vm.prank(deployerSigner); - sigdrop.setClaimConditions(conditions[0], false); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector( - DropSinglePhase.DropClaimExceedLimit.selector, - conditions[0].quantityLimitPerWallet, - 101 - ) - ); - sigdrop.claim(receiver, 101, address(0), 0, alp, ""); - - vm.prank(deployerSigner); - sigdrop.setClaimConditions(conditions[0], true); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector( - DropSinglePhase.DropClaimExceedLimit.selector, - conditions[0].quantityLimitPerWallet, - 101 - ) - ); - sigdrop.claim(receiver, 101, address(0), 0, alp, ""); - } - - function test_fuzz_claim_merkleProof(uint256 x) public { - vm.assume(x > 10 && x < 500); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(x); - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - SignatureDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - alp.pricePerToken = 0; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - // bytes32[] memory proofs = new bytes32[](0); - - SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = x; - conditions[0].quantityLimitPerWallet = 1; - conditions[0].merkleRoot = root; - - vm.prank(deployerSigner); - sigdrop.lazyMint(2 * x, "ipfs://", emptyEncodedBytes); - vm.prank(deployerSigner); - sigdrop.setClaimConditions(conditions[0], false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - sigdrop.claim(receiver, x - 5, address(0), 0, alp, ""); - assertEq(sigdrop.getSupplyClaimedByWallet(receiver), x - 5); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(DropSinglePhase.DropClaimExceedLimit.selector, x, x + 1)); - sigdrop.claim(receiver, 6, address(0), 0, alp, ""); - - vm.prank(receiver, receiver); - sigdrop.claim(receiver, 5, address(0), 0, alp, ""); - assertEq(sigdrop.getSupplyClaimedByWallet(receiver), x); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(DropSinglePhase.DropClaimExceedLimit.selector, x, x + 5)); - sigdrop.claim(receiver, 5, address(0), 0, alp, ""); - } - - /** - * note: Testing state changes; reset eligibility of claim conditions and claiming again for same condition id. - */ - function test_state_claimCondition_resetEligibility() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - SignatureDrop.AllowlistProof memory alp; - alp.proof = proofs; - - SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.prank(deployerSigner); - sigdrop.setClaimConditions(conditions[0], false); - - vm.prank(getActor(5), getActor(5)); - sigdrop.claim(receiver, 1, address(0), 0, alp, ""); - - vm.prank(deployerSigner); - sigdrop.setClaimConditions(conditions[0], true); - - vm.prank(getActor(5), getActor(5)); - sigdrop.claim(receiver, 1, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_delayedReveal_withNewLazyMintedEmptyBatch() public { - vm.startPrank(deployerSigner); - - bytes memory encryptedURI = sigdrop.encryptDecrypt("ipfs://", "key"); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", "key", block.chainid)); - sigdrop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - sigdrop.reveal(0, "key"); - - string memory uri = sigdrop.tokenURI(1); - assertEq(uri, string(abi.encodePacked("ipfs://", "1"))); - - bytes memory newEncryptedURI = sigdrop.encryptDecrypt("ipfs://secret", "key"); - vm.expectRevert(abi.encodeWithSelector(LazyMint.LazyMintInvalidAmount.selector)); - sigdrop.lazyMint(0, "", abi.encode(newEncryptedURI, provenanceHash)); - - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Reentrancy related Tests - //////////////////////////////////////////////////////////////*/ - - function test_revert_reentrancy_mintWithSignature() public { - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - uint256 id = 0; - SignatureDrop.MintRequest memory mintrequest; - - mintrequest.to = address(0); - mintrequest.royaltyRecipient = address(2); - mintrequest.royaltyBps = 0; - mintrequest.primarySaleRecipient = address(deployer); - mintrequest.uri = "ipfs://"; - mintrequest.quantity = 1; - mintrequest.pricePerToken = 1; - mintrequest.currency = address(NATIVE_TOKEN); - mintrequest.validityStartTimestamp = 1000; - mintrequest.validityEndTimestamp = 2000; - mintrequest.uid = bytes32(id); - - // Test with native token currency - { - uint256 totalSupplyBefore = sigdrop.totalSupply(); - - mintrequest.uid = bytes32(id); - bytes memory signature = signMintRequest(mintrequest, privateKey); - - MaliciousReceiver mal = new MaliciousReceiver(address(sigdrop)); - vm.deal(address(mal), 100 ether); - vm.warp(1000); - vm.expectRevert(); - mal.attackMintWithSignature(mintrequest, signature, false); - } - } - - function test_revert_reentrancy_claim() public { - vm.warp(1); - bytes32[] memory proofs = new bytes32[](0); - - SignatureDrop.AllowlistProof memory alp; - alp.proof = proofs; - - SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.prank(deployerSigner); - sigdrop.setClaimConditions(conditions[0], false); - - MaliciousReceiver mal = new MaliciousReceiver(address(sigdrop)); - vm.deal(address(mal), 100 ether); - vm.expectRevert(); - mal.attackClaim(alp, false); - } - - function test_revert_combination_signatureAndClaim() public { - vm.warp(1); - bytes32[] memory proofs = new bytes32[](0); - - SignatureDrop.AllowlistProof memory alp; - alp.proof = proofs; - - SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - vm.prank(deployerSigner); - sigdrop.setClaimConditions(conditions[0], false); - - uint256 id = 0; - SignatureDrop.MintRequest memory mintrequest; - - mintrequest.to = address(0); - mintrequest.royaltyRecipient = address(2); - mintrequest.royaltyBps = 0; - mintrequest.primarySaleRecipient = address(deployer); - mintrequest.uri = "ipfs://"; - mintrequest.quantity = 1; - mintrequest.pricePerToken = 1; - mintrequest.currency = address(NATIVE_TOKEN); - mintrequest.validityStartTimestamp = 1000; - mintrequest.validityEndTimestamp = 2000; - mintrequest.uid = bytes32(id); - - // Test with native token currency - { - uint256 totalSupplyBefore = sigdrop.totalSupply(); - - mintrequest.uid = bytes32(id); - bytes memory signature = signMintRequest(mintrequest, privateKey); - - MaliciousReceiver mal = new MaliciousReceiver(address(sigdrop)); - vm.deal(address(mal), 100 ether); - vm.warp(1000); - mal.saveCombination(mintrequest, signature, alp); - vm.expectRevert(); - mal.attackMintWithSignature(mintrequest, signature, true); - // mal.attackClaim(alp, true); - } - } -} - -contract MaliciousReceiver { - SignatureDrop public sigdrop; - - SignatureDrop.MintRequest public mintrequest; - SignatureDrop.AllowlistProof public alp; - bytes public signature; - bool public claim; - bool public loop = true; - - constructor(address _sigdrop) { - sigdrop = SignatureDrop(_sigdrop); - } - - function attackMintWithSignature( - SignatureDrop.MintRequest calldata _mintrequest, - bytes calldata _signature, - bool swap - ) external { - claim = swap; - mintrequest = _mintrequest; - signature = _signature; - sigdrop.mintWithSignature{ value: _mintrequest.pricePerToken }(_mintrequest, _signature); - } - - function attackClaim(SignatureDrop.AllowlistProof calldata _alp, bool swap) external { - claim = !swap; - alp = _alp; - sigdrop.claim(address(this), 1, address(0), 0, _alp, ""); - } - - function saveCombination( - SignatureDrop.MintRequest calldata _mintrequest, - bytes calldata _signature, - SignatureDrop.AllowlistProof calldata _alp - ) external { - mintrequest = _mintrequest; - signature = _signature; - alp = _alp; - } - - function onERC721Received(address, address, uint256, bytes calldata) external returns (bytes4) { - if (claim && loop) { - loop = false; - claim = false; - sigdrop.claim(address(this), 1, address(0), 0, alp, ""); - } else if (!claim && loop) { - loop = false; - sigdrop.mintWithSignature{ value: mintrequest.pricePerToken }(mintrequest, signature); - } - return this.onERC721Received.selector; - } -} diff --git a/src/test/TWFactory.t.sol b/src/test/TWFactory.t.sol deleted file mode 100644 index 474286352..000000000 --- a/src/test/TWFactory.t.sol +++ /dev/null @@ -1,326 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -// Test imports -import "./utils/BaseTest.sol"; -import { TWFactory } from "contracts/infra/TWFactory.sol"; -import { TWRegistry } from "contracts/infra/TWRegistry.sol"; - -// Helpers -import "@openzeppelin/contracts/utils/Create2.sol"; -import "@openzeppelin/contracts/proxy/Clones.sol"; -import "contracts/infra/TWProxy.sol"; -// import "./utils/Console.sol"; -import "./mocks/MockThirdwebContract.sol"; - -interface ITWFactoryData { - event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer); - event ImplementationAdded(address implementation, bytes32 indexed contractType, uint256 version); - event ImplementationApproved(address implementation, bool isApproved); -} - -contract TWFactoryTest is ITWFactoryData, BaseTest { - // Target contract - TWFactory internal _factory; - - // Actors - address internal proxyDeployer; - address internal proxyDeployer2; - - // Test params - MockThirdwebContract internal mockModule; - address internal mockUnapprovedImplementation = address(0x5); - - // ===== Set up ===== - - function setUp() public override { - super.setUp(); - - _factory = TWFactory(factory); - proxyDeployer = getActor(10); - proxyDeployer2 = getActor(11); - - vm.prank(factoryAdmin); - mockModule = new MockThirdwebContract(); - } - - // ===== Initial state ===== - - /** - * @dev Tests the relevant initial state of the contract. - * - * - Deployer of the contract has `FACTORY_ROLE` - */ - function test_initialState() public { - assertTrue(_factory.hasRole(_factory.FACTORY_ROLE(), factoryAdmin)); - } - - // ===== Functionality tests ===== - - /// @dev Test `addImplementation` - - function test_addImplementation() public { - bytes32 contractType = mockModule.contractType(); - uint256 moduleVersion = mockModule.contractVersion(); - uint256 moduleVersionOnFactory = _factory.currentVersion(contractType); - - vm.prank(factoryAdmin); - _factory.addImplementation(address(mockModule)); - - assertTrue(_factory.approval(address(mockModule))); - assertEq(address(mockModule), _factory.implementation(contractType, moduleVersion)); - assertEq(_factory.currentVersion(contractType), moduleVersionOnFactory + 1); - assertEq(_factory.getImplementation(contractType, moduleVersion), address(mockModule)); - } - - function test_addImplementation_directV2() public { - MockThirdwebContractV2 mockModuleV2 = new MockThirdwebContractV2(); - - bytes32 contractType = mockModuleV2.contractType(); - uint256 moduleVersion = mockModuleV2.contractVersion(); - uint256 moduleVersionOnFactory = _factory.currentVersion(contractType); - - vm.prank(factoryAdmin); - _factory.addImplementation(address(mockModuleV2)); - - assertTrue(_factory.approval(address(mockModuleV2))); - assertEq(address(mockModuleV2), _factory.implementation(contractType, moduleVersion)); - assertEq(_factory.currentVersion(contractType), moduleVersionOnFactory + 2); - assertEq(_factory.getImplementation(contractType, moduleVersion), address(mockModuleV2)); - } - - function test_addImplementation_newImpl() public { - vm.prank(factoryAdmin); - _factory.addImplementation(address(mockModule)); - - MockThirdwebContractV2 mockModuleV2 = new MockThirdwebContractV2(); - - bytes32 contractType = mockModuleV2.contractType(); - uint256 moduleVersion = mockModuleV2.contractVersion(); - uint256 moduleVersionOnFactory = _factory.currentVersion(contractType); - - vm.prank(factoryAdmin); - _factory.addImplementation(address(mockModuleV2)); - - assertTrue(_factory.approval(address(mockModuleV2))); - assertEq(address(mockModuleV2), _factory.implementation(contractType, moduleVersion)); - assertEq(_factory.currentVersion(contractType), moduleVersionOnFactory + 1); - assertEq(_factory.getImplementation(contractType, moduleVersion), address(mockModuleV2)); - } - - function test_addImplementation_revert_invalidCaller() public { - vm.expectRevert("not admin."); - - vm.prank(proxyDeployer); - _factory.addImplementation(address(mockModule)); - } - - function test_addImplementation_emit_ImplementationAdded() public { - bytes32 contractType = mockModule.contractType(); - uint256 moduleVersion = mockModule.contractVersion(); - - vm.expectEmit(true, false, false, true); - emit ImplementationAdded(address(mockModule), contractType, moduleVersion); - - vm.prank(factoryAdmin); - _factory.addImplementation(address(mockModule)); - } - - /// @dev Test `approveImplementation` - - function test_approveImplementation() public { - assertTrue(_factory.approval(address(mockModule)) == false); - assertTrue(_factory.currentVersion(mockModule.contractType()) == 0); - - vm.prank(factoryAdmin); - _factory.approveImplementation(address(mockModule), true); - - assertTrue(_factory.approval(address(mockModule))); - assertTrue(_factory.currentVersion(mockModule.contractType()) == 0); - } - - function test_approveImplementation_revert_invalidCaller() public { - vm.expectRevert("not admin."); - - vm.prank(proxyDeployer); - _factory.approveImplementation(address(mockModule), true); - } - - function test_approveImplementation_emit_ImplementationApproved() public { - vm.expectEmit(false, false, false, true); - emit ImplementationApproved(address(mockModule), true); - - vm.prank(factoryAdmin); - _factory.approveImplementation(address(mockModule), true); - } - - /// @dev Test `deployProxyByImplementation` - - function setUp_deployProxyByImplementation() internal { - vm.prank(factoryAdmin); - _factory.approveImplementation(address(mockModule), true); - } - - function test_deployProxyByImplementation(bytes32 _salt) public { - setUp_deployProxyByImplementation(); - - address computedProxyAddr = Clones.predictDeterministicAddress( - address(mockModule), - keccak256(abi.encodePacked(proxyDeployer, _salt)), - factory - ); - - vm.prank(proxyDeployer); - address deployedAddr = _factory.deployProxyByImplementation(address(mockModule), "", _salt); - - assertEq(deployedAddr, computedProxyAddr); - assertEq(mockModule.contractType(), MockThirdwebContract(computedProxyAddr).contractType()); - } - - function test_deployProxyByImplementation_revert_invalidImpl() public { - vm.expectRevert("implementation not approved"); - - vm.prank(proxyDeployer); - _factory.deployProxyByImplementation(address(mockModule), "", ""); - } - - function skiptest_deployProxyByImplementation_emit_ProxyDeployed() public { - setUp_deployProxyByImplementation(); - - bytes32 salt = bytes32("Random"); - address computedProxyAddr = Clones.predictDeterministicAddress( - address(mockModule), - keccak256(abi.encodePacked(proxyDeployer, salt)), - factory - ); - - vm.expectEmit(true, true, false, true); - emit ProxyDeployed(address(mockModule), computedProxyAddr, proxyDeployer); - - vm.prank(proxyDeployer); - _factory.deployProxyByImplementation(address(mockModule), "", salt); - } - - /// @dev Test `deployProxyDeterministic` - - function setUp_deployProxyDeterministic() internal { - vm.prank(factoryAdmin); - _factory.addImplementation(address(mockModule)); - } - - function test_deployProxyDeterministic(bytes32 _salt) public { - setUp_deployProxyDeterministic(); - - bytes32 contractType = mockModule.contractType(); - - address computedProxyAddr = Clones.predictDeterministicAddress( - address(mockModule), - keccak256(abi.encodePacked(proxyDeployer, _salt)), - factory - ); - - vm.prank(proxyDeployer); - address proxyAddr = _factory.deployProxyDeterministic(contractType, "", _salt); - - assertEq(proxyAddr, computedProxyAddr); - assertEq(mockModule.contractType(), MockThirdwebContract(computedProxyAddr).contractType()); - } - - function test_deployProxyDeterministic_revert_invalidImpl(bytes32 _salt) public { - bytes32 contractType = mockModule.contractType(); - - vm.expectRevert("implementation not approved"); - - vm.prank(proxyDeployer); - _factory.deployProxyDeterministic(contractType, "", _salt); - } - - function skiptest_deployProxyDeterministic_emit_ProxyDeployed() public { - setUp_deployProxyDeterministic(); - - bytes32 contractType = mockModule.contractType(); - - bytes32 salt = bytes32("Random"); - bytes memory proxyBytecode = abi.encodePacked(type(TWProxy).creationCode, abi.encode(address(mockModule), "")); - address computedProxyAddr = Create2.computeAddress(salt, keccak256(proxyBytecode), address(_factory)); - - vm.expectEmit(true, true, false, true); - emit ProxyDeployed(address(mockModule), computedProxyAddr, proxyDeployer); - - vm.prank(proxyDeployer); - _factory.deployProxyDeterministic(contractType, "", salt); - } - - /// @dev Test `deployProxy` - - function setUp_deployProxy() internal { - vm.prank(factoryAdmin); - _factory.addImplementation(address(mockModule)); - } - - function test_deployProxy() public { - setUp_deployProxy(); - - bytes32 contractType = mockModule.contractType(); - - vm.prank(proxyDeployer); - address proxyAddr = _factory.deployProxy(contractType, ""); - - assertEq(mockModule.contractType(), MockThirdwebContract(proxyAddr).contractType()); - } - - function test_deployProxy_sameBlock() public { - setUp_deployProxy(); - - bytes32 contractType = mockModule.contractType(); - - vm.startPrank(proxyDeployer); - address proxyAddr = _factory.deployProxy(contractType, ""); - address proxyAddr2 = _factory.deployProxy(contractType, ""); - - assertTrue(proxyAddr != proxyAddr2); - assertEq(mockModule.contractType(), MockThirdwebContract(proxyAddr).contractType()); - } - - function test_deployProxy_revert_invalidImpl() public { - bytes32 contractType = mockModule.contractType(); - - vm.expectRevert("implementation not approved"); - - vm.prank(proxyDeployer); - _factory.deployProxy(contractType, ""); - } - - function skiptest_deployProxy_emit_ProxyDeployed() public { - setUp_deployProxy(); - - bytes32 contractType = mockModule.contractType(); - - bytes32 salt = keccak256(abi.encodePacked(contractType, block.number)); - bytes memory proxyBytecode = abi.encodePacked(type(TWProxy).creationCode, abi.encode(address(mockModule), "")); - address computedProxyAddr = Create2.computeAddress(salt, keccak256(proxyBytecode), address(_factory)); - - vm.expectEmit(true, true, false, true); - emit ProxyDeployed(address(mockModule), computedProxyAddr, proxyDeployer); - - vm.prank(proxyDeployer); - _factory.deployProxy(contractType, ""); - } - - /** - * ===== Attack vectors ===== - * - * - No proxy should be able to point to an unapproved implementation. - * - No non-admin should be able to approve an implementation. - **/ - function testNoNonAdmin(address _implementation, address _deployer) public { - bool toApprove = true; - - if (!_factory.hasRole(_factory.FACTORY_ROLE(), _deployer)) { - vm.expectRevert("not admin."); - - vm.prank(_deployer); - _factory.approveImplementation(_implementation, toApprove); - } - } -} diff --git a/src/test/TWMultichainRegistry.t.sol b/src/test/TWMultichainRegistry.t.sol deleted file mode 100644 index 374148b3c..000000000 --- a/src/test/TWMultichainRegistry.t.sol +++ /dev/null @@ -1,197 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -// Test imports -import "./utils/BaseTest.sol"; -import "contracts/infra/interface/ITWMultichainRegistry.sol"; -import { TWMultichainRegistry } from "contracts/infra/TWMultichainRegistry.sol"; -import "./mocks/MockThirdwebContract.sol"; -import "contracts/extension/interface/plugin/IPluginMap.sol"; - -interface ITWMultichainRegistryData { - event Added(address indexed deployer, address indexed moduleAddress, uint256 indexed chainid, string metadataUri); - event Deleted(address indexed deployer, address indexed moduleAddress, uint256 indexed chainid); -} - -contract TWMultichainRegistryTest is ITWMultichainRegistryData, BaseTest { - // Target contract - TWMultichainRegistry internal _registry; - - // Test params - address internal factoryAdmin_; - address internal factory_; - - uint256[] internal chainIds; - address[] internal deploymentAddresses; - address internal deployer_; - - uint256 total = 1000; - - // ===== Set up ===== - - function setUp() public override { - super.setUp(); - - deployer_ = getActor(100); - factory_ = getActor(101); - factoryAdmin_ = getActor(102); - - for (uint256 i = 0; i < total; i += 1) { - chainIds.push(i); - vm.prank(deployer_); - address depl = address(new MockThirdwebContract()); - deploymentAddresses.push(depl); - } - - vm.startPrank(factoryAdmin_); - _registry = new TWMultichainRegistry(forwarders()); - - _registry.grantRole(keccak256("OPERATOR_ROLE"), factory_); - - vm.stopPrank(); - } - - function test_interfaceId() public pure { - console2.logBytes4(type(IPluginMap).interfaceId); - } - - // ===== Functionality tests ===== - - /// @dev Test `add` - - function test_addFromFactory() public { - vm.startPrank(factory_); - for (uint256 i = 0; i < total; i += 1) { - _registry.add(deployer_, deploymentAddresses[i], chainIds[i], ""); - } - vm.stopPrank(); - - ITWMultichainRegistry.Deployment[] memory modules = _registry.getAll(deployer_); - - assertEq(modules.length, total); - assertEq(_registry.count(deployer_), total); - - for (uint256 i = 0; i < total; i += 1) { - assertEq(modules[i].deploymentAddress, deploymentAddresses[i]); - assertEq(modules[i].chainId, chainIds[i]); - } - - vm.prank(factory_); - _registry.add(deployer_, address(0x43), 111, ""); - - modules = _registry.getAll(deployer_); - assertEq(modules.length, total + 1); - assertEq(_registry.count(deployer_), total + 1); - } - - function test_addFromSelf() public { - vm.startPrank(deployer_); - for (uint256 i = 0; i < total; i += 1) { - _registry.add(deployer_, deploymentAddresses[i], chainIds[i], ""); - } - vm.stopPrank(); - - ITWMultichainRegistry.Deployment[] memory modules = _registry.getAll(deployer_); - - assertEq(modules.length, total); - assertEq(_registry.count(deployer_), total); - - for (uint256 i = 0; i < total; i += 1) { - assertEq(modules[i].deploymentAddress, deploymentAddresses[i]); - assertEq(modules[i].chainId, chainIds[i]); - } - - vm.prank(factory_); - _registry.add(deployer_, address(0x43), 111, ""); - - modules = _registry.getAll(deployer_); - assertEq(modules.length, total + 1); - assertEq(_registry.count(deployer_), total + 1); - } - - function test_add_emit_Added() public { - vm.expectEmit(true, true, true, true); - emit Added(deployer_, deploymentAddresses[0], chainIds[0], "uri"); - - vm.prank(factory_); - _registry.add(deployer_, deploymentAddresses[0], chainIds[0], "uri"); - - string memory uri = _registry.getMetadataUri(chainIds[0], deploymentAddresses[0]); - assertEq(uri, "uri"); - } - - // Test `remove` - - function setUp_remove() public { - vm.startPrank(factory_); - for (uint256 i = 0; i < total; i += 1) { - _registry.add(deployer_, deploymentAddresses[i], chainIds[i], ""); - } - vm.stopPrank(); - } - - // ===== Functionality tests ===== - function test_removeFromFactory() public { - setUp_remove(); - vm.prank(factory_); - _registry.remove(deployer_, deploymentAddresses[0], chainIds[0]); - - ITWMultichainRegistry.Deployment[] memory modules = _registry.getAll(deployer_); - assertEq(modules.length, total - 1); - - for (uint256 i = 0; i < total - 1; i += 1) { - assertEq(modules[i].deploymentAddress, deploymentAddresses[i + 1]); - assertEq(modules[i].chainId, chainIds[i + 1]); - } - } - - function test_removeFromSelf() public { - setUp_remove(); - vm.prank(factory_); - _registry.remove(deployer_, deploymentAddresses[0], chainIds[0]); - - ITWMultichainRegistry.Deployment[] memory modules = _registry.getAll(deployer_); - assertEq(modules.length, total - 1); - } - - function test_remove_revert_invalidCaller() public { - setUp_remove(); - address invalidCaller = address(0x123); - assertTrue(invalidCaller != factory_ || invalidCaller != deployer_); - - vm.expectRevert("not operator or deployer."); - - vm.prank(invalidCaller); - _registry.remove(deployer_, deploymentAddresses[0], chainIds[0]); - } - - function test_remove_revert_noModulesToRemove() public { - setUp_remove(); - address actor = getActor(1); - ITWMultichainRegistry.Deployment[] memory modules = _registry.getAll(actor); - assertEq(modules.length, 0); - - vm.expectRevert("failed to remove"); - - vm.prank(actor); - _registry.remove(actor, deploymentAddresses[0], chainIds[0]); - } - - function test_remove_revert_incorrectChainId() public { - setUp_remove(); - - vm.expectRevert("failed to remove"); - - vm.prank(deployer_); - _registry.remove(deployer_, deploymentAddresses[0], 12345); - } - - function test_remove_emit_Deleted() public { - setUp_remove(); - vm.expectEmit(true, true, true, true); - emit Deleted(deployer_, deploymentAddresses[0], chainIds[0]); - - vm.prank(deployer_); - _registry.remove(deployer_, deploymentAddresses[0], chainIds[0]); - } -} diff --git a/src/test/TWRegistry.t.sol b/src/test/TWRegistry.t.sol deleted file mode 100644 index 8d9a8c79f..000000000 --- a/src/test/TWRegistry.t.sol +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -// Test imports -import "./utils/BaseTest.sol"; -import { TWRegistry } from "contracts/infra/TWRegistry.sol"; - -interface ITWRegistryData { - event Added(address indexed deployer, address indexed moduleAddress); - event Deleted(address indexed deployer, address indexed moduleAddress); -} - -contract TWRegistryTest is ITWRegistryData, BaseTest { - // Target contract - TWRegistry internal _registry; - - // Test params - address internal mockModuleAddress = address(0x42); - address internal actor; - - // ===== Set up ===== - - function setUp() public override { - super.setUp(); - actor = getActor(0); - _registry = TWRegistry(registry); - } - - // ===== Functionality tests ===== - - /// @dev Test `add` - - function test_addFromFactory() public { - vm.prank(factory); - _registry.add(actor, mockModuleAddress); - - address[] memory modules = _registry.getAll(actor); - assertEq(modules.length, 1); - assertEq(modules[0], mockModuleAddress); - assertEq(_registry.count(actor), 1); - - vm.prank(factory); - _registry.add(actor, address(0x43)); - - modules = _registry.getAll(actor); - assertEq(modules.length, 2); - assertEq(_registry.count(actor), 2); - } - - function test_addFromSelf() public { - vm.prank(actor); - _registry.add(actor, mockModuleAddress); - - address[] memory modules = _registry.getAll(actor); - - assertEq(modules.length, 1); - assertEq(modules[0], mockModuleAddress); - assertEq(_registry.count(actor), 1); - } - - function test_add_emit_Added() public { - vm.expectEmit(true, true, false, true); - emit Added(actor, mockModuleAddress); - - vm.prank(factory); - _registry.add(actor, mockModuleAddress); - } - - // Test `remove` - - function setUp_remove() public { - vm.prank(factory); - _registry.add(actor, mockModuleAddress); - } - - // ===== Functionality tests ===== - function test_removeFromFactory() public { - setUp_remove(); - vm.prank(factory); - _registry.remove(actor, mockModuleAddress); - - address[] memory modules = _registry.getAll(actor); - assertEq(modules.length, 0); - } - - function test_removeFromSelf() public { - setUp_remove(); - vm.prank(actor); - _registry.remove(actor, mockModuleAddress); - - address[] memory modules = _registry.getAll(actor); - assertEq(modules.length, 0); - } - - function test_remove_revert_invalidCaller() public { - setUp_remove(); - address invalidCaller = address(0x123); - assertTrue(invalidCaller != factory || invalidCaller != actor); - - vm.expectRevert("not operator or deployer."); - - vm.prank(invalidCaller); - _registry.remove(actor, mockModuleAddress); - } - - function test_remove_revert_noModulesToRemove() public { - setUp_remove(); - actor = getActor(1); - address[] memory modules = _registry.getAll(actor); - assertEq(modules.length, 0); - - vm.expectRevert("failed to remove"); - - vm.prank(actor); - _registry.remove(actor, mockModuleAddress); - } - - function test_remove_emit_Deleted() public { - setUp_remove(); - vm.expectEmit(true, true, false, true); - emit Deleted(actor, mockModuleAddress); - - vm.prank(actor); - _registry.remove(actor, mockModuleAddress); - } -} diff --git a/src/test/TieredDrop.t.sol b/src/test/TieredDrop.t.sol deleted file mode 100644 index 151d12ecb..000000000 --- a/src/test/TieredDrop.t.sol +++ /dev/null @@ -1,1103 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "./utils/BaseTest.sol"; - -import { TieredDrop } from "contracts/prebuilts/tiered-drop/TieredDrop.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract TieredDropTest is BaseTest { - using Strings for uint256; - - TieredDrop public tieredDrop; - - address internal dropAdmin; - address internal claimer; - - // Signature params - address internal deployerSigner; - bytes32 internal typehashGenericRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - // Lazy mint variables - uint256 internal quantityTier1 = 10; - string internal tier1 = "tier1"; - string internal baseURITier1 = "baseURI1/"; - string internal placeholderURITier1 = "placeholderURI1/"; - bytes internal keyTier1 = "tier1_key"; - - uint256 internal quantityTier2 = 20; - string internal tier2 = "tier2"; - string internal baseURITier2 = "baseURI2/"; - string internal placeholderURITier2 = "placeholderURI2/"; - bytes internal keyTier2 = "tier2_key"; - - uint256 internal quantityTier3 = 30; - string internal tier3 = "tier3"; - string internal baseURITier3 = "baseURI3/"; - string internal placeholderURITier3 = "placeholderURI3/"; - bytes internal keyTier3 = "tier3_key"; - - function setUp() public virtual override { - super.setUp(); - - dropAdmin = getActor(1); - claimer = getActor(2); - - // Deploy implementation. - address tieredDropImpl = address(new TieredDrop()); - - // Deploy proxy pointing to implementaion. - vm.prank(dropAdmin); - tieredDrop = TieredDrop( - address( - new TWProxy( - tieredDropImpl, - abi.encodeCall( - TieredDrop.initialize, - (dropAdmin, "Tiered Drop", "TD", "ipfs://", new address[](0), dropAdmin, dropAdmin, 0) - ) - ) - ) - ); - - // ====== signature params - - deployerSigner = signer; - vm.prank(dropAdmin); - tieredDrop.grantRole(keccak256("MINTER_ROLE"), deployerSigner); - - typehashGenericRequest = keccak256( - "GenericRequest(uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid,bytes data)" - ); - nameHash = keccak256(bytes("SignatureAction")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tieredDrop)) - ); - - // ====== - } - - TieredDrop.GenericRequest internal claimRequest; - bytes internal claimSignature; - - uint256 internal nonce; - - function _setupClaimSignature(string[] memory _orderedTiers, uint256 _totalQuantity) internal { - claimRequest.validityStartTimestamp = 1000; - claimRequest.validityEndTimestamp = 2000; - claimRequest.uid = keccak256(abi.encodePacked(nonce)); - nonce += 1; - claimRequest.data = abi.encode( - _orderedTiers, - claimer, - address(0), - 0, - dropAdmin, - _totalQuantity, - 0, - NATIVE_TOKEN - ); - - bytes memory encodedRequest = abi.encode( - typehashGenericRequest, - claimRequest.validityStartTimestamp, - claimRequest.validityEndTimestamp, - claimRequest.uid, - keccak256(bytes(claimRequest.data)) - ); - - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); - claimSignature = abi.encodePacked(r, s, v); - } - - //////////////////////////////////////////////// - // // - // lazyMintWithTier tests // - // // - //////////////////////////////////////////////// - - // function test_state_lazyMintWithTier() public { - // // Lazy mint tokens: 3 different tiers - // vm.startPrank(dropAdmin); - - // // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - // tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. - // tieredDrop.lazyMint(quantityTier2, baseURITier2, tier2, ""); - // // Tier 3: tokenIds assigned 30 -> 60 non-inclusive. - // tieredDrop.lazyMint(quantityTier3, baseURITier3, tier3, ""); - - // vm.stopPrank(); - - // TieredDrop.TierMetadata[] memory metadataForAllTiers = tieredDrop.getMetadataForAllTiers(); - // (TieredDrop.TokenRange[] memory tokens_1, string[] memory baseURIs_1) = ( - // metadataForAllTiers[0].ranges, - // metadataForAllTiers[0].baseURIs - // ); - // (TieredDrop.TokenRange[] memory tokens_2, string[] memory baseURIs_2) = ( - // metadataForAllTiers[1].ranges, - // metadataForAllTiers[1].baseURIs - // ); - // (TieredDrop.TokenRange[] memory tokens_3, string[] memory baseURIs_3) = ( - // metadataForAllTiers[2].ranges, - // metadataForAllTiers[2].baseURIs - // ); - - // uint256 cumulativeStart = 0; - - // TieredDrop.TokenRange memory range = tokens_1[0]; - // string memory baseURI = baseURIs_1[0]; - - // assertEq(range.startIdInclusive, cumulativeStart); - // assertEq(range.endIdNonInclusive, cumulativeStart + quantityTier1); - // assertEq(baseURI, baseURITier1); - - // cumulativeStart += quantityTier1; - - // range = tokens_2[0]; - // baseURI = baseURIs_2[0]; - - // assertEq(range.startIdInclusive, cumulativeStart); - // assertEq(range.endIdNonInclusive, cumulativeStart + quantityTier2); - // assertEq(baseURI, baseURITier2); - - // cumulativeStart += quantityTier2; - - // range = tokens_3[0]; - // baseURI = baseURIs_3[0]; - - // assertEq(range.startIdInclusive, cumulativeStart); - // assertEq(range.endIdNonInclusive, cumulativeStart + quantityTier3); - // assertEq(baseURI, baseURITier3); - // } - - // function test_state_lazyMintWithTier_sameTier() public { - // // Lazy mint tokens: 3 different tiers - // vm.startPrank(dropAdmin); - - // // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - // tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. - // tieredDrop.lazyMint(quantityTier2, baseURITier2, tier2, ""); - // // Tier 1 Again: tokenIds assigned 30 -> 60 non-inclusive. - // tieredDrop.lazyMint(quantityTier3, baseURITier3, tier1, ""); - - // TieredDrop.TierMetadata[] memory metadataForAllTiers = tieredDrop.getMetadataForAllTiers(); - // (TieredDrop.TokenRange[] memory tokens_1, string[] memory baseURIs_1) = ( - // metadataForAllTiers[0].ranges, - // metadataForAllTiers[0].baseURIs - // ); - // (TieredDrop.TokenRange[] memory tokens_2, string[] memory baseURIs_2) = ( - // metadataForAllTiers[1].ranges, - // metadataForAllTiers[1].baseURIs - // ); - - // vm.stopPrank(); - - // uint256 cumulativeStart = 0; - - // TieredDrop.TokenRange memory range = tokens_1[0]; - // string memory baseURI = baseURIs_1[0]; - - // assertEq(range.startIdInclusive, cumulativeStart); - // assertEq(range.endIdNonInclusive, cumulativeStart + quantityTier1); - // assertEq(baseURI, baseURITier1); - - // cumulativeStart += quantityTier1; - - // range = tokens_2[0]; - // baseURI = baseURIs_2[0]; - - // assertEq(range.startIdInclusive, cumulativeStart); - // assertEq(range.endIdNonInclusive, cumulativeStart + quantityTier2); - // assertEq(baseURI, baseURITier2); - - // cumulativeStart += quantityTier2; - - // range = tokens_1[1]; - // baseURI = baseURIs_1[1]; - - // assertEq(range.startIdInclusive, cumulativeStart); - // assertEq(range.endIdNonInclusive, cumulativeStart + quantityTier3); - // assertEq(baseURI, baseURITier3); - // } - - function test_revert_lazyMintWithTier_notMinterRole() public { - vm.expectRevert("Not authorized"); - tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - } - - function test_revert_lazyMintWithTier_mintingZeroAmount() public { - vm.prank(dropAdmin); - vm.expectRevert("0 amt"); - tieredDrop.lazyMint(0, baseURITier1, tier1, ""); - } - - //////////////////////////////////////////////// - // // - // claimWithSignature tests // - // // - //////////////////////////////////////////////// - - function test_state_claimWithSignature() public { - // Lazy mint tokens: 3 different tiers - vm.startPrank(dropAdmin); - - // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. - tieredDrop.lazyMint(quantityTier2, baseURITier2, tier2, ""); - // Tier 3: tokenIds assigned 30 -> 60 non-inclusive. - tieredDrop.lazyMint(quantityTier3, baseURITier3, tier3, ""); - - vm.stopPrank(); - - /** - * Claim tokens. - * - Order of priority: [tier2, tier1] - * - Total quantity: 25. [20 from tier2, 5 from tier1] - */ - - string[] memory tiers = new string[](2); - tiers[0] = tier2; - tiers[1] = tier1; - - uint256 claimQuantity = 25; - - _setupClaimSignature(tiers, claimQuantity); - - assertEq(tieredDrop.hasRole(keccak256("MINTER_ROLE"), deployerSigner), true); - - vm.warp(claimRequest.validityStartTimestamp); - vm.prank(claimer); - tieredDrop.claimWithSignature(claimRequest, claimSignature); - - /** - * Check token URIs for tokens of tiers: - * - Tier 2: token IDs 0 -> 19 mapped one-to-one to metadata IDs 10 -> 29 - * - Tier 1: token IDs 20 -> 24 mapped one-to-one to metadata IDs 0 -> 4 - */ - - uint256 tier2Id = 10; - uint256 tier1Id = 0; - - for (uint256 i = 0; i < claimQuantity; i += 1) { - if (i < 20) { - assertEq(tieredDrop.tokenURI(i), string(abi.encodePacked(baseURITier2, tier2Id.toString()))); - tier2Id += 1; - } else { - assertEq(tieredDrop.tokenURI(i), string(abi.encodePacked(baseURITier1, tier1Id.toString()))); - tier1Id += 1; - } - } - } - - function test_revert_claimWithSignature_invalidEncoding() public { - // Lazy mint tokens: 3 different tiers - vm.startPrank(dropAdmin); - - // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. - tieredDrop.lazyMint(quantityTier2, baseURITier2, tier2, ""); - // Tier 3: tokenIds assigned 30 -> 60 non-inclusive. - tieredDrop.lazyMint(quantityTier3, baseURITier3, tier3, ""); - - vm.stopPrank(); - - /** - * Claim tokens. - * - Order of priority: [tier2, tier1] - * - Total quantity: 25. [20 from tier2, 5 from tier1] - */ - - string[] memory tiers = new string[](2); - tiers[0] = tier2; - tiers[1] = tier1; - - uint256 claimQuantity = 25; - - // Create data with invalid encoding. - claimRequest.data = abi.encode(1, ""); - _setupClaimSignature(tiers, claimQuantity); - - claimRequest.data = abi.encode(1, ""); - - assertEq(tieredDrop.hasRole(keccak256("MINTER_ROLE"), deployerSigner), true); - - vm.warp(claimRequest.validityStartTimestamp); - vm.prank(claimer); - vm.expectRevert(); - tieredDrop.claimWithSignature(claimRequest, claimSignature); - } - - function test_revert_claimWithSignature_mintingZeroQuantity() public { - // Lazy mint tokens: 3 different tiers - vm.startPrank(dropAdmin); - - // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. - tieredDrop.lazyMint(quantityTier2, baseURITier2, tier2, ""); - // Tier 3: tokenIds assigned 30 -> 60 non-inclusive. - tieredDrop.lazyMint(quantityTier3, baseURITier3, tier3, ""); - - vm.stopPrank(); - - /** - * Claim tokens. - * - Order of priority: [tier2, tier1] - * - Total quantity: 25. [20 from tier2, 5 from tier1] - */ - - string[] memory tiers = new string[](2); - tiers[0] = tier2; - tiers[1] = tier1; - - uint256 claimQuantity = 0; - - _setupClaimSignature(tiers, claimQuantity); - - assertEq(tieredDrop.hasRole(keccak256("MINTER_ROLE"), deployerSigner), true); - - vm.warp(claimRequest.validityStartTimestamp); - vm.prank(claimer); - vm.expectRevert("0 qty"); - tieredDrop.claimWithSignature(claimRequest, claimSignature); - } - - function test_revert_claimWithSignature_notEnoughLazyMintedTokens() public { - // Lazy mint tokens: 3 different tiers - vm.startPrank(dropAdmin); - - // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. - tieredDrop.lazyMint(quantityTier2, baseURITier2, tier2, ""); - // Tier 3: tokenIds assigned 30 -> 60 non-inclusive. - tieredDrop.lazyMint(quantityTier3, baseURITier3, tier3, ""); - - vm.stopPrank(); - - /** - * Claim tokens. - * - Order of priority: [tier2, tier1] - * - Total quantity: 25. [20 from tier2, 5 from tier1] - */ - - string[] memory tiers = new string[](2); - tiers[0] = tier2; - tiers[1] = tier1; - - uint256 claimQuantity = quantityTier1 + quantityTier2 + quantityTier3 + 1; - - _setupClaimSignature(tiers, claimQuantity); - - assertEq(tieredDrop.hasRole(keccak256("MINTER_ROLE"), deployerSigner), true); - - vm.warp(claimRequest.validityStartTimestamp); - vm.prank(claimer); - vm.expectRevert("!Tokens"); - tieredDrop.claimWithSignature(claimRequest, claimSignature); - } - - function test_revert_claimWithSignature_insufficientTokensInTiers() public { - // Lazy mint tokens: 3 different tiers - vm.startPrank(dropAdmin); - - // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. - tieredDrop.lazyMint(quantityTier2, baseURITier2, tier2, ""); - // Tier 3: tokenIds assigned 30 -> 60 non-inclusive. - tieredDrop.lazyMint(quantityTier3, baseURITier3, tier3, ""); - - vm.stopPrank(); - - /** - * Claim tokens. - * - Order of priority: [tier2, tier1] - * - Total quantity: 25. [20 from tier2, 5 from tier1] - */ - - string[] memory tiers = new string[](2); - tiers[0] = "non-exsitent tier 1"; - tiers[1] = "non-exsitent tier 2"; - - uint256 claimQuantity = 25; - - _setupClaimSignature(tiers, claimQuantity); - - assertEq(tieredDrop.hasRole(keccak256("MINTER_ROLE"), deployerSigner), true); - - vm.warp(claimRequest.validityStartTimestamp); - vm.prank(claimer); - vm.expectRevert("Insufficient tokens in tiers."); - tieredDrop.claimWithSignature(claimRequest, claimSignature); - } - - //////////////////////////////////////////////// - // // - // reveal tests // - // // - //////////////////////////////////////////////// - - function _getProvenanceHash(string memory _revealURI, bytes memory _key) private view returns (bytes32) { - return keccak256(abi.encodePacked(_revealURI, _key, block.chainid)); - } - - function test_state_revealWithScrambleOffset() public { - // Lazy mint tokens: 3 different tiers: with delayed reveal - bytes memory encryptedURITier1 = tieredDrop.encryptDecrypt(bytes(baseURITier1), keyTier1); - bytes memory encryptedURITier2 = tieredDrop.encryptDecrypt(bytes(baseURITier2), keyTier2); - bytes memory encryptedURITier3 = tieredDrop.encryptDecrypt(bytes(baseURITier3), keyTier3); - - vm.startPrank(dropAdmin); - - // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - tieredDrop.lazyMint( - quantityTier1, - placeholderURITier1, - tier1, - abi.encode(encryptedURITier1, _getProvenanceHash(baseURITier1, keyTier1)) - ); - // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. - tieredDrop.lazyMint( - quantityTier2, - placeholderURITier2, - tier2, - abi.encode(encryptedURITier2, _getProvenanceHash(baseURITier2, keyTier2)) - ); - // Tier 3: tokenIds assigned 30 -> 60 non-inclusive. - tieredDrop.lazyMint( - quantityTier3, - placeholderURITier3, - tier3, - abi.encode(encryptedURITier3, _getProvenanceHash(baseURITier3, keyTier3)) - ); - - vm.stopPrank(); - - /** - * Claim tokens. - * - Order of priority: [tier2, tier1] - * - Total quantity: 25. [20 from tier2, 5 from tier1] - */ - - string[] memory tiers = new string[](2); - tiers[0] = tier2; - tiers[1] = tier1; - - uint256 claimQuantity = 25; - - _setupClaimSignature(tiers, claimQuantity); - - assertEq(tieredDrop.hasRole(keccak256("MINTER_ROLE"), deployerSigner), true); - - vm.warp(claimRequest.validityStartTimestamp); - vm.prank(claimer); - tieredDrop.claimWithSignature(claimRequest, claimSignature); - - /** - * Check token URIs for tokens of tiers: - * - Tier 2: token IDs 0 -> 19 mapped one-to-one to metadata IDs 10 -> 29 - * - Tier 1: token IDs 20 -> 24 mapped one-to-one to metadata IDs 0 -> 4 - */ - - uint256 tier2Id = 10; - uint256 tier1Id = 0; - - for (uint256 i = 0; i < claimQuantity; i += 1) { - // console.log(i); - if (i < 20) { - assertEq(tieredDrop.tokenURI(i), string(abi.encodePacked(placeholderURITier2, uint256(0).toString()))); - tier2Id += 1; - } else { - assertEq(tieredDrop.tokenURI(i), string(abi.encodePacked(placeholderURITier1, uint256(0).toString()))); - tier1Id += 1; - } - } - - // Reveal tokens. - vm.startPrank(dropAdmin); - tieredDrop.reveal(0, keyTier1); - tieredDrop.reveal(1, keyTier2); - tieredDrop.reveal(2, keyTier3); - - uint256 tier2IdStart = 10; - uint256 tier2IdEnd = 30; - - uint256 tier1IdStart = 0; - uint256 tier1IdEnd = 10; - - for (uint256 i = 0; i < claimQuantity; i += 1) { - bytes32 tokenURIHash = keccak256(abi.encodePacked(tieredDrop.tokenURI(i))); - bool detected = false; - - if (i < 20) { - for (uint256 j = tier2IdStart; j < tier2IdEnd; j += 1) { - bytes32 expectedURIHash = keccak256(abi.encodePacked(baseURITier2, j.toString())); - - if (tokenURIHash == expectedURIHash) { - detected = true; - } - - if (detected) { - break; - } - } - } else { - for (uint256 k = tier1IdStart; k < tier1IdEnd; k += 1) { - bytes32 expectedURIHash = keccak256(abi.encodePacked(baseURITier1, k.toString())); - - if (tokenURIHash == expectedURIHash) { - detected = true; - } - - if (detected) { - break; - } - } - } - - assertEq(detected, true); - } - } - - event URIReveal(uint256 tokenId, string uri); - - //////////////////////////////////////////////// - // // - // getTokensInTierLen tests // - // // - //////////////////////////////////////////////// - - function test_state_getTokensInTierLen() public { - // Lazy mint tokens: 3 different tiers - vm.startPrank(dropAdmin); - - // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. - tieredDrop.lazyMint(quantityTier2, baseURITier2, tier2, ""); - // Tier 3: tokenIds assigned 30 -> 60 non-inclusive. - tieredDrop.lazyMint(quantityTier3, baseURITier3, tier3, ""); - - vm.stopPrank(); - - /** - * Claim tokens. - * - Order of priority: [tier2, tier1] - * - Total quantity: 25. [20 from tier2, 5 from tier1] - */ - - string[] memory tiers = new string[](2); - tiers[0] = tier2; - tiers[1] = tier1; - - uint256 claimQuantity = 25; - - _setupClaimSignature(tiers, claimQuantity); - - vm.warp(claimRequest.validityStartTimestamp); - - vm.prank(claimer); - tieredDrop.claimWithSignature(claimRequest, claimSignature); - - assertEq(tieredDrop.getTokensInTierLen(), 2); - - for (uint256 i = 0; i < 5; i += 1) { - _setupClaimSignature(tiers, 1); - - vm.warp(claimRequest.validityStartTimestamp); - - vm.prank(claimer); - tieredDrop.claimWithSignature(claimRequest, claimSignature); - } - - assertEq(tieredDrop.getTokensInTierLen(), 7); - } - - //////////////////////////////////////////////// - // // - // getTokensInTier tests // - // // - //////////////////////////////////////////////// - - function test_state_getTokensInTier() public { - // Lazy mint tokens: 3 different tiers - vm.startPrank(dropAdmin); - - // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. - tieredDrop.lazyMint(quantityTier2, baseURITier2, tier2, ""); - // Tier 3: tokenIds assigned 30 -> 60 non-inclusive. - tieredDrop.lazyMint(quantityTier3, baseURITier3, tier3, ""); - - vm.stopPrank(); - - /** - * Claim tokens. - * - Order of priority: [tier2, tier1] - * - Total quantity: 25. [20 from tier2, 5 from tier1] - */ - - string[] memory tiers = new string[](2); - tiers[0] = tier2; - tiers[1] = tier1; - - uint256 claimQuantity = 25; - - _setupClaimSignature(tiers, claimQuantity); - - vm.warp(claimRequest.validityStartTimestamp); - - vm.prank(claimer); - tieredDrop.claimWithSignature(claimRequest, claimSignature); - - TieredDrop.TokenRange[] memory rangesTier1 = tieredDrop.getTokensInTier(tier1, 0, 2); - assertEq(rangesTier1.length, 1); - - TieredDrop.TokenRange[] memory rangesTier2 = tieredDrop.getTokensInTier(tier2, 0, 2); - assertEq(rangesTier2.length, 1); - - assertEq(rangesTier1[0].startIdInclusive, 20); - assertEq(rangesTier1[0].endIdNonInclusive, 25); - assertEq(rangesTier2[0].startIdInclusive, 0); - assertEq(rangesTier2[0].endIdNonInclusive, 20); - } - - //////////////////////////////////////////////// - // // - // getTierForToken tests // - // // - //////////////////////////////////////////////// - - function test_state_getTierForToken() public { - // Lazy mint tokens: 3 different tiers - vm.startPrank(dropAdmin); - - // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. - tieredDrop.lazyMint(quantityTier2, baseURITier2, tier2, ""); - // Tier 3: tokenIds assigned 30 -> 60 non-inclusive. - tieredDrop.lazyMint(quantityTier3, baseURITier3, tier3, ""); - - vm.stopPrank(); - - /** - * Claim tokens. - * - Order of priority: [tier2, tier1] - * - Total quantity: 25. [20 from tier2, 5 from tier1] - */ - - string[] memory tiers = new string[](2); - tiers[0] = tier2; - tiers[1] = tier1; - - uint256 claimQuantity = 25; - - _setupClaimSignature(tiers, claimQuantity); - - vm.warp(claimRequest.validityStartTimestamp); - - vm.prank(claimer); - tieredDrop.claimWithSignature(claimRequest, claimSignature); - - /** - * Check token URIs for tokens of tiers: - * - Tier 2: token IDs 0 -> 19 mapped one-to-one to metadata IDs 10 -> 29 - * - Tier 1: token IDs 20 -> 24 mapped one-to-one to metadata IDs 0 -> 4 - */ - - uint256 tier2Id = 10; - uint256 tier1Id = 0; - - for (uint256 i = 0; i < claimQuantity; i += 1) { - if (i < 20) { - string memory tierForToken = tieredDrop.getTierForToken(i); - assertEq(tierForToken, tier2); - - tier2Id += 1; - } else { - string memory tierForToken = tieredDrop.getTierForToken(i); - assertEq(tierForToken, tier1); - - tier1Id += 1; - } - } - } - - //////////////////////////////////////////////// - // // - // getMetadataForAllTiers tests // - // // - //////////////////////////////////////////////// - - // function test_state_getMetadataForAllTiers() public { - // // Lazy mint tokens: 3 different tiers - // vm.startPrank(dropAdmin); - - // // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - // tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. - // tieredDrop.lazyMint(quantityTier2, baseURITier2, tier2, ""); - // // Tier 3: tokenIds assigned 30 -> 60 non-inclusive. - // tieredDrop.lazyMint(quantityTier3, baseURITier3, tier3, ""); - - // vm.stopPrank(); - - // TieredDrop.TierMetadata[] memory metadataForAllTiers = tieredDrop.getMetadataForAllTiers(); - - // // Tier 1 - // assertEq(metadataForAllTiers[0].tier, tier1); - - // TieredDrop.TokenRange[] memory ranges1 = metadataForAllTiers[0].ranges; - // assertEq(ranges1.length, 1); - // assertEq(ranges1[0].startIdInclusive, 0); - // assertEq(ranges1[0].endIdNonInclusive, 10); - - // string[] memory baseURIs1 = metadataForAllTiers[0].baseURIs; - // assertEq(baseURIs1.length, 1); - // assertEq(baseURIs1[0], baseURITier1); - - // // Tier 2 - // assertEq(metadataForAllTiers[1].tier, tier2); - - // TieredDrop.TokenRange[] memory ranges2 = metadataForAllTiers[1].ranges; - // assertEq(ranges2.length, 1); - // assertEq(ranges2[0].startIdInclusive, 10); - // assertEq(ranges2[0].endIdNonInclusive, 30); - - // string[] memory baseURIs2 = metadataForAllTiers[1].baseURIs; - // assertEq(baseURIs2.length, 1); - // assertEq(baseURIs2[0], baseURITier2); - - // // Tier 3 - // assertEq(metadataForAllTiers[2].tier, tier3); - - // TieredDrop.TokenRange[] memory ranges3 = metadataForAllTiers[2].ranges; - // assertEq(ranges3.length, 1); - // assertEq(ranges3[0].startIdInclusive, 30); - // assertEq(ranges3[0].endIdNonInclusive, 60); - - // string[] memory baseURIs3 = metadataForAllTiers[2].baseURIs; - // assertEq(baseURIs3.length, 1); - // assertEq(baseURIs3[0], baseURITier3); - // } - - //////////////////////////////////////////////// - // // - // audit tests // - // // - //////////////////////////////////////////////// - - function test_state_claimWithSignature_IssueH1() public { - // Lazy mint tokens: 3 different tiers - vm.startPrank(dropAdmin); - - // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // Tier 2: tokenIds assigned 10 -> 20 non-inclusive. - tieredDrop.lazyMint(10, baseURITier2, tier2, ""); - // Tier 3: tokenIds assigned 20 -> 50 non-inclusive. - tieredDrop.lazyMint(quantityTier3, baseURITier3, tier3, ""); - - // Tier 2: tokenIds assigned 50 -> 60 non-inclusive. - tieredDrop.lazyMint(quantityTier2 - 10, baseURITier2, tier2, ""); - - vm.stopPrank(); - - string[] memory tiers = new string[](2); - tiers[0] = tier2; - tiers[1] = tier1; - - uint256 claimQuantity = 25; - - _setupClaimSignature(tiers, claimQuantity); - - assertEq(tieredDrop.hasRole(keccak256("MINTER_ROLE"), deployerSigner), true); - - vm.warp(claimRequest.validityStartTimestamp); - vm.prank(claimer); - tieredDrop.claimWithSignature(claimRequest, claimSignature); - assertEq(tieredDrop.balanceOf(claimer), claimQuantity); - - for (uint256 i = 0; i < claimQuantity; i += 1) { - // Outputs: - // Checking 0 baseURI2/10 - // Checking 1 baseURI2/11 - // Checking 2 baseURI2/12 - // Checking 3 baseURI2/13 - // Checking 4 baseURI2/14 - // Checking 5 baseURI2/15 - // Checking 6 baseURI2/16 - // Checking 7 baseURI2/17 - // Checking 8 baseURI2/18 - // Checking 9 baseURI2/19 - // Checking 10 baseURI3/50 - // Checking 11 baseURI3/51 - // Checking 12 baseURI3/52 - // Checking 13 baseURI3/53 - // Checking 14 baseURI3/54 - // Checking 15 baseURI3/55 - // Checking 16 baseURI3/56 - // Checking 17 baseURI3/57 - // Checking 18 baseURI3/58 - // Checking 19 baseURI3/59 - // Checking 20 baseURI1/0 - // Checking 21 baseURI1/1 - // Checking 22 baseURI1/2 - // Checking 23 baseURI1/3 - // Checking 24 baseURI1/4 - console.log("Checking", i, tieredDrop.tokenURI(i)); - } - } - - function test_state_claimWithSignature_IssueH1_2() public { - // Lazy mint tokens: 3 different tiers - vm.startPrank(dropAdmin); - - // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. - tieredDrop.lazyMint(quantityTier1, baseURITier1, tier1, ""); - // Tier 2: tokenIds assigned 10 -> 20 non-inclusive. - tieredDrop.lazyMint(1, baseURITier2, tier2, ""); // 10 -> 11 - tieredDrop.lazyMint(9, baseURITier2, tier2, ""); // 11 -> 20 - // Tier 3: tokenIds assigned 20 -> 50 non-inclusive. - tieredDrop.lazyMint(quantityTier3, baseURITier3, tier3, ""); - - // Tier 2: tokenIds assigned 50 -> 60 non-inclusive. - tieredDrop.lazyMint(quantityTier2 - 10, baseURITier2, tier2, ""); - - vm.stopPrank(); - - string[] memory tiers = new string[](2); - tiers[0] = tier2; - tiers[1] = tier1; - - uint256[3] memory claimQuantities = [uint256(1), uint256(3), uint256(21)]; - uint256 claimedCount = 0; - for (uint256 loop = 0; loop < 3; loop++) { - uint256 claimQuantity = claimQuantities[loop]; - uint256 offset = claimedCount; - - _setupClaimSignature(tiers, claimQuantity); - - assertEq(tieredDrop.hasRole(keccak256("MINTER_ROLE"), deployerSigner), true); - - vm.warp(claimRequest.validityStartTimestamp); - vm.prank(claimer); - tieredDrop.claimWithSignature(claimRequest, claimSignature); - - claimedCount += claimQuantity; - assertEq(tieredDrop.balanceOf(claimer), claimedCount); - - for (uint256 i = offset; i < claimQuantity + (offset); i += 1) { - // Outputs: - // Checking 0 baseURI2/10 - // Checking 1 baseURI2/11 - // Checking 2 baseURI2/12 - // Checking 3 baseURI2/13 - // Checking 4 baseURI2/14 - // Checking 5 baseURI2/15 - // Checking 6 baseURI2/16 - // Checking 7 baseURI2/17 - // Checking 8 baseURI2/18 - // Checking 9 baseURI2/19 - // Checking 10 baseURI3/50 - // Checking 11 baseURI3/51 - // Checking 12 baseURI3/52 - // Checking 13 baseURI3/53 - // Checking 14 baseURI3/54 - // Checking 15 baseURI3/55 - // Checking 16 baseURI3/56 - // Checking 17 baseURI3/57 - // Checking 18 baseURI3/58 - // Checking 19 baseURI3/59 - // Checking 20 baseURI1/0 - // Checking 21 baseURI1/1 - // Checking 22 baseURI1/2 - // Checking 23 baseURI1/3 - // Checking 24 baseURI1/4 - console.log("Checking", i, tieredDrop.tokenURI(i)); - } - } - } -} - -// contract TieredDropBechmarkTest is BaseTest { -// using Strings for uint256; - -// TieredDrop public tieredDrop; - -// address internal dropAdmin; -// address internal claimer; - -// // Signature params -// address internal deployerSigner; -// bytes32 internal typehashGenericRequest; -// bytes32 internal nameHash; -// bytes32 internal versionHash; -// bytes32 internal typehashEip712; -// bytes32 internal domainSeparator; - -// // Lazy mint variables -// uint256 internal quantityTier1 = 10; -// string internal tier1 = "tier1"; -// string internal baseURITier1 = "baseURI1/"; -// string internal placeholderURITier1 = "placeholderURI1/"; -// bytes internal keyTier1 = "tier1_key"; - -// uint256 internal quantityTier2 = 20; -// string internal tier2 = "tier2"; -// string internal baseURITier2 = "baseURI2/"; -// string internal placeholderURITier2 = "placeholderURI2/"; -// bytes internal keyTier2 = "tier2_key"; - -// uint256 internal quantityTier3 = 30; -// string internal tier3 = "tier3"; -// string internal baseURITier3 = "baseURI3/"; -// string internal placeholderURITier3 = "placeholderURI3/"; -// bytes internal keyTier3 = "tier3_key"; - -// function setUp() public virtual override { -// super.setUp(); - -// dropAdmin = getActor(1); -// claimer = getActor(2); - -// // Deploy implementation. -// address tieredDropImpl = address(new TieredDrop()); - -// // Deploy proxy pointing to implementaion. -// vm.prank(dropAdmin); -// tieredDrop = TieredDrop( -// address( -// new TWProxy( -// tieredDropImpl, -// abi.encodeCall( -// TieredDrop.initialize, -// (dropAdmin, "Tiered Drop", "TD", "ipfs://", new address[](0), dropAdmin, dropAdmin, 0) -// ) -// ) -// ) -// ); - -// // ====== signature params - -// deployerSigner = signer; -// vm.prank(dropAdmin); -// tieredDrop.grantRole(keccak256("MINTER_ROLE"), deployerSigner); - -// typehashGenericRequest = keccak256( -// "GenericRequest(uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid,bytes data)" -// ); -// nameHash = keccak256(bytes("SignatureAction")); -// versionHash = keccak256(bytes("1")); -// typehashEip712 = keccak256( -// "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" -// ); -// domainSeparator = keccak256( -// abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tieredDrop)) -// ); - -// // ====== - -// // Lazy mint tokens: 3 different tiers -// vm.startPrank(dropAdmin); - -// // Tier 1: tokenIds assigned 0 -> 10 non-inclusive. -// tieredDrop.lazyMint(totalQty, baseURITier1, tier1, ""); -// // Tier 2: tokenIds assigned 10 -> 30 non-inclusive. -// tieredDrop.lazyMint(totalQty, baseURITier2, tier2, ""); - -// vm.stopPrank(); - -// /** -// * Claim tokens. -// * - Order of priority: [tier2, tier1] -// * - Total quantity: 25. [20 from tier2, 5 from tier1] -// */ - -// string[] memory tiers = new string[](2); -// tiers[0] = tier2; -// tiers[1] = tier1; - -// uint256 claimQuantity = totalQty; - -// for (uint256 i = 0; i < claimQuantity; i += 1) { -// _setupClaimSignature(tiers, 1); - -// vm.warp(claimRequest.validityStartTimestamp); - -// vm.prank(claimer); -// tieredDrop.claimWithSignature(claimRequest, claimSignature); -// } -// } - -// TieredDrop.GenericRequest internal claimRequest; -// bytes internal claimSignature; - -// uint256 internal nonce; - -// function _setupClaimSignature(string[] memory _orderedTiers, uint256 _totalQuantity) internal { -// claimRequest.validityStartTimestamp = 1000; -// claimRequest.validityEndTimestamp = 2000; -// claimRequest.uid = keccak256(abi.encodePacked(nonce)); -// nonce += 1; -// claimRequest.data = abi.encode( -// _orderedTiers, -// claimer, -// address(0), -// 0, -// dropAdmin, -// _totalQuantity, -// 0, -// NATIVE_TOKEN -// ); - -// bytes memory encodedRequest = abi.encode( -// typehashGenericRequest, -// claimRequest.validityStartTimestamp, -// claimRequest.validityEndTimestamp, -// claimRequest.uid, -// keccak256(bytes(claimRequest.data)) -// ); - -// bytes32 structHash = keccak256(encodedRequest); -// bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - -// (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); -// claimSignature = abi.encodePacked(r, s, v); -// } - -// // What does it take to exhaust the 550mil RPC view fn gas limit ? - -// // 10_000: 67 mil gas (67,536,754) -// uint256 internal totalQty = 10_000; - -// function test_banchmark_getTokensInTier() public view { -// tieredDrop.getTokensInTier(tier1, 0, totalQty); -// } - -// function test_banchmark_getTokensInTier_ten() public view { -// tieredDrop.getTokensInTier(tier1, 0, 10); -// } - -// function test_banchmark_getTokensInTier_hundred() public view { -// tieredDrop.getTokensInTier(tier1, 0, 100); -// } -// } diff --git a/src/test/airdrop/Airdrop.t.sol b/src/test/airdrop/Airdrop.t.sol deleted file mode 100644 index 0d0ebbf60..000000000 --- a/src/test/airdrop/Airdrop.t.sol +++ /dev/null @@ -1,1065 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { Airdrop, SafeTransferLib, ECDSA } from "contracts/prebuilts/airdrop/Airdrop.sol"; - -// Test imports -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import "../utils/BaseTest.sol"; - -contract MockSmartWallet { - using ECDSA for bytes32; - - bytes4 private constant EIP1271_MAGIC_VALUE = 0x1626ba7e; - address private admin; - - constructor(address _admin) { - admin = _admin; - } - - function isValidSignature(bytes32 _hash, bytes memory _signature) public view returns (bytes4) { - if (_hash.recover(_signature) == admin) { - return EIP1271_MAGIC_VALUE; - } - } - - function onERC721Received(address, address, uint256, bytes memory) external pure returns (bytes4) { - return this.onERC721Received.selector; - } - - function onERC1155Received(address, address, uint256, uint256, bytes memory) external pure returns (bytes4) { - return this.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] memory, - uint256[] memory, - bytes memory - ) external pure returns (bytes4) { - return this.onERC1155BatchReceived.selector; - } -} - -contract AirdropTest is BaseTest { - Airdrop internal airdrop; - MockSmartWallet internal mockSmartWallet; - - bytes32 private constant CONTENT_TYPEHASH_ERC20 = - keccak256("AirdropContentERC20(address recipient,uint256 amount)"); - bytes32 private constant REQUEST_TYPEHASH_ERC20 = - keccak256( - "AirdropRequestERC20(bytes32 uid,address tokenAddress,uint256 expirationTimestamp,AirdropContentERC20[] contents)AirdropContentERC20(address recipient,uint256 amount)" - ); - - bytes32 private constant CONTENT_TYPEHASH_ERC721 = - keccak256("AirdropContentERC721(address recipient,uint256 tokenId)"); - bytes32 private constant REQUEST_TYPEHASH_ERC721 = - keccak256( - "AirdropRequestERC721(bytes32 uid,address tokenAddress,uint256 expirationTimestamp,AirdropContentERC721[] contents)AirdropContentERC721(address recipient,uint256 tokenId)" - ); - - bytes32 private constant CONTENT_TYPEHASH_ERC1155 = - keccak256("AirdropContentERC1155(address recipient,uint256 tokenId,uint256 amount)"); - bytes32 private constant REQUEST_TYPEHASH_ERC1155 = - keccak256( - "AirdropRequestERC1155(bytes32 uid,address tokenAddress,uint256 expirationTimestamp,AirdropContentERC1155[] contents)AirdropContentERC1155(address recipient,uint256 tokenId,uint256 amount)" - ); - - bytes32 private constant NAME_HASH = keccak256(bytes("Airdrop")); - bytes32 private constant VERSION_HASH = keccak256(bytes("1")); - bytes32 private constant TYPE_HASH_EIP712 = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - - bytes32 internal domainSeparator; - - function setUp() public override { - super.setUp(); - - address impl = address(new Airdrop()); - - airdrop = Airdrop(payable(address(new TWProxy(impl, abi.encodeCall(Airdrop.initialize, (signer, "")))))); - - domainSeparator = keccak256( - abi.encode(TYPE_HASH_EIP712, NAME_HASH, VERSION_HASH, block.chainid, address(airdrop)) - ); - - mockSmartWallet = new MockSmartWallet(signer); - } - - function _getContentsERC20(uint256 length) internal pure returns (Airdrop.AirdropContentERC20[] memory contents) { - contents = new Airdrop.AirdropContentERC20[](length); - for (uint256 i = 0; i < length; i++) { - contents[i].recipient = address(uint160(i + 10)); - contents[i].amount = i + 10; - } - } - - function _getContentsERC721(uint256 length) internal pure returns (Airdrop.AirdropContentERC721[] memory contents) { - contents = new Airdrop.AirdropContentERC721[](length); - for (uint256 i = 0; i < length; i++) { - contents[i].recipient = address(uint160(i + 10)); - contents[i].tokenId = i; - } - } - - function _getContentsERC1155( - uint256 length - ) internal pure returns (Airdrop.AirdropContentERC1155[] memory contents) { - contents = new Airdrop.AirdropContentERC1155[](length); - for (uint256 i = 0; i < length; i++) { - contents[i].recipient = address(uint160(i + 10)); - contents[i].tokenId = 0; - contents[i].amount = i + 10; - } - } - - function _signReqERC20( - Airdrop.AirdropRequestERC20 memory req, - uint256 privateKey - ) internal view returns (bytes memory signature) { - bytes32[] memory contentHashes = new bytes32[](req.contents.length); - for (uint i = 0; i < req.contents.length; i++) { - contentHashes[i] = keccak256( - abi.encode(CONTENT_TYPEHASH_ERC20, req.contents[i].recipient, req.contents[i].amount) - ); - } - bytes32 contentHash = keccak256(abi.encodePacked(contentHashes)); - - bytes memory dataToHash; - { - dataToHash = abi.encode( - REQUEST_TYPEHASH_ERC20, - req.uid, - req.tokenAddress, - req.expirationTimestamp, - contentHash - ); - } - - { - bytes32 _structHash = keccak256(dataToHash); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, _structHash)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); - - signature = abi.encodePacked(r, s, v); - } - } - - function _signReqERC721( - Airdrop.AirdropRequestERC721 memory req, - uint256 privateKey - ) internal view returns (bytes memory signature) { - bytes32[] memory contentHashes = new bytes32[](req.contents.length); - for (uint i = 0; i < req.contents.length; i++) { - contentHashes[i] = keccak256( - abi.encode(CONTENT_TYPEHASH_ERC721, req.contents[i].recipient, req.contents[i].tokenId) - ); - } - bytes32 contentHash = keccak256(abi.encodePacked(contentHashes)); - - bytes memory dataToHash; - { - dataToHash = abi.encode( - REQUEST_TYPEHASH_ERC721, - req.uid, - req.tokenAddress, - req.expirationTimestamp, - contentHash - ); - } - - { - bytes32 _structHash = keccak256(dataToHash); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, _structHash)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); - - signature = abi.encodePacked(r, s, v); - } - } - - function _signReqERC1155( - Airdrop.AirdropRequestERC1155 memory req, - uint256 privateKey - ) internal view returns (bytes memory signature) { - bytes32[] memory contentHashes = new bytes32[](req.contents.length); - for (uint i = 0; i < req.contents.length; i++) { - contentHashes[i] = keccak256( - abi.encode( - CONTENT_TYPEHASH_ERC1155, - req.contents[i].recipient, - req.contents[i].tokenId, - req.contents[i].amount - ) - ); - } - bytes32 contentHash = keccak256(abi.encodePacked(contentHashes)); - - bytes memory dataToHash; - { - dataToHash = abi.encode( - REQUEST_TYPEHASH_ERC1155, - req.uid, - req.tokenAddress, - req.expirationTimestamp, - contentHash - ); - } - - { - bytes32 _structHash = keccak256(dataToHash); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, _structHash)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); - - signature = abi.encodePacked(r, s, v); - } - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Push ERC20 - //////////////////////////////////////////////////////////////*/ - - function test_state_airdropPush_erc20() public { - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(10); - - vm.prank(signer); - - airdrop.airdropERC20(address(erc20), contents); - - uint256 totalAmount; - for (uint256 i = 0; i < contents.length; i++) { - totalAmount += contents[i].amount; - assertEq(erc20.balanceOf(contents[i].recipient), contents[i].amount); - } - assertEq(erc20.balanceOf(signer), 100 ether - totalAmount); - } - - function test_state_airdropPush_nativeToken() public { - vm.deal(signer, 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(10); - - uint256 totalAmount; - for (uint256 i = 0; i < contents.length; i++) { - totalAmount += contents[i].amount; - assertEq(contents[i].recipient.balance, 0); - } - - vm.prank(signer); - airdrop.airdropNativeToken{ value: totalAmount }(contents); - - for (uint256 i = 0; i < contents.length; i++) { - assertEq(contents[i].recipient.balance, contents[i].amount); - } - - assertEq(signer.balance, 100 ether - totalAmount); - } - - function test_revert_airdropPush_nativeToken() public { - vm.deal(signer, 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(10); - - vm.prank(signer); - vm.expectRevert(abi.encodeWithSelector(SafeTransferLib.ETHTransferFailed.selector)); - airdrop.airdropNativeToken{ value: 0 }(contents); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Signature ERC20 - //////////////////////////////////////////////////////////////*/ - - function test_state_airdropSignature_erc20() public { - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(10); - Airdrop.AirdropRequestERC20 memory req = Airdrop.AirdropRequestERC20({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc20), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC20(req, privateKey); - - vm.prank(signer); - - airdrop.airdropERC20WithSignature(req, signature); - - uint256 totalAmount; - for (uint256 i = 0; i < contents.length; i++) { - totalAmount += contents[i].amount; - assertEq(erc20.balanceOf(contents[i].recipient), contents[i].amount); - } - assertEq(erc20.balanceOf(signer), 100 ether - totalAmount); - } - - function test_state_airdropSignature_erc20_eip1271() public { - // set mockSmartWallet as contract owner - vm.prank(signer); - airdrop.setOwner(address(mockSmartWallet)); - - // mint tokens to mockSmartWallet - erc20.mint(address(mockSmartWallet), 100 ether); - vm.prank(address(mockSmartWallet)); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(10); - Airdrop.AirdropRequestERC20 memory req = Airdrop.AirdropRequestERC20({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc20), - expirationTimestamp: 1000, - contents: contents - }); - - // sign with original EOA signer private key - bytes memory signature = _signReqERC20(req, privateKey); - - airdrop.airdropERC20WithSignature(req, signature); - - uint256 totalAmount; - for (uint256 i = 0; i < contents.length; i++) { - totalAmount += contents[i].amount; - assertEq(erc20.balanceOf(contents[i].recipient), contents[i].amount); - } - assertEq(erc20.balanceOf(address(mockSmartWallet)), 100 ether - totalAmount); - } - - function test_revert_airdropSignature_erc20_eip1271_invalidSignature() public { - // set mockSmartWallet as contract owner - vm.prank(signer); - airdrop.setOwner(address(mockSmartWallet)); - - // mint tokens to mockSmartWallet - erc20.mint(address(mockSmartWallet), 100 ether); - vm.prank(address(mockSmartWallet)); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(10); - Airdrop.AirdropRequestERC20 memory req = Airdrop.AirdropRequestERC20({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc20), - expirationTimestamp: 1000, - contents: contents - }); - - // sign with random private key - bytes memory signature = _signReqERC20(req, 123); - - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropRequestInvalidSigner.selector)); - airdrop.airdropERC20WithSignature(req, signature); - } - - function test_revert_airdropSignature_erc20_expired() public { - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(10); - Airdrop.AirdropRequestERC20 memory req = Airdrop.AirdropRequestERC20({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc20), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC20(req, privateKey); - - vm.warp(1001); - - vm.prank(signer); - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropRequestExpired.selector, req.expirationTimestamp)); - airdrop.airdropERC20WithSignature(req, signature); - } - - function test_revert_airdropSignature_erc20_alreadyProcessed() public { - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(10); - Airdrop.AirdropRequestERC20 memory req = Airdrop.AirdropRequestERC20({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc20), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC20(req, privateKey); - - vm.prank(signer); - - airdrop.airdropERC20WithSignature(req, signature); - - // try re-sending same request/signature - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropRequestAlreadyProcessed.selector)); - airdrop.airdropERC20WithSignature(req, signature); - } - - function test_revert_airdropSignature_erc20_invalidSigner() public { - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(10); - Airdrop.AirdropRequestERC20 memory req = Airdrop.AirdropRequestERC20({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc20), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC20(req, 123); - - vm.prank(signer); - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropRequestInvalidSigner.selector)); - airdrop.airdropERC20WithSignature(req, signature); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Claim ERC20 - //////////////////////////////////////////////////////////////*/ - - function test_state_airdropClaim_erc20() public { - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - // set merkle root - vm.prank(signer); - airdrop.setMerkleRoot(address(erc20), root, true); - - // generate proof - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 5; - - airdrop.claimERC20(address(erc20), receiver, quantity, proofs); - - assertEq(erc20.balanceOf(receiver), quantity); - assertEq(erc20.balanceOf(signer), 100 ether - quantity); - } - - function test_revert_airdropClaim_erc20_alreadyClaimed() public { - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - // set merkle root - vm.prank(signer); - airdrop.setMerkleRoot(address(erc20), root, true); - - // generate proof - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 5; - - airdrop.claimERC20(address(erc20), receiver, quantity, proofs); - - // revert when claiming again - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropAlreadyClaimed.selector)); - airdrop.claimERC20(address(erc20), receiver, quantity, proofs); - } - - function test_revert_airdropClaim_erc20_noMerkleRoot() public { - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - bytes32[] memory proofs; - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 5; - - // revert when claiming again - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropNoMerkleRoot.selector)); - airdrop.claimERC20(address(erc20), receiver, quantity, proofs); - } - - function test_revert_airdropClaim_erc20_invalidProof() public { - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - // set merkle root - vm.prank(signer); - airdrop.setMerkleRoot(address(erc20), root, true); - - // generate proof - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address receiver = address(0x12345); - uint256 quantity = 5; - - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropInvalidProof.selector)); - airdrop.claimERC20(address(erc20), receiver, quantity, proofs); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Push ERC721 - //////////////////////////////////////////////////////////////*/ - - function test_state_airdropPush_erc721() public { - erc721.mint(signer, 100); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(10); - - vm.prank(signer); - - airdrop.airdropERC721(address(erc721), contents); - - for (uint256 i = 0; i < contents.length; i++) { - assertEq(erc721.ownerOf(contents[i].tokenId), contents[i].recipient); - } - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Signature ERC721 - //////////////////////////////////////////////////////////////*/ - - function test_state_airdropSignature_erc721() public { - erc721.mint(signer, 1000); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(10); - Airdrop.AirdropRequestERC721 memory req = Airdrop.AirdropRequestERC721({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc721), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC721(req, privateKey); - - vm.prank(signer); - - airdrop.airdropERC721WithSignature(req, signature); - - for (uint256 i = 0; i < contents.length; i++) { - assertEq(erc721.ownerOf(contents[i].tokenId), contents[i].recipient); - } - } - - function test_state_airdropSignature_erc721_eip1271() public { - // set mockSmartWallet as contract owner - vm.prank(signer); - airdrop.setOwner(address(mockSmartWallet)); - - // mint tokens to mockSmartWallet - erc721.mint(address(mockSmartWallet), 1000); - vm.prank(address(mockSmartWallet)); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(10); - Airdrop.AirdropRequestERC721 memory req = Airdrop.AirdropRequestERC721({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc721), - expirationTimestamp: 1000, - contents: contents - }); - - // sign with original EOA signer private key - bytes memory signature = _signReqERC721(req, privateKey); - - airdrop.airdropERC721WithSignature(req, signature); - - for (uint256 i = 0; i < contents.length; i++) { - assertEq(erc721.ownerOf(contents[i].tokenId), contents[i].recipient); - } - } - - function test_revert_airdropSignature_erc721_eip1271_invalidSignature() public { - // set mockSmartWallet as contract owner - vm.prank(signer); - airdrop.setOwner(address(mockSmartWallet)); - - // mint tokens to mockSmartWallet - erc721.mint(address(mockSmartWallet), 1000); - vm.prank(address(mockSmartWallet)); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(10); - Airdrop.AirdropRequestERC721 memory req = Airdrop.AirdropRequestERC721({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc721), - expirationTimestamp: 1000, - contents: contents - }); - - // sign with random private key - bytes memory signature = _signReqERC721(req, 123); - - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropRequestInvalidSigner.selector)); - airdrop.airdropERC721WithSignature(req, signature); - } - - function test_revert_airdropSignature_erc721_expired() public { - erc721.mint(signer, 1000); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(10); - Airdrop.AirdropRequestERC721 memory req = Airdrop.AirdropRequestERC721({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc721), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC721(req, privateKey); - - vm.warp(1001); - - vm.prank(signer); - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropRequestExpired.selector, req.expirationTimestamp)); - airdrop.airdropERC721WithSignature(req, signature); - } - - function test_revert_airdropSignature_erc721_alreadyProcessed() public { - erc721.mint(signer, 1000); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(10); - Airdrop.AirdropRequestERC721 memory req = Airdrop.AirdropRequestERC721({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc721), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC721(req, privateKey); - - vm.prank(signer); - airdrop.airdropERC721WithSignature(req, signature); - - // send it again - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropRequestAlreadyProcessed.selector)); - airdrop.airdropERC721WithSignature(req, signature); - } - - function test_revert_airdropSignature_erc721_invalidSigner() public { - erc721.mint(signer, 1000); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(10); - Airdrop.AirdropRequestERC721 memory req = Airdrop.AirdropRequestERC721({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc721), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC721(req, 123); - - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropRequestInvalidSigner.selector)); - airdrop.airdropERC721WithSignature(req, signature); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Claim ERC721 - //////////////////////////////////////////////////////////////*/ - - function test_state_airdropClaim_erc721() public { - erc721.mint(signer, 100); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - // set merkle root - vm.prank(signer); - airdrop.setMerkleRoot(address(erc721), root, true); - - // generate proof - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 tokenId = 5; - - airdrop.claimERC721(address(erc721), receiver, tokenId, proofs); - - assertEq(erc721.ownerOf(tokenId), receiver); - } - - function test_revert_airdropClaim_erc721_alreadyClaimed() public { - erc721.mint(signer, 100); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - // set merkle root - vm.prank(signer); - airdrop.setMerkleRoot(address(erc721), root, true); - - // generate proof - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 tokenId = 5; - - airdrop.claimERC721(address(erc721), receiver, tokenId, proofs); - - // revert when claiming again - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropAlreadyClaimed.selector)); - airdrop.claimERC721(address(erc721), receiver, tokenId, proofs); - } - - function test_revert_airdropClaim_erc721_noMerkleRoot() public { - erc721.mint(signer, 100); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - bytes32[] memory proofs; - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 tokenId = 5; - - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropNoMerkleRoot.selector)); - airdrop.claimERC721(address(erc721), receiver, tokenId, proofs); - } - - function test_revert_airdropClaim_erc721_invalidProof() public { - erc721.mint(signer, 100); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - // set merkle root - vm.prank(signer); - airdrop.setMerkleRoot(address(erc721), root, true); - - // generate proof - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address receiver = address(0x12345); - uint256 tokenId = 5; - - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropInvalidProof.selector)); - airdrop.claimERC721(address(erc721), receiver, tokenId, proofs); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Push ERC1155 - //////////////////////////////////////////////////////////////*/ - - function test_state_airdropPush_erc1155() public { - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(10); - - vm.prank(signer); - - airdrop.airdropERC1155(address(erc1155), contents); - - for (uint256 i = 0; i < contents.length; i++) { - assertEq(erc1155.balanceOf(contents[i].recipient, contents[i].tokenId), contents[i].amount); - } - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Signature ERC1155 - //////////////////////////////////////////////////////////////*/ - - function test_state_airdropSignature_erc115() public { - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(10); - Airdrop.AirdropRequestERC1155 memory req = Airdrop.AirdropRequestERC1155({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc1155), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC1155(req, privateKey); - - vm.prank(signer); - - airdrop.airdropERC1155WithSignature(req, signature); - - for (uint256 i = 0; i < contents.length; i++) { - assertEq(erc1155.balanceOf(contents[i].recipient, contents[i].tokenId), contents[i].amount); - } - } - - function test_state_airdropSignature_erc1155_eip1271() public { - // set mockSmartWallet as contract owner - vm.prank(signer); - airdrop.setOwner(address(mockSmartWallet)); - - // mint tokens to mockSmartWallet - erc1155.mint(address(mockSmartWallet), 0, 100 ether); - vm.prank(address(mockSmartWallet)); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(10); - Airdrop.AirdropRequestERC1155 memory req = Airdrop.AirdropRequestERC1155({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc1155), - expirationTimestamp: 1000, - contents: contents - }); - - // sign with original EOA signer private key - bytes memory signature = _signReqERC1155(req, privateKey); - - airdrop.airdropERC1155WithSignature(req, signature); - - for (uint256 i = 0; i < contents.length; i++) { - assertEq(erc1155.balanceOf(contents[i].recipient, contents[i].tokenId), contents[i].amount); - } - } - - function test_revert_airdropSignature_erc1155_eip1271_invalidSignature() public { - // set mockSmartWallet as contract owner - vm.prank(signer); - airdrop.setOwner(address(mockSmartWallet)); - - // mint tokens to mockSmartWallet - erc1155.mint(address(mockSmartWallet), 0, 100 ether); - vm.prank(address(mockSmartWallet)); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(10); - Airdrop.AirdropRequestERC1155 memory req = Airdrop.AirdropRequestERC1155({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc1155), - expirationTimestamp: 1000, - contents: contents - }); - - // sign with random private key - bytes memory signature = _signReqERC1155(req, 123); - - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropRequestInvalidSigner.selector)); - airdrop.airdropERC1155WithSignature(req, signature); - } - - function test_revert_airdropSignature_erc115_expired() public { - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(10); - Airdrop.AirdropRequestERC1155 memory req = Airdrop.AirdropRequestERC1155({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc1155), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC1155(req, privateKey); - - vm.warp(1001); - - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropRequestExpired.selector, req.expirationTimestamp)); - airdrop.airdropERC1155WithSignature(req, signature); - } - - function test_revert_airdropSignature_erc115_alreadyProcessed() public { - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(10); - Airdrop.AirdropRequestERC1155 memory req = Airdrop.AirdropRequestERC1155({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc1155), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC1155(req, privateKey); - - airdrop.airdropERC1155WithSignature(req, signature); - - // send it again - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropRequestAlreadyProcessed.selector)); - airdrop.airdropERC1155WithSignature(req, signature); - } - - function test_revert_airdropSignature_erc115_invalidSigner() public { - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(10); - Airdrop.AirdropRequestERC1155 memory req = Airdrop.AirdropRequestERC1155({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc1155), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC1155(req, 123); - - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropRequestInvalidSigner.selector)); - airdrop.airdropERC1155WithSignature(req, signature); - } - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Claim ERC1155 - //////////////////////////////////////////////////////////////*/ - - function test_state_airdropClaim_erc1155() public { - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - string[] memory inputs = new string[](4); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop1155.ts"; - inputs[2] = Strings.toString(uint256(0)); - inputs[3] = Strings.toString(uint256(5)); - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - // set merkle root - vm.prank(signer); - airdrop.setMerkleRoot(address(erc1155), root, true); - - // generate proof - inputs[1] = "src/test/scripts/getProofAirdrop1155.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 5; - - airdrop.claimERC1155(address(erc1155), receiver, 0, quantity, proofs); - - assertEq(erc1155.balanceOf(receiver, 0), quantity); - assertEq(erc1155.balanceOf(signer, 0), 100 ether - quantity); - } - - function test_revert_airdropClaim_erc1155_alreadyClaimed() public { - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - string[] memory inputs = new string[](4); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop1155.ts"; - inputs[2] = Strings.toString(uint256(0)); - inputs[3] = Strings.toString(uint256(5)); - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - // set merkle root - vm.prank(signer); - airdrop.setMerkleRoot(address(erc1155), root, true); - - // generate proof - inputs[1] = "src/test/scripts/getProofAirdrop1155.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 5; - - airdrop.claimERC1155(address(erc1155), receiver, 0, quantity, proofs); - - // revert when claiming again - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropAlreadyClaimed.selector)); - airdrop.claimERC1155(address(erc1155), receiver, 0, quantity, proofs); - } - - function test_revert_airdropClaim_erc1155_noMerkleRoot() public { - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - // generate proof - bytes32[] memory proofs; - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 5; - - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropNoMerkleRoot.selector)); - airdrop.claimERC1155(address(erc1155), receiver, 0, quantity, proofs); - } - - function test_revert_airdropClaim_erc1155_invalidProof() public { - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - string[] memory inputs = new string[](4); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop1155.ts"; - inputs[2] = Strings.toString(uint256(0)); - inputs[3] = Strings.toString(uint256(5)); - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - // set merkle root - vm.prank(signer); - airdrop.setMerkleRoot(address(erc1155), root, true); - - // generate proof - inputs[1] = "src/test/scripts/getProofAirdrop1155.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address receiver = address(0x12345); - uint256 quantity = 5; - - vm.expectRevert(abi.encodeWithSelector(Airdrop.AirdropInvalidProof.selector)); - airdrop.claimERC1155(address(erc1155), receiver, 0, quantity, proofs); - } -} diff --git a/src/test/airdrop/AirdropERC1155.t.sol b/src/test/airdrop/AirdropERC1155.t.sol deleted file mode 100644 index 161e9d364..000000000 --- a/src/test/airdrop/AirdropERC1155.t.sol +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { AirdropERC1155, IAirdropERC1155 } from "contracts/prebuilts/unaudited/airdrop/AirdropERC1155.sol"; - -// Test imports -import { Wallet } from "../utils/Wallet.sol"; -import "../utils/BaseTest.sol"; - -contract AirdropERC1155Test is BaseTest { - AirdropERC1155 internal drop; - - Wallet internal tokenOwner; - - IAirdropERC1155.AirdropContent[] internal _contentsOne; - IAirdropERC1155.AirdropContent[] internal _contentsTwo; - - uint256 countOne; - uint256 countTwo; - - function setUp() public override { - super.setUp(); - - drop = AirdropERC1155(getContract("AirdropERC1155")); - - tokenOwner = getWallet(); - - erc1155.mint(address(tokenOwner), 0, 1000); - erc1155.mint(address(tokenOwner), 1, 2000); - erc1155.mint(address(tokenOwner), 2, 3000); - erc1155.mint(address(tokenOwner), 3, 4000); - erc1155.mint(address(tokenOwner), 4, 5000); - - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(drop), true); - - countOne = 1000; - countTwo = 200; - - for (uint256 i = 0; i < countOne; i++) { - _contentsOne.push( - IAirdropERC1155.AirdropContent({ recipient: getActor(uint160(i)), tokenId: i % 5, amount: 5 }) - ); - } - - for (uint256 i = countOne; i < countOne + countTwo; i++) { - _contentsTwo.push( - IAirdropERC1155.AirdropContent({ recipient: getActor(uint160(i)), tokenId: i % 5, amount: 5 }) - ); - } - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stateless airdrop - //////////////////////////////////////////////////////////////*/ - - function test_state_airdrop() public { - vm.prank(deployer); - drop.airdropERC1155(address(erc1155), address(tokenOwner), _contentsOne); - - for (uint256 i = 0; i < countOne; i++) { - assertEq(erc1155.balanceOf(_contentsOne[i].recipient, i % 5), 5); - } - - assertEq(erc1155.balanceOf(address(tokenOwner), 0), 0); - assertEq(erc1155.balanceOf(address(tokenOwner), 1), 1000); - assertEq(erc1155.balanceOf(address(tokenOwner), 2), 2000); - assertEq(erc1155.balanceOf(address(tokenOwner), 3), 3000); - assertEq(erc1155.balanceOf(address(tokenOwner), 4), 4000); - } - - function test_revert_airdrop_notOwner() public { - vm.prank(address(25)); - vm.expectRevert("Not authorized."); - drop.airdropERC1155(address(erc1155), address(tokenOwner), _contentsOne); - } - - function test_revert_airdrop_notApproved() public { - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(drop), false); - - vm.startPrank(deployer); - vm.expectRevert("Not balance or approved"); - drop.airdropERC1155(address(erc1155), address(tokenOwner), _contentsOne); - vm.stopPrank(); - } -} - -contract AirdropERC1155GasTest is BaseTest { - AirdropERC1155 internal drop; - - Wallet internal tokenOwner; - - function setUp() public override { - super.setUp(); - - drop = AirdropERC1155(getContract("AirdropERC1155")); - - tokenOwner = getWallet(); - - erc1155.mint(address(tokenOwner), 0, 1000); - - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(drop), true); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: gas benchmarks, etc. - //////////////////////////////////////////////////////////////*/ - - function test_safeTransferFrom_toEOA() public { - vm.prank(address(tokenOwner)); - erc1155.safeTransferFrom(address(tokenOwner), address(0x123), 0, 10, ""); - } - - function test_safeTransferFrom_toContract() public { - vm.prank(address(tokenOwner)); - erc1155.safeTransferFrom(address(tokenOwner), address(this), 0, 10, ""); - } - - function test_safeTransferFrom_toEOA_gasOverride() public { - vm.prank(address(tokenOwner)); - console.log(gasleft()); - erc1155.safeTransferFrom{ gas: 100_000 }(address(tokenOwner), address(this), 0, 10, ""); - console.log(gasleft()); - } - - function test_safeTransferFrom_toContract_gasOverride() public { - vm.prank(address(tokenOwner)); - console.log(gasleft()); - erc1155.safeTransferFrom{ gas: 100_000 }(address(tokenOwner), address(this), 0, 10, ""); - console.log(gasleft()); - } - - function onERC1155Received(address, address, uint256, uint256, bytes calldata) external pure returns (bytes4) { - return this.onERC1155Received.selector; - } -} diff --git a/src/test/airdrop/AirdropERC1155Claimable.t.sol b/src/test/airdrop/AirdropERC1155Claimable.t.sol deleted file mode 100644 index 4ce582d16..000000000 --- a/src/test/airdrop/AirdropERC1155Claimable.t.sol +++ /dev/null @@ -1,218 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "contracts/prebuilts/unaudited/airdrop/AirdropERC1155Claimable.sol"; - -// Test imports -import { Wallet } from "../utils/Wallet.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { BaseTest } from "../utils/BaseTest.sol"; -import { Strings } from "contracts/lib/Strings.sol"; - -contract AirdropERC1155ClaimableTest is BaseTest { - address public implementation; - AirdropERC1155Claimable internal drop; - - function setUp() public override { - super.setUp(); - - address implementation = address(new AirdropERC1155Claimable()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - drop = AirdropERC1155Claimable( - address( - new TWProxy( - implementation, - abi.encodeCall( - AirdropERC1155Claimable.initialize, - ( - forwarders(), - address(airdropTokenOwner), - address(erc1155), - _airdropTokenIdsERC1155, - _airdropAmountsERC1155, - 1000, - _airdropWalletClaimCountERC1155, - _airdropMerkleRootERC1155 - ) - ) - ) - ) - ); - - erc1155.mint(address(airdropTokenOwner), 0, 100); - erc1155.mint(address(airdropTokenOwner), 1, 100); - erc1155.mint(address(airdropTokenOwner), 2, 100); - erc1155.mint(address(airdropTokenOwner), 3, 100); - erc1155.mint(address(airdropTokenOwner), 4, 100); - - airdropTokenOwner.setApprovalForAllERC1155(address(erc1155), address(drop), true); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `claim` -- for allowlisted claimers - //////////////////////////////////////////////////////////////*/ - - function test_state_claim_allowlistedClaimer() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 2; - uint256 id = 0; - - uint256 _availableAmount = drop.availableAmount(id); - - vm.prank(receiver); - drop.claim(receiver, quantity, id, proofs, 5); - - assertEq(erc1155.balanceOf(receiver, id), quantity); - assertEq(drop.supplyClaimedByWallet(id, receiver), quantity); - assertEq(drop.availableAmount(id), _availableAmount - quantity); - } - - function test_revert_claim_notInAllowlist_invalidQty() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(4)); // generate proof with incorrect amount - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 2; - uint256 id = 0; - - vm.prank(receiver); - vm.expectRevert("invalid quantity."); - drop.claim(receiver, quantity, id, proofs, 5); - } - - function test_revert_claim_allowlistedClaimer_proofClaimed() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 2; - uint256 id = 0; - - vm.prank(receiver); - drop.claim(receiver, quantity, id, proofs, 5); - - quantity = 3; - - vm.prank(receiver); - drop.claim(receiver, quantity, id, proofs, 5); - - vm.prank(receiver); - vm.expectRevert("invalid quantity."); - drop.claim(receiver, quantity, id, proofs, 5); - } - - function test_state_claim_allowlistedClaimer_invalidQuantity() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 6; - uint256 id = 0; - - vm.prank(receiver); - vm.expectRevert("invalid quantity."); - drop.claim(receiver, quantity, id, proofs, 5); - } - - function test_revert_claim_allowlistedClaimer_airdropExpired() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1001); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 5; - uint256 id = 0; - - vm.prank(receiver); - vm.expectRevert("airdrop expired."); - drop.claim(receiver, quantity, id, proofs, 5); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `claim` -- for open claiming - //////////////////////////////////////////////////////////////*/ - - function test_state_claim_nonAllowlistedClaimer() public { - address receiver = address(0x123); - uint256 quantity = 1; - bytes32[] memory proofs; - uint256 id = 0; - - uint256 _availableAmount = drop.availableAmount(id); - - vm.prank(receiver); - drop.claim(receiver, quantity, id, proofs, 0); - - assertEq(erc1155.balanceOf(receiver, id), quantity); - assertEq(drop.supplyClaimedByWallet(id, receiver), quantity); - assertEq(drop.availableAmount(id), _availableAmount - quantity); - } - - function test_revert_claim_nonAllowlistedClaimer_invalidQuantity() public { - address receiver = address(0x123); - uint256 quantity = 2; - bytes32[] memory proofs; - uint256 id = 0; - - vm.prank(receiver); - vm.expectRevert("invalid quantity."); - drop.claim(receiver, quantity, id, proofs, 0); - } - - function test_revert_claim_nonAllowlistedClaimer_exceedsAvailable() public { - uint256 id = 0; - uint256 _availableAmount = drop.availableAmount(id); - bytes32[] memory proofs; - - uint256 i = 0; - for (; i < _availableAmount; i++) { - address receiver = getActor(uint160(i)); - vm.prank(receiver); - drop.claim(receiver, 1, id, proofs, 0); - } - - address receiver = getActor(uint160(i)); - vm.prank(receiver); - vm.expectRevert("exceeds available tokens."); - drop.claim(receiver, 1, id, proofs, 0); - } -} diff --git a/src/test/airdrop/AirdropERC20.t.sol b/src/test/airdrop/AirdropERC20.t.sol deleted file mode 100644 index 54d0c4997..000000000 --- a/src/test/airdrop/AirdropERC20.t.sol +++ /dev/null @@ -1,187 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { AirdropERC20, IAirdropERC20 } from "contracts/prebuilts/unaudited/airdrop/AirdropERC20.sol"; -import { CurrencyTransferLib } from "contracts/lib/CurrencyTransferLib.sol"; - -// Test imports -import { Wallet } from "../utils/Wallet.sol"; -import "../utils/BaseTest.sol"; - -import "../mocks/MockERC20NonCompliant.sol"; - -contract AirdropERC20Test is BaseTest { - AirdropERC20 internal drop; - - Wallet internal tokenOwner; - - IAirdropERC20.AirdropContent[] internal _contentsOne; - IAirdropERC20.AirdropContent[] internal _contentsTwo; - - uint256 countOne; - uint256 countTwo; - - function setUp() public override { - super.setUp(); - - drop = AirdropERC20(getContract("AirdropERC20")); - - tokenOwner = getWallet(); - - erc20.mint(address(tokenOwner), 10_000 ether); - tokenOwner.setAllowanceERC20(address(erc20), address(drop), type(uint256).max); - - countOne = 1000; - countTwo = 200; - - for (uint256 i = 0; i < countOne; i++) { - _contentsOne.push(IAirdropERC20.AirdropContent({ recipient: getActor(uint160(i)), amount: 10 ether })); - } - - for (uint256 i = countOne; i < countOne + countTwo; i++) { - _contentsTwo.push(IAirdropERC20.AirdropContent({ recipient: getActor(uint160(i)), amount: 10 ether })); - } - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stateless airdrop - //////////////////////////////////////////////////////////////*/ - - function test_state_airdrop() public { - vm.prank(deployer); - drop.airdropERC20(address(erc20), address(tokenOwner), _contentsOne); - - for (uint256 i = 0; i < countOne; i++) { - assertEq(erc20.balanceOf(_contentsOne[i].recipient), _contentsOne[i].amount); - } - assertEq(erc20.balanceOf(address(tokenOwner)), 0); - } - - function test_revert_airdrop_insufficientValue() public { - vm.prank(deployer); - vm.expectRevert("Insufficient native token amount"); - drop.airdropERC20(CurrencyTransferLib.NATIVE_TOKEN, address(tokenOwner), _contentsOne); - } - - function test_revert_airdrop_notOwner() public { - vm.startPrank(address(25)); - vm.expectRevert("Not authorized."); - drop.airdropERC20(address(erc20), address(tokenOwner), _contentsOne); - vm.stopPrank(); - } - - function test_revert_airdrop_notApproved() public { - tokenOwner.setAllowanceERC20(address(erc20), address(drop), 0); - - vm.startPrank(deployer); - vm.expectRevert("Not balance or allowance"); - drop.airdropERC20(address(erc20), address(tokenOwner), _contentsOne); - vm.stopPrank(); - } -} - -contract AirdropERC20AuditTest is BaseTest { - AirdropERC20 internal drop; - - Wallet internal tokenOwner; - - IAirdropERC20.AirdropContent[] internal _contentsOne; - IAirdropERC20.AirdropContent[] internal _contentsTwo; - - uint256 countOne; - uint256 countTwo; - - MockERC20NonCompliant public erc20_nonCompliant; - - function setUp() public override { - super.setUp(); - - erc20_nonCompliant = new MockERC20NonCompliant(); - drop = AirdropERC20(getContract("AirdropERC20")); - - tokenOwner = getWallet(); - - erc20_nonCompliant.mint(address(tokenOwner), 10_000 ether); - tokenOwner.setAllowanceERC20(address(erc20_nonCompliant), address(drop), type(uint256).max); - - countOne = 1000; - countTwo = 200; - - for (uint256 i = 0; i < countOne; i++) { - _contentsOne.push(IAirdropERC20.AirdropContent({ recipient: getActor(uint160(i)), amount: 10 ether })); - } - - for (uint256 i = countOne; i < countOne + countTwo; i++) { - _contentsTwo.push(IAirdropERC20.AirdropContent({ recipient: getActor(uint160(i)), amount: 10 ether })); - } - } - - function test_process_payments_with_non_compliant_token() public { - vm.prank(deployer); - drop.airdropERC20(address(erc20_nonCompliant), address(tokenOwner), _contentsOne); - - // check balances after airdrop - for (uint256 i = 0; i < countOne; i++) { - assertEq(erc20_nonCompliant.balanceOf(_contentsOne[i].recipient), _contentsOne[i].amount); - } - assertEq(erc20_nonCompliant.balanceOf(address(tokenOwner)), 0); - } -} - -contract AirdropERC20GasTest is BaseTest { - AirdropERC20 internal drop; - - Wallet internal tokenOwner; - - function setUp() public override { - super.setUp(); - - drop = AirdropERC20(getContract("AirdropERC20")); - - tokenOwner = getWallet(); - - erc20.mint(address(tokenOwner), 10_000 ether); - tokenOwner.setAllowanceERC20(address(erc20), address(drop), type(uint256).max); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: gas benchmarks, etc. - //////////////////////////////////////////////////////////////*/ - - function test_transferNativeToken_toEOA() public { - vm.prank(address(tokenOwner)); - (bool success, bytes memory data) = address(0x123).call{ value: 1 ether }(""); - - // Silence warning: Return value of low-level calls not used. - (success, data) = (success, data); - } - - function test_transferNativeToken_toContract() public { - vm.prank(address(tokenOwner)); - (bool success, bytes memory data) = address(this).call{ value: 1 ether }(""); - - // Silence warning: Return value of low-level calls not used. - (success, data) = (success, data); - } - - function test_transferNativeToken_toEOA_gasOverride() public { - vm.prank(address(tokenOwner)); - console.log(gasleft()); - (bool success, bytes memory data) = address(0x123).call{ value: 1 ether, gas: 100_000 }(""); - - // Silence warning: Return value of low-level calls not used. - (success, data) = (success, data); - - console.log(gasleft()); - } - - function test_transferNativeToken_toContract_gasOverride() public { - vm.prank(address(tokenOwner)); - console.log(gasleft()); - (bool success, bytes memory data) = address(this).call{ value: 1 ether, gas: 100_000 }(""); - console.log(gasleft()); - - // Silence warning: Return value of low-level calls not used. - (success, data) = (success, data); - } -} diff --git a/src/test/airdrop/AirdropERC20Claimable.t.sol b/src/test/airdrop/AirdropERC20Claimable.t.sol deleted file mode 100644 index efc5029a8..000000000 --- a/src/test/airdrop/AirdropERC20Claimable.t.sol +++ /dev/null @@ -1,199 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "contracts/prebuilts/unaudited/airdrop/AirdropERC20Claimable.sol"; - -// Test imports -import { Wallet } from "../utils/Wallet.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import "../utils/BaseTest.sol"; - -contract AirdropERC20ClaimableTest is BaseTest { - address public implementation; - AirdropERC20Claimable internal drop; - - function setUp() public override { - super.setUp(); - - address implementation = address(new AirdropERC20Claimable()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - drop = AirdropERC20Claimable( - address( - new TWProxy( - implementation, - abi.encodeCall( - AirdropERC20Claimable.initialize, - ( - forwarders(), - address(airdropTokenOwner), - address(erc20), - 10_000 ether, - 1000, - 1, - _airdropMerkleRootERC20 - ) - ) - ) - ) - ); - - erc20.mint(address(airdropTokenOwner), 10_000 ether); - airdropTokenOwner.setAllowanceERC20(address(erc20), address(drop), type(uint256).max); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `claim` -- for allowlisted claimers - //////////////////////////////////////////////////////////////*/ - - function test_state_claim_allowlistedClaimer() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 2; - - uint256 _availableAmount = drop.availableAmount(); - - vm.prank(receiver); - drop.claim(receiver, quantity, proofs, 5); - - assertEq(erc20.balanceOf(receiver), quantity); - assertEq(erc20.balanceOf(address(airdropTokenOwner)), _availableAmount - quantity); - assertEq(drop.supplyClaimedByWallet(receiver), quantity); - assertEq(drop.availableAmount(), _availableAmount - quantity); - } - - function test_revert_claim_notInAllowlist() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(4)); // generate proof with incorrect amount - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 2; - - vm.prank(receiver); - vm.expectRevert("invalid quantity."); - drop.claim(receiver, quantity, proofs, 4); - } - - function test_state_claim_allowlistedClaimer_maxAmountClaimed() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 2; - - vm.prank(receiver); - drop.claim(receiver, quantity, proofs, 5); - - quantity = 3; - - vm.prank(receiver); - drop.claim(receiver, quantity, proofs, 5); - - // claiming again after exhausting claim limit - vm.prank(receiver); - vm.expectRevert("invalid quantity."); - drop.claim(receiver, quantity, proofs, 5); - } - - function test_state_claim_allowlistedClaimer_invalidQuantity() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 6; - - vm.prank(receiver); - vm.expectRevert("invalid quantity."); - drop.claim(receiver, quantity, proofs, 5); - } - - function test_state_claim_allowlistedClaimer_airdropExpired() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1001); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 5; - - vm.prank(receiver); - vm.expectRevert("airdrop expired."); - drop.claim(receiver, quantity, proofs, 5); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `claim` -- for open claiming - //////////////////////////////////////////////////////////////*/ - - function test_state_claim_nonAllowlistedClaimer() public { - address receiver = address(0x123); - uint256 quantity = 1; - bytes32[] memory proofs; - - uint256 _availableAmount = drop.availableAmount(); - - vm.prank(receiver); - drop.claim(receiver, quantity, proofs, 0); - - assertEq(erc20.balanceOf(receiver), quantity); - assertEq(erc20.balanceOf(address(airdropTokenOwner)), _availableAmount - quantity); - assertEq(drop.supplyClaimedByWallet(receiver), quantity); - assertEq(drop.availableAmount(), _availableAmount - quantity); - } - - function test_revert_claim_nonAllowlistedClaimer_invalidQuantity() public { - address receiver = address(0x123); - uint256 quantity = 2; - bytes32[] memory proofs; - - vm.prank(receiver); - vm.expectRevert("invalid quantity."); - drop.claim(receiver, quantity, proofs, 0); - } - - function test_revert_claim_nonAllowlistedClaimer_exceedsAvailable() public { - uint256 _availableAmount = drop.availableAmount(); - bytes32[] memory proofs; - - address receiver = getActor(uint160(2)); - vm.prank(receiver); - vm.expectRevert("exceeds available tokens."); - drop.claim(receiver, 10_001 ether, proofs, 0); - } -} diff --git a/src/test/airdrop/AirdropERC721.t.sol b/src/test/airdrop/AirdropERC721.t.sol deleted file mode 100644 index 6b579eb46..000000000 --- a/src/test/airdrop/AirdropERC721.t.sol +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { AirdropERC721, IAirdropERC721 } from "contracts/prebuilts/unaudited/airdrop/AirdropERC721.sol"; - -// Test imports -import { Wallet } from "../utils/Wallet.sol"; -import "../utils/BaseTest.sol"; - -contract AirdropERC721Test is BaseTest { - AirdropERC721 internal drop; - - Wallet internal tokenOwner; - - IAirdropERC721.AirdropContent[] internal _contentsOne; - IAirdropERC721.AirdropContent[] internal _contentsTwo; - - uint256 countOne; - uint256 countTwo; - - function setUp() public override { - super.setUp(); - - drop = AirdropERC721(getContract("AirdropERC721")); - - tokenOwner = getWallet(); - - erc721.mint(address(tokenOwner), 1500); - tokenOwner.setApprovalForAllERC721(address(erc721), address(drop), true); - - countOne = 1000; - countTwo = 200; - - for (uint256 i = 0; i < countOne; i++) { - _contentsOne.push(IAirdropERC721.AirdropContent({ recipient: getActor(uint160(i)), tokenId: i })); - } - - for (uint256 i = countOne; i < countOne + countTwo; i++) { - _contentsTwo.push(IAirdropERC721.AirdropContent({ recipient: getActor(uint160(i)), tokenId: i })); - } - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stateless airdrop - //////////////////////////////////////////////////////////////*/ - - function test_state_airdrop() public { - vm.prank(deployer); - drop.airdropERC721(address(erc721), address(tokenOwner), _contentsOne); - - for (uint256 i = 0; i < 1000; i++) { - assertEq(erc721.ownerOf(i), _contentsOne[i].recipient); - } - } - - function test_revert_airdrop_notOwner() public { - vm.prank(address(25)); - vm.expectRevert("Not authorized."); - drop.airdropERC721(address(erc721), address(tokenOwner), _contentsOne); - } - - function test_revert_airdrop_notApproved() public { - tokenOwner.setApprovalForAllERC721(address(erc721), address(drop), false); - - vm.startPrank(deployer); - vm.expectRevert("Not owner or approved"); - drop.airdropERC721(address(erc721), address(tokenOwner), _contentsOne); - vm.stopPrank(); - } -} - -contract AirdropERC721GasTest is BaseTest { - AirdropERC721 internal drop; - - Wallet internal tokenOwner; - - function setUp() public override { - super.setUp(); - - drop = AirdropERC721(getContract("AirdropERC721")); - - tokenOwner = getWallet(); - - erc721.mint(address(tokenOwner), 1500); - tokenOwner.setApprovalForAllERC721(address(erc721), address(drop), true); - - vm.startPrank(address(tokenOwner)); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: gas benchmarks, etc. - //////////////////////////////////////////////////////////////*/ - - function test_safeTransferFrom_toEOA() public { - erc721.safeTransferFrom(address(tokenOwner), address(0x123), 0); - } - - function test_safeTransferFrom_toContract() public { - erc721.safeTransferFrom(address(tokenOwner), address(this), 0); - } - - function test_safeTransferFrom_toEOA_gasOverride() public { - console.log(gasleft()); - erc721.safeTransferFrom{ gas: 100_000 }(address(tokenOwner), address(0x123), 0); - console.log(gasleft()); - } - - function test_safeTransferFrom_toContract_gasOverride() public { - console.log(gasleft()); - erc721.safeTransferFrom{ gas: 100_000 }(address(tokenOwner), address(this), 0); - console.log(gasleft()); - } - - function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) { - return this.onERC721Received.selector; - } -} diff --git a/src/test/airdrop/AirdropERC721Claimable.t.sol b/src/test/airdrop/AirdropERC721Claimable.t.sol deleted file mode 100644 index 917ac4043..000000000 --- a/src/test/airdrop/AirdropERC721Claimable.t.sol +++ /dev/null @@ -1,220 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "contracts/prebuilts/unaudited/airdrop/AirdropERC721Claimable.sol"; - -// Test imports -import { Wallet } from "../utils/Wallet.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { BaseTest } from "../utils/BaseTest.sol"; -import { Strings } from "contracts/lib/Strings.sol"; - -contract AirdropERC721ClaimableTest is BaseTest { - address public implementation; - AirdropERC721Claimable internal drop; - - function setUp() public override { - super.setUp(); - - address implementation = address(new AirdropERC721Claimable()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - drop = AirdropERC721Claimable( - address( - new TWProxy( - implementation, - abi.encodeCall( - AirdropERC721Claimable.initialize, - ( - forwarders(), - address(airdropTokenOwner), - address(erc721), - _airdropTokenIdsERC721, - 1000, - 1, - _airdropMerkleRootERC721 - ) - ) - ) - ) - ); - - erc721.mint(address(airdropTokenOwner), 1000); - airdropTokenOwner.setApprovalForAllERC721(address(erc721), address(drop), true); - } - - // /*/////////////////////////////////////////////////////////////// - // Unit tests: `claim` -- for allowlisted claimers - // //////////////////////////////////////////////////////////////*/ - - function test_state_claim_allowlistedClaimer() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 2; - - uint256 _availableAmount = drop.availableAmount(); - uint256 _nextIndex = drop.nextIndex(); - - vm.prank(receiver); - drop.claim(receiver, quantity, proofs, 5); - - for (uint256 i = 0; i < quantity; i++) { - assertEq(erc721.ownerOf(i), receiver); - } - assertEq(drop.nextIndex(), _nextIndex + quantity); - assertEq(drop.supplyClaimedByWallet(receiver), quantity); - assertEq(drop.availableAmount(), _availableAmount - quantity); - } - - function test_revert_claim_notInAllowlist() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(4)); // generate proof with incorrect amount - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 2; - - vm.prank(receiver); - vm.expectRevert("invalid quantity."); - drop.claim(receiver, quantity, proofs, 4); - } - - function test_revert_claim_allowlistedClaimer_proofClaimed() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 2; - - uint256 _availableAmount = drop.availableAmount(); - uint256 _nextIndex = drop.nextIndex(); - - vm.prank(receiver); - drop.claim(receiver, quantity, proofs, 5); - - for (uint256 i = 0; i < quantity; i++) { - assertEq(erc721.ownerOf(i), receiver); - } - assertEq(drop.nextIndex(), _nextIndex + quantity); - assertEq(drop.supplyClaimedByWallet(receiver), quantity); - assertEq(drop.availableAmount(), _availableAmount - quantity); - - quantity = 3; - - vm.prank(receiver); - drop.claim(receiver, quantity, proofs, 5); - - vm.prank(receiver); - vm.expectRevert("invalid quantity."); - drop.claim(receiver, quantity, proofs, 5); - } - - function test_state_claim_allowlistedClaimer_invalidQuantity() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 6; - - vm.prank(receiver); - vm.expectRevert("invalid quantity."); - drop.claim(receiver, quantity, proofs, 5); - } - - function test_state_claim_allowlistedClaimer_airdropExpired() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - vm.warp(1001); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 5; - - vm.prank(receiver); - vm.expectRevert("airdrop expired."); - drop.claim(receiver, quantity, proofs, 5); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `claim` -- for open claiming - //////////////////////////////////////////////////////////////*/ - - function test_state_claim_nonAllowlistedClaimer() public { - address receiver = address(0x123); - uint256 quantity = 1; - bytes32[] memory proofs; - - uint256 _availableAmount = drop.availableAmount(); - uint256 _nextIndex = drop.nextIndex(); - - vm.prank(receiver); - drop.claim(receiver, quantity, proofs, 0); - - assertEq(erc721.ownerOf(0), receiver); - assertEq(drop.nextIndex(), _nextIndex + quantity); - assertEq(drop.supplyClaimedByWallet(receiver), quantity); - assertEq(drop.availableAmount(), _availableAmount - quantity); - } - - function test_revert_claim_nonAllowlistedClaimer_invalidQuantity() public { - address receiver = address(0x123); - uint256 quantity = 2; - bytes32[] memory proofs; - - vm.prank(receiver); - vm.expectRevert("invalid quantity."); - drop.claim(receiver, quantity, proofs, 0); - } - - function test_revert_claim_nonAllowlistedClaimer_exceedsAvailable() public { - uint256 _availableAmount = drop.availableAmount(); - bytes32[] memory proofs; - - uint256 i = 0; - for (; i < _availableAmount; i++) { - address receiver = getActor(uint160(i)); - vm.prank(receiver); - drop.claim(receiver, 1, proofs, 0); - } - - address receiver = getActor(uint160(i)); - vm.prank(receiver); - vm.expectRevert("exceeds available tokens."); - drop.claim(receiver, 1, proofs, 0); - } -} diff --git a/src/test/benchmark/AccountBenchmark.t.sol b/src/test/benchmark/AccountBenchmark.t.sol deleted file mode 100644 index c07faa649..000000000 --- a/src/test/benchmark/AccountBenchmark.t.sol +++ /dev/null @@ -1,524 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test utils -import "../utils/BaseTest.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Account Abstraction setup for smart wallets. -import { EntryPoint, IEntryPoint } from "contracts/prebuilts/account/utils/EntryPoint.sol"; -import { PackedUserOperation } from "contracts/prebuilts/account/interfaces/PackedUserOperation.sol"; - -// Target -import { IAccountPermissions } from "contracts/extension/interface/IAccountPermissions.sol"; -import { AccountFactory } from "contracts/prebuilts/account/non-upgradeable/AccountFactory.sol"; -import { Account as SimpleAccount } from "contracts/prebuilts/account/non-upgradeable/Account.sol"; -import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - -/// @dev This is a dummy contract to test contract interactions with Account. -contract Number { - uint256 public num; - - function setNum(uint256 _num) public { - num = _num; - } - - function doubleNum() public { - num *= 2; - } - - function incrementNum() public { - num += 1; - } -} - -contract AccountBenchmarkTest is BaseTest { - // Target contracts - EntryPoint private entrypoint; - AccountFactory private accountFactory; - - // Mocks - Number internal numberContract; - - // Test params - uint256 private accountAdminPKey = 100; - address private accountAdmin; - - uint256 private accountSignerPKey = 200; - address private accountSigner; - - uint256 private nonSignerPKey = 300; - address private nonSigner; - - // UserOp terminology: `sender` is the smart wallet. - address private sender = 0x0df2C3523703d165Aa7fA1a552f3F0B56275DfC6; - address payable private beneficiary = payable(address(0x45654)); - - bytes32 private uidCache = bytes32("random uid"); - - event AccountCreated(address indexed account, address indexed accountAdmin); - - function _signSignerPermissionRequest( - IAccountPermissions.SignerPermissionRequest memory _req - ) internal view returns (bytes memory signature) { - bytes32 typehashSignerPermissionRequest = keccak256( - "SignerPermissionRequest(address signer,uint8 isAdmin,address[] approvedTargets,uint256 nativeTokenLimitPerTransaction,uint128 permissionStartTimestamp,uint128 permissionEndTimestamp,uint128 reqValidityStartTimestamp,uint128 reqValidityEndTimestamp,bytes32 uid)" - ); - bytes32 nameHash = keccak256(bytes("Account")); - bytes32 versionHash = keccak256(bytes("1")); - bytes32 typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - bytes32 domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, sender)); - - bytes memory encodedRequest = bytes.concat( - abi.encode( - typehashSignerPermissionRequest, - _req.signer, - _req.isAdmin, - keccak256(abi.encodePacked(_req.approvedTargets)), - _req.nativeTokenLimitPerTransaction - ), - abi.encode( - _req.permissionStartTimestamp, - _req.permissionEndTimestamp, - _req.reqValidityStartTimestamp, - _req.reqValidityEndTimestamp, - _req.uid - ) - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, typedDataHash); - signature = abi.encodePacked(r, s, v); - } - - function _setupUserOp( - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (PackedUserOperation[] memory ops) { - uint256 nonce = entrypoint.getNonce(sender, 0); - PackedUserOperation memory op; - - { - uint128 verificationGasLimit = 500_000; - uint128 callGasLimit = 500_000; - bytes32 packedGasLimits = (bytes32(uint256(verificationGasLimit)) << 128) | bytes32(uint256(callGasLimit)); - - // Get user op fields - op = PackedUserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - accountGasLimits: packedGasLimits, - preVerificationGas: 500_000, - gasFees: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - } - - // Sign UserOp - bytes32 opHash = EntryPoint(entrypoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new PackedUserOperation[](1); - ops[0] = op; - } - - function _setupUserOpExecute( - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "execute(address,uint256,bytes)", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - function _setupUserOpExecuteBatch( - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "executeBatch(address[],uint256[],bytes[])", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - function setUp() public override { - super.setUp(); - - // Setup signers. - accountAdmin = vm.addr(accountAdminPKey); - vm.deal(accountAdmin, 100 ether); - - accountSigner = vm.addr(accountSignerPKey); - nonSigner = vm.addr(nonSignerPKey); - - // Setup contracts - entrypoint = new EntryPoint(); - // deploy account factory - accountFactory = new AccountFactory(deployer, IEntryPoint(payable(address(entrypoint)))); - // deploy dummy contract - numberContract = new Number(); - } - - /*/////////////////////////////////////////////////////////////// - Test: creating an account - //////////////////////////////////////////////////////////////*/ - - /// @dev Create an account by directly calling the factory. - function test_state_createAccount_viaFactory() public { - accountFactory.createAccount(accountAdmin, bytes("")); - } - - /// @dev Create an account via Entrypoint. - function test_state_createAccount_viaEntrypoint() public { - vm.pauseGasMetering(); - bytes memory initCallData = abi.encodeWithSignature("createAccount(address,bytes)", accountAdmin, bytes("")); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - vm.resumeGasMetering(); - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecute( - accountAdminPKey, - initCode, - address(0), - 0, - bytes("") - ); - - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - } - - /*/////////////////////////////////////////////////////////////// - Test: performing a contract call - //////////////////////////////////////////////////////////////*/ - - function _setup_executeTransaction() internal { - bytes memory initCallData = abi.encodeWithSignature("createAccount(address,bytes)", accountAdmin, bytes("")); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecute( - accountAdminPKey, - initCode, - address(0), - 0, - bytes("") - ); - - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - } - - /// @dev Perform a state changing transaction directly via account. - function test_state_executeTransaction() public { - vm.pauseGasMetering(); - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - vm.resumeGasMetering(); - vm.prank(accountAdmin); - SimpleAccount(payable(account)).execute( - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - } - - /// @dev Perform many state changing transactions in a batch directly via account. - function test_state_executeBatchTransaction() public { - vm.pauseGasMetering(); - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 0; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - vm.resumeGasMetering(); - vm.prank(accountAdmin); - SimpleAccount(payable(account)).executeBatch(targets, values, callData); - } - - /// @dev Perform a state changing transaction via Entrypoint. - function test_state_executeTransaction_viaEntrypoint() public { - vm.pauseGasMetering(); - _setup_executeTransaction(); - - vm.resumeGasMetering(); - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountAdminPKey, - bytes(""), - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - } - - /// @dev Perform many state changing transactions in a batch via Entrypoint. - function test_state_executeBatchTransaction_viaEntrypoint() public { - vm.pauseGasMetering(); - _setup_executeTransaction(); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 0; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - vm.resumeGasMetering(); - PackedUserOperation[] memory userOp = _setupUserOpExecuteBatch( - accountAdminPKey, - bytes(""), - targets, - values, - callData - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - } - - /// @dev Perform many state changing transactions in a batch via Entrypoint. - function test_state_executeBatchTransaction_viaAccountSigner() public { - vm.pauseGasMetering(); - _setup_executeTransaction(); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 0; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - - vm.resumeGasMetering(); - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - PackedUserOperation[] memory userOp = _setupUserOpExecuteBatch( - accountSignerPKey, - bytes(""), - targets, - values, - callData - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - } - - /// @dev Perform a state changing transaction via Entrypoint and a SIGNER_ROLE holder. - function test_state_executeTransaction_viaAccountSigner() public { - vm.pauseGasMetering(); - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - - vm.resumeGasMetering(); - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountSignerPKey, - bytes(""), - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - } - - /*/////////////////////////////////////////////////////////////// - Test: receiving and sending native tokens - //////////////////////////////////////////////////////////////*/ - - /// @dev Send native tokens to an account. - function test_state_accountReceivesNativeTokens() public { - vm.pauseGasMetering(); - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - vm.resumeGasMetering(); - vm.prank(accountAdmin); - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory data) = payable(account).call{ value: 1000 }(""); - - // Silence warning: Return value of low-level calls not used. - (success, data) = (success, data); - } - - /// @dev Transfer native tokens out of an account. - function test_state_transferOutsNativeTokens() public { - vm.pauseGasMetering(); - _setup_executeTransaction(); - - uint256 value = 1000; - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - vm.prank(accountAdmin); - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory data) = payable(account).call{ value: value }(""); - - // Silence warning: Return value of low-level calls not used. - (success, data) = (success, data); - - address recipient = address(0x3456); - - vm.resumeGasMetering(); - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountAdminPKey, - bytes(""), - recipient, - value, - bytes("") - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - } - - /// @dev Add and remove a deposit for the account from the Entrypoint. - - function test_state_addAndWithdrawDeposit() public { - vm.pauseGasMetering(); - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - vm.resumeGasMetering(); - vm.startPrank(accountAdmin); - SimpleAccount(payable(account)).addDeposit{ value: 1000 }(); - - SimpleAccount(payable(account)).withdrawDepositTo(payable(accountSigner), 500); - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Test: receiving ERC-721 and ERC-1155 NFTs - //////////////////////////////////////////////////////////////*/ - - /// @dev Send an ERC-721 NFT to an account. - function test_state_receiveERC721NFT() public { - vm.pauseGasMetering(); - _setup_executeTransaction(); - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - vm.resumeGasMetering(); - erc721.mint(account, 1); - } - - /// @dev Send an ERC-1155 NFT to an account. - function test_state_receiveERC1155NFT() public { - vm.pauseGasMetering(); - _setup_executeTransaction(); - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - vm.resumeGasMetering(); - erc1155.mint(account, 0, 1); - } - - /*/////////////////////////////////////////////////////////////// - Test: setting contract metadata - //////////////////////////////////////////////////////////////*/ - - /// @dev Set contract metadata via entrypoint. - function test_state_contractMetadata() public { - vm.pauseGasMetering(); - _setup_executeTransaction(); - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).setContractURI("https://example.com"); - - vm.resumeGasMetering(); - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountAdminPKey, - bytes(""), - address(account), - 0, - abi.encodeWithSignature("setContractURI(string)", "https://thirdweb.com") - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - } -} diff --git a/src/test/benchmark/AirdropBenchmark.t.sol b/src/test/benchmark/AirdropBenchmark.t.sol deleted file mode 100644 index be93c7126..000000000 --- a/src/test/benchmark/AirdropBenchmark.t.sol +++ /dev/null @@ -1,695 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { Airdrop } from "contracts/prebuilts/airdrop/Airdrop.sol"; - -// Test imports -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import "../utils/BaseTest.sol"; - -contract ERC721ReceiverCompliant is IERC721Receiver { - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external view virtual override returns (bytes4) { - return this.onERC721Received.selector; - } -} - -contract ERC1155ReceiverCompliant is IERC1155Receiver { - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes calldata data - ) external view virtual override returns (bytes4) { - return this.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address operator, - address from, - uint256[] calldata ids, - uint256[] calldata values, - bytes calldata data - ) external returns (bytes4) { - return this.onERC1155BatchReceived.selector; - } - - function supportsInterface(bytes4 interfaceId) external view returns (bool) {} -} - -contract AirdropBenchmarkTest is BaseTest { - Airdrop internal airdrop; - - bytes32 private constant CONTENT_TYPEHASH_ERC20 = - keccak256("AirdropContentERC20(address recipient,uint256 amount)"); - bytes32 private constant REQUEST_TYPEHASH_ERC20 = - keccak256( - "AirdropRequestERC20(bytes32 uid,address tokenAddress,uint256 expirationTimestamp,AirdropContentERC20[] contents)AirdropContentERC20(address recipient,uint256 amount)" - ); - - bytes32 private constant CONTENT_TYPEHASH_ERC721 = - keccak256("AirdropContentERC721(address recipient,uint256 tokenId)"); - bytes32 private constant REQUEST_TYPEHASH_ERC721 = - keccak256( - "AirdropRequestERC721(bytes32 uid,address tokenAddress,uint256 expirationTimestamp,AirdropContentERC721[] contents)AirdropContentERC721(address recipient,uint256 tokenId)" - ); - - bytes32 private constant CONTENT_TYPEHASH_ERC1155 = - keccak256("AirdropContentERC1155(address recipient,uint256 tokenId,uint256 amount)"); - bytes32 private constant REQUEST_TYPEHASH_ERC1155 = - keccak256( - "AirdropRequestERC1155(bytes32 uid,address tokenAddress,uint256 expirationTimestamp,AirdropContentERC1155[] contents)AirdropContentERC1155(address recipient,uint256 tokenId,uint256 amount)" - ); - - bytes32 private constant NAME_HASH = keccak256(bytes("Airdrop")); - bytes32 private constant VERSION_HASH = keccak256(bytes("1")); - bytes32 private constant TYPE_HASH_EIP712 = - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); - - bytes32 internal domainSeparator; - - function setUp() public override { - super.setUp(); - - address impl = address(new Airdrop()); - - airdrop = Airdrop(payable(address(new TWProxy(impl, abi.encodeCall(Airdrop.initialize, (signer, "")))))); - - domainSeparator = keccak256( - abi.encode(TYPE_HASH_EIP712, NAME_HASH, VERSION_HASH, block.chainid, address(airdrop)) - ); - } - - function _getContentsERC20(uint256 length) internal pure returns (Airdrop.AirdropContentERC20[] memory contents) { - contents = new Airdrop.AirdropContentERC20[](length); - for (uint256 i = 0; i < length; i++) { - contents[i].recipient = address(uint160(i + 10)); - contents[i].amount = i + 10; - } - } - - function _getContentsERC721(uint256 length) internal pure returns (Airdrop.AirdropContentERC721[] memory contents) { - contents = new Airdrop.AirdropContentERC721[](length); - for (uint256 i = 0; i < length; i++) { - contents[i].recipient = address(uint160(i + 10)); - contents[i].tokenId = i; - } - } - - function _getContentsERC1155( - uint256 length - ) internal pure returns (Airdrop.AirdropContentERC1155[] memory contents) { - contents = new Airdrop.AirdropContentERC1155[](length); - for (uint256 i = 0; i < length; i++) { - contents[i].recipient = address(uint160(i + 10)); - contents[i].tokenId = 0; - contents[i].amount = i + 10; - } - } - - function _signReqERC20( - Airdrop.AirdropRequestERC20 memory req, - uint256 privateKey - ) internal view returns (bytes memory signature) { - bytes32[] memory contentHashes = new bytes32[](req.contents.length); - for (uint i = 0; i < req.contents.length; i++) { - contentHashes[i] = keccak256( - abi.encode(CONTENT_TYPEHASH_ERC20, req.contents[i].recipient, req.contents[i].amount) - ); - } - bytes32 contentHash = keccak256(abi.encodePacked(contentHashes)); - - bytes memory dataToHash; - { - dataToHash = abi.encode( - REQUEST_TYPEHASH_ERC20, - req.uid, - req.tokenAddress, - req.expirationTimestamp, - contentHash - ); - } - - { - bytes32 _structHash = keccak256(dataToHash); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, _structHash)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); - - signature = abi.encodePacked(r, s, v); - } - } - - function _signReqERC721( - Airdrop.AirdropRequestERC721 memory req, - uint256 privateKey - ) internal view returns (bytes memory signature) { - bytes32[] memory contentHashes = new bytes32[](req.contents.length); - for (uint i = 0; i < req.contents.length; i++) { - contentHashes[i] = keccak256( - abi.encode(CONTENT_TYPEHASH_ERC721, req.contents[i].recipient, req.contents[i].tokenId) - ); - } - bytes32 contentHash = keccak256(abi.encodePacked(contentHashes)); - - bytes memory dataToHash; - { - dataToHash = abi.encode( - REQUEST_TYPEHASH_ERC721, - req.uid, - req.tokenAddress, - req.expirationTimestamp, - contentHash - ); - } - - { - bytes32 _structHash = keccak256(dataToHash); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, _structHash)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); - - signature = abi.encodePacked(r, s, v); - } - } - - function _signReqERC1155( - Airdrop.AirdropRequestERC1155 memory req, - uint256 privateKey - ) internal view returns (bytes memory signature) { - bytes32[] memory contentHashes = new bytes32[](req.contents.length); - for (uint i = 0; i < req.contents.length; i++) { - contentHashes[i] = keccak256( - abi.encode( - CONTENT_TYPEHASH_ERC1155, - req.contents[i].recipient, - req.contents[i].tokenId, - req.contents[i].amount - ) - ); - } - bytes32 contentHash = keccak256(abi.encodePacked(contentHashes)); - - bytes memory dataToHash; - { - dataToHash = abi.encode( - REQUEST_TYPEHASH_ERC1155, - req.uid, - req.tokenAddress, - req.expirationTimestamp, - contentHash - ); - } - - { - bytes32 _structHash = keccak256(dataToHash); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, _structHash)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); - - signature = abi.encodePacked(r, s, v); - } - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Push ERC20 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_airdropPush_erc20_10() public { - vm.pauseGasMetering(); - - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(10); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC20(address(erc20), contents); - } - - function test_benchmark_airdropPush_erc20_100() public { - vm.pauseGasMetering(); - - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(100); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC20(address(erc20), contents); - } - - function test_benchmark_airdropPush_erc20_1000() public { - vm.pauseGasMetering(); - - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(1000); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC20(address(erc20), contents); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Signature ERC20 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_airdropSignature_erc20_10() public { - vm.pauseGasMetering(); - - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(10); - Airdrop.AirdropRequestERC20 memory req = Airdrop.AirdropRequestERC20({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc20), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC20(req, privateKey); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC20WithSignature(req, signature); - } - - function test_benchmark_airdropSignature_erc20_100() public { - vm.pauseGasMetering(); - - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(100); - Airdrop.AirdropRequestERC20 memory req = Airdrop.AirdropRequestERC20({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc20), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC20(req, privateKey); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC20WithSignature(req, signature); - } - - function test_benchmark_airdropSignature_erc20_1000() public { - vm.pauseGasMetering(); - - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - Airdrop.AirdropContentERC20[] memory contents = _getContentsERC20(1000); - Airdrop.AirdropRequestERC20 memory req = Airdrop.AirdropRequestERC20({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc20), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC20(req, privateKey); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC20WithSignature(req, signature); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Claim ERC20 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_airdropClaim_erc20() public { - vm.pauseGasMetering(); - - erc20.mint(signer, 100 ether); - vm.prank(signer); - erc20.approve(address(airdrop), 100 ether); - - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - // set merkle root - vm.prank(signer); - airdrop.setMerkleRoot(address(erc20), root, true); - - // generate proof - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 5; - - vm.prank(receiver); - vm.resumeGasMetering(); - airdrop.claimERC20(address(erc20), receiver, quantity, proofs); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Push ERC721 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_airdropPush_erc721_10() public { - vm.pauseGasMetering(); - - erc721.mint(signer, 100); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(10); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC721(address(erc721), contents); - } - - function test_benchmark_airdropPush_erc721_100() public { - vm.pauseGasMetering(); - - erc721.mint(signer, 100); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(100); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC721(address(erc721), contents); - } - - function test_benchmark_airdropPush_erc721_1000() public { - vm.pauseGasMetering(); - - erc721.mint(signer, 1000); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(1000); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC721(address(erc721), contents); - } - - function test_benchmark_airdropPush_erc721ReceiverCompliant() public { - vm.pauseGasMetering(); - - erc721.mint(signer, 100); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = new Airdrop.AirdropContentERC721[](1); - - contents[0].recipient = address(new ERC721ReceiverCompliant()); - contents[0].tokenId = 0; - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC721(address(erc721), contents); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Signature ERC721 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_airdropSignature_erc721_10() public { - vm.pauseGasMetering(); - - erc721.mint(signer, 1000); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(10); - Airdrop.AirdropRequestERC721 memory req = Airdrop.AirdropRequestERC721({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc721), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC721(req, privateKey); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC721WithSignature(req, signature); - } - - function test_benchmark_airdropSignature_erc721_100() public { - vm.pauseGasMetering(); - - erc721.mint(signer, 1000); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(100); - Airdrop.AirdropRequestERC721 memory req = Airdrop.AirdropRequestERC721({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc721), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC721(req, privateKey); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC721WithSignature(req, signature); - } - - function test_benchmark_airdropSignature_erc721_1000() public { - vm.pauseGasMetering(); - - erc721.mint(signer, 1000); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC721[] memory contents = _getContentsERC721(1000); - Airdrop.AirdropRequestERC721 memory req = Airdrop.AirdropRequestERC721({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc721), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC721(req, privateKey); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC721WithSignature(req, signature); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Claim ERC721 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_airdropClaim_erc721() public { - vm.pauseGasMetering(); - - erc721.mint(signer, 100); - vm.prank(signer); - erc721.setApprovalForAll(address(airdrop), true); - - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - // set merkle root - vm.prank(signer); - airdrop.setMerkleRoot(address(erc721), root, true); - - // generate proof - inputs[1] = "src/test/scripts/getProofAirdrop.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 tokenId = 5; - - vm.prank(receiver); - vm.resumeGasMetering(); - airdrop.claimERC721(address(erc721), receiver, tokenId, proofs); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Push ERC1155 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_airdropPush_erc1155_10() public { - vm.pauseGasMetering(); - - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(10); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC1155(address(erc1155), contents); - } - - function test_benchmark_airdropPush_erc1155_100() public { - vm.pauseGasMetering(); - - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(100); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC1155(address(erc1155), contents); - } - - function test_benchmark_airdropPush_erc1155_1000() public { - vm.pauseGasMetering(); - - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(1000); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC1155(address(erc1155), contents); - } - - function test_benchmark_airdropPush_erc1155ReceiverCompliant() public { - vm.pauseGasMetering(); - - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = new Airdrop.AirdropContentERC1155[](1); - - contents[0].recipient = address(new ERC1155ReceiverCompliant()); - contents[0].tokenId = 0; - contents[0].amount = 100; - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC1155(address(erc1155), contents); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Signature ERC1155 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_airdropSignature_erc115_10() public { - vm.pauseGasMetering(); - - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(10); - Airdrop.AirdropRequestERC1155 memory req = Airdrop.AirdropRequestERC1155({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc1155), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC1155(req, privateKey); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC1155WithSignature(req, signature); - } - - function test_benchmark_airdropSignature_erc115_100() public { - vm.pauseGasMetering(); - - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(100); - Airdrop.AirdropRequestERC1155 memory req = Airdrop.AirdropRequestERC1155({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc1155), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC1155(req, privateKey); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC1155WithSignature(req, signature); - } - - function test_benchmark_airdropSignature_erc115_1000() public { - vm.pauseGasMetering(); - - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - Airdrop.AirdropContentERC1155[] memory contents = _getContentsERC1155(1000); - Airdrop.AirdropRequestERC1155 memory req = Airdrop.AirdropRequestERC1155({ - uid: bytes32(uint256(1)), - tokenAddress: address(erc1155), - expirationTimestamp: 1000, - contents: contents - }); - bytes memory signature = _signReqERC1155(req, privateKey); - - vm.prank(signer); - vm.resumeGasMetering(); - airdrop.airdropERC1155WithSignature(req, signature); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Airdrop Claim ERC1155 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_airdropClaim_erc1155() public { - vm.pauseGasMetering(); - - erc1155.mint(signer, 0, 100 ether); - vm.prank(signer); - erc1155.setApprovalForAll(address(airdrop), true); - - string[] memory inputs = new string[](4); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop1155.ts"; - inputs[2] = Strings.toString(uint256(0)); - inputs[3] = Strings.toString(uint256(5)); - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - // set merkle root - vm.prank(signer); - airdrop.setMerkleRoot(address(erc1155), root, true); - - // generate proof - inputs[1] = "src/test/scripts/getProofAirdrop1155.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - uint256 quantity = 5; - - vm.prank(receiver); - vm.resumeGasMetering(); - airdrop.claimERC1155(address(erc1155), receiver, 0, quantity, proofs); - } -} diff --git a/src/test/benchmark/AirdropERC1155Benchmark.t.sol b/src/test/benchmark/AirdropERC1155Benchmark.t.sol deleted file mode 100644 index 389903565..000000000 --- a/src/test/benchmark/AirdropERC1155Benchmark.t.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { AirdropERC1155, IAirdropERC1155 } from "contracts/prebuilts/unaudited/airdrop/AirdropERC1155.sol"; - -// Test imports -import { Wallet } from "../utils/Wallet.sol"; -import "../utils/BaseTest.sol"; - -contract AirdropERC1155BenchmarkTest is BaseTest { - AirdropERC1155 internal drop; - - Wallet internal tokenOwner; - - IAirdropERC1155.AirdropContent[] internal _contentsOne; - IAirdropERC1155.AirdropContent[] internal _contentsTwo; - - uint256 countOne; - uint256 countTwo; - - function setUp() public override { - super.setUp(); - - drop = AirdropERC1155(getContract("AirdropERC1155")); - - tokenOwner = getWallet(); - - erc1155.mint(address(tokenOwner), 0, 1000); - erc1155.mint(address(tokenOwner), 1, 2000); - erc1155.mint(address(tokenOwner), 2, 3000); - erc1155.mint(address(tokenOwner), 3, 4000); - erc1155.mint(address(tokenOwner), 4, 5000); - - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(drop), true); - - countOne = 1000; - countTwo = 200; - - for (uint256 i = 0; i < countOne; i++) { - _contentsOne.push( - IAirdropERC1155.AirdropContent({ recipient: getActor(uint160(i)), tokenId: i % 5, amount: 5 }) - ); - } - - for (uint256 i = countOne; i < countOne + countTwo; i++) { - _contentsTwo.push( - IAirdropERC1155.AirdropContent({ recipient: getActor(uint160(i)), tokenId: i % 5, amount: 5 }) - ); - } - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: AirdropERC1155 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_airdropERC1155_airdrop() public { - vm.pauseGasMetering(); - vm.prank(deployer); - vm.resumeGasMetering(); - drop.airdropERC1155(address(erc1155), address(tokenOwner), _contentsOne); - } -} diff --git a/src/test/benchmark/AirdropERC20Benchmark.t.sol b/src/test/benchmark/AirdropERC20Benchmark.t.sol deleted file mode 100644 index d36ba5f49..000000000 --- a/src/test/benchmark/AirdropERC20Benchmark.t.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { AirdropERC20, IAirdropERC20 } from "contracts/prebuilts/unaudited/airdrop/AirdropERC20.sol"; - -// Test imports -import { Wallet } from "../utils/Wallet.sol"; -import "../utils/BaseTest.sol"; - -import "../mocks/MockERC20NonCompliant.sol"; - -contract AirdropERC20BenchmarkTest is BaseTest { - AirdropERC20 internal drop; - - Wallet internal tokenOwner; - - IAirdropERC20.AirdropContent[] internal _contentsOne; - IAirdropERC20.AirdropContent[] internal _contentsTwo; - - uint256 countOne; - uint256 countTwo; - - function setUp() public override { - super.setUp(); - - drop = AirdropERC20(getContract("AirdropERC20")); - - tokenOwner = getWallet(); - - erc20.mint(address(tokenOwner), 10_000 ether); - tokenOwner.setAllowanceERC20(address(erc20), address(drop), type(uint256).max); - - countOne = 1000; - countTwo = 200; - - for (uint256 i = 0; i < countOne; i++) { - _contentsOne.push(IAirdropERC20.AirdropContent({ recipient: getActor(uint160(i)), amount: 10 ether })); - } - - for (uint256 i = countOne; i < countOne + countTwo; i++) { - _contentsTwo.push(IAirdropERC20.AirdropContent({ recipient: getActor(uint160(i)), amount: 10 ether })); - } - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: AirdropERC20 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_airdropERC20_airdrop() public { - vm.pauseGasMetering(); - vm.prank(deployer); - vm.resumeGasMetering(); - drop.airdropERC20(address(erc20), address(tokenOwner), _contentsOne); - } -} diff --git a/src/test/benchmark/AirdropERC721Benchmark.t.sol b/src/test/benchmark/AirdropERC721Benchmark.t.sol deleted file mode 100644 index 407636572..000000000 --- a/src/test/benchmark/AirdropERC721Benchmark.t.sol +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { AirdropERC721, IAirdropERC721 } from "contracts/prebuilts/unaudited/airdrop/AirdropERC721.sol"; - -// Test imports -import { Wallet } from "../utils/Wallet.sol"; -import "../utils/BaseTest.sol"; - -contract AirdropERC721BenchmarkTest is BaseTest { - AirdropERC721 internal drop; - - Wallet internal tokenOwner; - - IAirdropERC721.AirdropContent[] internal _contentsOne; - IAirdropERC721.AirdropContent[] internal _contentsTwo; - - uint256 countOne; - uint256 countTwo; - - function setUp() public override { - super.setUp(); - - drop = AirdropERC721(getContract("AirdropERC721")); - - tokenOwner = getWallet(); - - erc721.mint(address(tokenOwner), 1500); - tokenOwner.setApprovalForAllERC721(address(erc721), address(drop), true); - - countOne = 1000; - countTwo = 200; - - for (uint256 i = 0; i < countOne; i++) { - _contentsOne.push(IAirdropERC721.AirdropContent({ recipient: getActor(uint160(i)), tokenId: i })); - } - - for (uint256 i = countOne; i < countOne + countTwo; i++) { - _contentsTwo.push(IAirdropERC721.AirdropContent({ recipient: getActor(uint160(i)), tokenId: i })); - } - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: AirdropERC721 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_airdropERC721_airdrop() public { - vm.pauseGasMetering(); - vm.prank(deployer); - vm.resumeGasMetering(); - drop.airdropERC721(address(erc721), address(tokenOwner), _contentsOne); - } -} diff --git a/src/test/benchmark/DropERC1155Benchmark.t.sol b/src/test/benchmark/DropERC1155Benchmark.t.sol deleted file mode 100644 index 093be4089..000000000 --- a/src/test/benchmark/DropERC1155Benchmark.t.sol +++ /dev/null @@ -1,288 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155, IPermissions, ILazyMint } from "contracts/prebuilts/drop/DropERC1155.sol"; - -// Test imports -import "../utils/BaseTest.sol"; - -contract DropERC1155BenchmarkTest is BaseTest { - using Strings for uint256; - using Strings for address; - - event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI); - event TokenURIRevealed(uint256 indexed index, string revealedURI); - - DropERC1155 public drop; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - drop = DropERC1155(getContract("DropERC1155")); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - } - - /*/////////////////////////////////////////////////////////////// - DropERC1155 benchmark - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_dropERC1155_claim() public { - vm.pauseGasMetering(); - uint256 _tokenId = 0; - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "5"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 5; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - vm.prank(receiver, receiver); - vm.resumeGasMetering(); - drop.claim(receiver, _tokenId, 100, address(erc20), 5, alp, ""); - } - - function test_benchmark_dropERC1155_setClaimConditions_five_conditions() public { - vm.pauseGasMetering(); - uint256 _tokenId = 0; - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "5"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 5; - alp.currency = address(erc20); - - vm.warp(1); - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](5); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - conditions[1].maxClaimableSupply = 600; - conditions[1].pricePerToken = 20; - conditions[1].startTimestamp = 100000; - - conditions[2].maxClaimableSupply = 700; - conditions[2].pricePerToken = 30; - conditions[2].startTimestamp = 200000; - - conditions[3].maxClaimableSupply = 800; - conditions[3].pricePerToken = 40; - conditions[3].startTimestamp = 300000; - - conditions[4].maxClaimableSupply = 700; - conditions[4].pricePerToken = 30; - conditions[4].startTimestamp = 400000; - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - vm.resumeGasMetering(); - drop.setClaimConditions(_tokenId, conditions, false); - } - - function test_benchmark_dropERC1155_lazyMint() public { - vm.pauseGasMetering(); - vm.prank(deployer); - vm.resumeGasMetering(); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - } - - // function test_benchmark_dropERC1155_setClaimConditions_one_condition() public { - // vm.pauseGasMetering(); - // uint256 _tokenId = 0; - // string[] memory inputs = new string[](5); - - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/generateRoot.ts"; - // inputs[2] = "300"; - // inputs[3] = "5"; - // inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - // bytes memory result = vm.ffi(inputs); - // // revert(); - // bytes32 root = abi.decode(result, (bytes32)); - - // inputs[1] = "src/test/scripts/getProof.ts"; - // result = vm.ffi(inputs); - // bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - // DropERC1155.AllowlistProof memory alp; - // alp.proof = proofs; - // alp.quantityLimitPerWallet = 300; - // alp.pricePerToken = 5; - // alp.currency = address(erc20); - - // vm.warp(1); - - // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - // DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - // conditions[0].maxClaimableSupply = 500; - // conditions[0].quantityLimitPerWallet = 10; - // conditions[0].merkleRoot = root; - // conditions[0].pricePerToken = 10; - // conditions[0].currency = address(erc20); - - // vm.prank(deployer); - // drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - // vm.prank(deployer); - // vm.resumeGasMetering(); - // drop.setClaimConditions(_tokenId, conditions, false); - // } - - // function test_benchmark_dropERC1155_setClaimConditions_two_conditions() public { - // vm.pauseGasMetering(); - // uint256 _tokenId = 0; - // string[] memory inputs = new string[](5); - - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/generateRoot.ts"; - // inputs[2] = "300"; - // inputs[3] = "5"; - // inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - // bytes memory result = vm.ffi(inputs); - // // revert(); - // bytes32 root = abi.decode(result, (bytes32)); - - // inputs[1] = "src/test/scripts/getProof.ts"; - // result = vm.ffi(inputs); - // bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - // DropERC1155.AllowlistProof memory alp; - // alp.proof = proofs; - // alp.quantityLimitPerWallet = 300; - // alp.pricePerToken = 5; - // alp.currency = address(erc20); - - // vm.warp(1); - - // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - // DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](2); - // conditions[0].maxClaimableSupply = 500; - // conditions[0].quantityLimitPerWallet = 10; - // conditions[0].merkleRoot = root; - // conditions[0].pricePerToken = 10; - // conditions[0].currency = address(erc20); - - // conditions[1].maxClaimableSupply = 600; - // conditions[1].pricePerToken = 20; - // conditions[1].startTimestamp = 100000; - - // vm.prank(deployer); - // drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - // vm.prank(deployer); - // vm.resumeGasMetering(); - // drop.setClaimConditions(_tokenId, conditions, false); - // } - - // function test_benchmark_dropERC1155_setClaimConditions_three_conditions() public { - // vm.pauseGasMetering(); - // uint256 _tokenId = 0; - // string[] memory inputs = new string[](5); - - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/generateRoot.ts"; - // inputs[2] = "300"; - // inputs[3] = "5"; - // inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - // bytes memory result = vm.ffi(inputs); - // // revert(); - // bytes32 root = abi.decode(result, (bytes32)); - - // inputs[1] = "src/test/scripts/getProof.ts"; - // result = vm.ffi(inputs); - // bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - // DropERC1155.AllowlistProof memory alp; - // alp.proof = proofs; - // alp.quantityLimitPerWallet = 300; - // alp.pricePerToken = 5; - // alp.currency = address(erc20); - - // vm.warp(1); - - // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - // DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](3); - // conditions[0].maxClaimableSupply = 500; - // conditions[0].quantityLimitPerWallet = 10; - // conditions[0].merkleRoot = root; - // conditions[0].pricePerToken = 10; - // conditions[0].currency = address(erc20); - - // conditions[1].maxClaimableSupply = 600; - // conditions[1].pricePerToken = 20; - // conditions[1].startTimestamp = 100000; - - // conditions[2].maxClaimableSupply = 700; - // conditions[2].pricePerToken = 30; - // conditions[2].startTimestamp = 200000; - - // vm.prank(deployer); - // drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - // vm.prank(deployer); - // vm.resumeGasMetering(); - // drop.setClaimConditions(_tokenId, conditions, false); - // } -} diff --git a/src/test/benchmark/DropERC20Benchmark.t.sol b/src/test/benchmark/DropERC20Benchmark.t.sol deleted file mode 100644 index 53f432604..000000000 --- a/src/test/benchmark/DropERC20Benchmark.t.sol +++ /dev/null @@ -1,261 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC20 } from "contracts/prebuilts/drop/DropERC20.sol"; - -// Test imports -import "../utils/BaseTest.sol"; - -contract DropERC20BenchmarkTest is BaseTest { - using Strings for uint256; - using Strings for address; - - DropERC20 public drop; - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - drop = DropERC20(getContract("DropERC20")); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - } - - /*/////////////////////////////////////////////////////////////// - DropERC20 benchmark - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_dropERC20_setClaimConditions_five_conditions() public { - vm.pauseGasMetering(); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(uint256(300 ether)); - inputs[3] = Strings.toString(uint256(1 ether)); - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC20.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300 ether; - alp.pricePerToken = 1 ether; - alp.currency = address(erc20); - - vm.warp(1); - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](5); - conditions[0].maxClaimableSupply = 500 ether; - conditions[0].quantityLimitPerWallet = 10 ether; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 5 ether; - conditions[0].currency = address(erc20); - - conditions[1].maxClaimableSupply = 600; - conditions[1].pricePerToken = 20; - conditions[1].startTimestamp = 100000; - - conditions[2].maxClaimableSupply = 700; - conditions[2].pricePerToken = 30; - conditions[2].startTimestamp = 200000; - - conditions[3].maxClaimableSupply = 800; - conditions[3].pricePerToken = 40; - conditions[3].startTimestamp = 300000; - - conditions[4].maxClaimableSupply = 700; - conditions[4].pricePerToken = 30; - conditions[4].startTimestamp = 400000; - - vm.prank(deployer); - vm.resumeGasMetering(); - drop.setClaimConditions(conditions, false); - } - - function test_benchmark_dropERC20_claim() public { - vm.pauseGasMetering(); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(uint256(300 ether)); - inputs[3] = Strings.toString(uint256(1 ether)); - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC20.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300 ether; - alp.pricePerToken = 1 ether; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500 ether; - conditions[0].quantityLimitPerWallet = 10 ether; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 5 ether; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - erc20.mint(receiver, 1000 ether); - vm.prank(receiver); - erc20.approve(address(drop), 1000 ether); - - vm.prank(receiver, receiver); - vm.resumeGasMetering(); - drop.claim(receiver, 100 ether, address(erc20), 1 ether, alp, ""); - } - - // function test_benchmark_dropERC20_setClaimConditions_one_condition() public { - // vm.pauseGasMetering(); - // string[] memory inputs = new string[](5); - - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/generateRoot.ts"; - // inputs[2] = Strings.toString(300 ether); - // inputs[3] = Strings.toString(1 ether); - // inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - // bytes memory result = vm.ffi(inputs); - // // revert(); - // bytes32 root = abi.decode(result, (bytes32)); - - // inputs[1] = "src/test/scripts/getProof.ts"; - // result = vm.ffi(inputs); - // bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - // DropERC20.AllowlistProof memory alp; - // alp.proof = proofs; - // alp.quantityLimitPerWallet = 300 ether; - // alp.pricePerToken = 1 ether; - // alp.currency = address(erc20); - - // vm.warp(1); - - // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - // DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); - // conditions[0].maxClaimableSupply = 500 ether; - // conditions[0].quantityLimitPerWallet = 10 ether; - // conditions[0].merkleRoot = root; - // conditions[0].pricePerToken = 5 ether; - // conditions[0].currency = address(erc20); - - // vm.prank(deployer); - // vm.resumeGasMetering(); - // drop.setClaimConditions(conditions, false); - // } - - // function test_benchmark_dropERC20_setClaimConditions_two_conditions() public { - // vm.pauseGasMetering(); - // string[] memory inputs = new string[](5); - - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/generateRoot.ts"; - // inputs[2] = Strings.toString(300 ether); - // inputs[3] = Strings.toString(1 ether); - // inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - // bytes memory result = vm.ffi(inputs); - // // revert(); - // bytes32 root = abi.decode(result, (bytes32)); - - // inputs[1] = "src/test/scripts/getProof.ts"; - // result = vm.ffi(inputs); - // bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - // DropERC20.AllowlistProof memory alp; - // alp.proof = proofs; - // alp.quantityLimitPerWallet = 300 ether; - // alp.pricePerToken = 1 ether; - // alp.currency = address(erc20); - - // vm.warp(1); - - // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - // DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](2); - // conditions[0].maxClaimableSupply = 500 ether; - // conditions[0].quantityLimitPerWallet = 10 ether; - // conditions[0].merkleRoot = root; - // conditions[0].pricePerToken = 5 ether; - // conditions[0].currency = address(erc20); - - // conditions[1].maxClaimableSupply = 600; - // conditions[1].pricePerToken = 20; - // conditions[1].startTimestamp = 100000; - - // vm.prank(deployer); - // vm.resumeGasMetering(); - // drop.setClaimConditions(conditions, false); - // } - - // function test_benchmark_dropERC20_setClaimConditions_three_conditions() public { - // vm.pauseGasMetering(); - // string[] memory inputs = new string[](5); - - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/generateRoot.ts"; - // inputs[2] = Strings.toString(300 ether); - // inputs[3] = Strings.toString(1 ether); - // inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - // bytes memory result = vm.ffi(inputs); - // // revert(); - // bytes32 root = abi.decode(result, (bytes32)); - - // inputs[1] = "src/test/scripts/getProof.ts"; - // result = vm.ffi(inputs); - // bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - // DropERC20.AllowlistProof memory alp; - // alp.proof = proofs; - // alp.quantityLimitPerWallet = 300 ether; - // alp.pricePerToken = 1 ether; - // alp.currency = address(erc20); - - // vm.warp(1); - - // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - // DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](3); - // conditions[0].maxClaimableSupply = 500 ether; - // conditions[0].quantityLimitPerWallet = 10 ether; - // conditions[0].merkleRoot = root; - // conditions[0].pricePerToken = 5 ether; - // conditions[0].currency = address(erc20); - - // conditions[1].maxClaimableSupply = 600; - // conditions[1].pricePerToken = 20; - // conditions[1].startTimestamp = 100000; - - // conditions[2].maxClaimableSupply = 700; - // conditions[2].pricePerToken = 30; - // conditions[2].startTimestamp = 200000; - - // vm.prank(deployer); - // vm.resumeGasMetering(); - // drop.setClaimConditions(conditions, false); - // } -} diff --git a/src/test/benchmark/DropERC721Benchmark.t.sol b/src/test/benchmark/DropERC721Benchmark.t.sol deleted file mode 100644 index d11507c06..000000000 --- a/src/test/benchmark/DropERC721Benchmark.t.sol +++ /dev/null @@ -1,458 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721, IDelayedReveal, ERC721AUpgradeable, IPermissions, ILazyMint } from "contracts/prebuilts/drop/DropERC721.sol"; - -// Test imports -import { IERC721AUpgradeable } from "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "../utils/BaseTest.sol"; - -contract DropERC721BenchmarkTest is BaseTest { - using Strings for uint256; - using Strings for address; - - DropERC721 public drop; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - drop = DropERC721(getContract("DropERC721")); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - } - - /*/////////////////////////////////////////////////////////////// - DropERC721 benchmark - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_dropERC721_claim_five_tokens() public { - vm.pauseGasMetering(); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "5"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 5; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(receiver, receiver); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - vm.prank(receiver, receiver); - vm.resumeGasMetering(); - drop.claim(receiver, 5, address(erc20), 5, alp, ""); - } - - function test_benchmark_dropERC721_setClaimConditions_five_conditions() public { - vm.pauseGasMetering(); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "5"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 5; - alp.currency = address(erc20); - - vm.warp(1); - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](5); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - conditions[1].maxClaimableSupply = 600; - conditions[1].pricePerToken = 20; - conditions[1].startTimestamp = 100000; - - conditions[2].maxClaimableSupply = 700; - conditions[2].pricePerToken = 30; - conditions[2].startTimestamp = 200000; - - conditions[3].maxClaimableSupply = 800; - conditions[3].pricePerToken = 40; - conditions[3].startTimestamp = 300000; - - conditions[4].maxClaimableSupply = 700; - conditions[4].pricePerToken = 30; - conditions[4].startTimestamp = 400000; - - vm.prank(deployer); - vm.resumeGasMetering(); - drop.setClaimConditions(conditions, false); - } - - function test_benchmark_dropERC721_lazyMint() public { - vm.pauseGasMetering(); - vm.prank(deployer); - vm.resumeGasMetering(); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - } - - function test_benchmark_dropERC721_lazyMint_for_delayed_reveal() public { - vm.pauseGasMetering(); - uint256 amountToLazyMint = 100; - string memory baseURI = "ipfs://"; - bytes memory encryptedBaseURI = "encryptedBaseURI://"; - bytes32 provenanceHash = bytes32("whatever"); - - vm.prank(deployer); - vm.resumeGasMetering(); - drop.lazyMint(amountToLazyMint, baseURI, abi.encode(encryptedBaseURI, provenanceHash)); - } - - function test_benchmark_dropERC721_reveal() public { - vm.pauseGasMetering(); - - bytes memory key = "key"; - uint256 amountToLazyMint = 100; - bytes memory secretURI = "ipfs://"; - string memory placeholderURI = "abcd://"; - bytes memory encryptedURI = drop.encryptDecrypt(secretURI, key); - bytes32 provenanceHash = keccak256(abi.encodePacked(secretURI, key, block.chainid)); - - vm.prank(deployer); - drop.lazyMint(amountToLazyMint, placeholderURI, abi.encode(encryptedURI, provenanceHash)); - - vm.prank(deployer); - vm.resumeGasMetering(); - drop.reveal(0, key); - } - - // function test_benchmark_dropERC721_claim_one_token() public { - // vm.pauseGasMetering(); - // string[] memory inputs = new string[](5); - - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/generateRoot.ts"; - // inputs[2] = "300"; - // inputs[3] = "5"; - // inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - // bytes memory result = vm.ffi(inputs); - // // revert(); - // bytes32 root = abi.decode(result, (bytes32)); - - // inputs[1] = "src/test/scripts/getProof.ts"; - // result = vm.ffi(inputs); - // bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - // DropERC721.AllowlistProof memory alp; - // alp.proof = proofs; - // alp.quantityLimitPerWallet = 300; - // alp.pricePerToken = 5; - // alp.currency = address(erc20); - - // vm.warp(1); - - // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - // DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - // conditions[0].maxClaimableSupply = 500; - // conditions[0].quantityLimitPerWallet = 10; - // conditions[0].merkleRoot = root; - // conditions[0].pricePerToken = 10; - // conditions[0].currency = address(erc20); - - // vm.prank(deployer); - // drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - // vm.prank(deployer); - // drop.setClaimConditions(conditions, false); - - // vm.prank(receiver, receiver); - - // erc20.mint(receiver, 10000); - // vm.prank(receiver); - // erc20.approve(address(drop), 10000); - - // vm.prank(receiver, receiver); - // vm.resumeGasMetering(); - // drop.claim(receiver, 1, address(erc20), 5, alp, ""); - // } - - // function test_benchmark_dropERC721_claim_two_tokens() public { - // vm.pauseGasMetering(); - // string[] memory inputs = new string[](5); - - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/generateRoot.ts"; - // inputs[2] = "300"; - // inputs[3] = "5"; - // inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - // bytes memory result = vm.ffi(inputs); - // // revert(); - // bytes32 root = abi.decode(result, (bytes32)); - - // inputs[1] = "src/test/scripts/getProof.ts"; - // result = vm.ffi(inputs); - // bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - // DropERC721.AllowlistProof memory alp; - // alp.proof = proofs; - // alp.quantityLimitPerWallet = 300; - // alp.pricePerToken = 5; - // alp.currency = address(erc20); - - // vm.warp(1); - - // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - // DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - // conditions[0].maxClaimableSupply = 500; - // conditions[0].quantityLimitPerWallet = 10; - // conditions[0].merkleRoot = root; - // conditions[0].pricePerToken = 10; - // conditions[0].currency = address(erc20); - - // vm.prank(deployer); - // drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - // vm.prank(deployer); - // drop.setClaimConditions(conditions, false); - - // vm.prank(receiver, receiver); - - // erc20.mint(receiver, 10000); - // vm.prank(receiver); - // erc20.approve(address(drop), 10000); - - // vm.prank(receiver, receiver); - // vm.resumeGasMetering(); - // drop.claim(receiver, 2, address(erc20), 5, alp, ""); - // } - - // function test_benchmark_dropERC721_claim_three_tokens() public { - // vm.pauseGasMetering(); - // string[] memory inputs = new string[](5); - - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/generateRoot.ts"; - // inputs[2] = "300"; - // inputs[3] = "5"; - // inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - // bytes memory result = vm.ffi(inputs); - // // revert(); - // bytes32 root = abi.decode(result, (bytes32)); - - // inputs[1] = "src/test/scripts/getProof.ts"; - // result = vm.ffi(inputs); - // bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - // DropERC721.AllowlistProof memory alp; - // alp.proof = proofs; - // alp.quantityLimitPerWallet = 300; - // alp.pricePerToken = 5; - // alp.currency = address(erc20); - - // vm.warp(1); - - // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - // DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - // conditions[0].maxClaimableSupply = 500; - // conditions[0].quantityLimitPerWallet = 10; - // conditions[0].merkleRoot = root; - // conditions[0].pricePerToken = 10; - // conditions[0].currency = address(erc20); - - // vm.prank(deployer); - // drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - // vm.prank(deployer); - // drop.setClaimConditions(conditions, false); - - // vm.prank(receiver, receiver); - - // erc20.mint(receiver, 10000); - // vm.prank(receiver); - // erc20.approve(address(drop), 10000); - - // vm.prank(receiver, receiver); - // vm.resumeGasMetering(); - // drop.claim(receiver, 3, address(erc20), 5, alp, ""); - // } - - // function test_benchmark_dropERC721_setClaimConditions_one_condition() public { - // vm.pauseGasMetering(); - // string[] memory inputs = new string[](5); - - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/generateRoot.ts"; - // inputs[2] = "300"; - // inputs[3] = "5"; - // inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - // bytes memory result = vm.ffi(inputs); - // // revert(); - // bytes32 root = abi.decode(result, (bytes32)); - - // inputs[1] = "src/test/scripts/getProof.ts"; - // result = vm.ffi(inputs); - // bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - // DropERC721.AllowlistProof memory alp; - // alp.proof = proofs; - // alp.quantityLimitPerWallet = 300; - // alp.pricePerToken = 5; - // alp.currency = address(erc20); - - // vm.warp(1); - - // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - // DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - // conditions[0].maxClaimableSupply = 500; - // conditions[0].quantityLimitPerWallet = 10; - // conditions[0].merkleRoot = root; - // conditions[0].pricePerToken = 10; - // conditions[0].currency = address(erc20); - - // vm.prank(deployer); - // vm.resumeGasMetering(); - // drop.setClaimConditions(conditions, false); - // } - - // function test_benchmark_dropERC721_setClaimConditions_two_conditions() public { - // vm.pauseGasMetering(); - // string[] memory inputs = new string[](5); - - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/generateRoot.ts"; - // inputs[2] = "300"; - // inputs[3] = "5"; - // inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - // bytes memory result = vm.ffi(inputs); - // // revert(); - // bytes32 root = abi.decode(result, (bytes32)); - - // inputs[1] = "src/test/scripts/getProof.ts"; - // result = vm.ffi(inputs); - // bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - // DropERC721.AllowlistProof memory alp; - // alp.proof = proofs; - // alp.quantityLimitPerWallet = 300; - // alp.pricePerToken = 5; - // alp.currency = address(erc20); - - // vm.warp(1); - - // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - // DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](2); - // conditions[0].maxClaimableSupply = 500; - // conditions[0].quantityLimitPerWallet = 10; - // conditions[0].merkleRoot = root; - // conditions[0].pricePerToken = 10; - // conditions[0].currency = address(erc20); - - // conditions[1].maxClaimableSupply = 600; - // conditions[1].pricePerToken = 20; - // conditions[1].startTimestamp = 100000; - - // vm.prank(deployer); - // vm.resumeGasMetering(); - // drop.setClaimConditions(conditions, false); - // } - - // function test_benchmark_dropERC721_setClaimConditions_three_conditions() public { - // vm.pauseGasMetering(); - // string[] memory inputs = new string[](5); - - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/generateRoot.ts"; - // inputs[2] = "300"; - // inputs[3] = "5"; - // inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - // bytes memory result = vm.ffi(inputs); - // // revert(); - // bytes32 root = abi.decode(result, (bytes32)); - - // inputs[1] = "src/test/scripts/getProof.ts"; - // result = vm.ffi(inputs); - // bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - // DropERC721.AllowlistProof memory alp; - // alp.proof = proofs; - // alp.quantityLimitPerWallet = 300; - // alp.pricePerToken = 5; - // alp.currency = address(erc20); - - // vm.warp(1); - - // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - // DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](3); - // conditions[0].maxClaimableSupply = 500; - // conditions[0].quantityLimitPerWallet = 10; - // conditions[0].merkleRoot = root; - // conditions[0].pricePerToken = 10; - // conditions[0].currency = address(erc20); - - // conditions[1].maxClaimableSupply = 600; - // conditions[1].pricePerToken = 20; - // conditions[1].startTimestamp = 100000; - - // conditions[2].maxClaimableSupply = 700; - // conditions[2].pricePerToken = 30; - // conditions[2].startTimestamp = 200000; - - // vm.prank(deployer); - // vm.resumeGasMetering(); - // drop.setClaimConditions(conditions, false); - // } -} diff --git a/src/test/benchmark/EditionStakeBenchmark.t.sol b/src/test/benchmark/EditionStakeBenchmark.t.sol deleted file mode 100644 index f8b604dc4..000000000 --- a/src/test/benchmark/EditionStakeBenchmark.t.sol +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { EditionStake } from "contracts/prebuilts/staking/EditionStake.sol"; - -// Test imports -import "../utils/BaseTest.sol"; - -contract EditionStakeBenchmarkTest is BaseTest { - EditionStake internal stakeContract; - - address internal stakerOne; - address internal stakerTwo; - - uint256 internal defaultTimeUnit; - uint256 internal defaultRewardsPerUnitTime; - - function setUp() public override { - super.setUp(); - - defaultTimeUnit = 60; - defaultRewardsPerUnitTime = 1; - - stakerOne = address(0x345); - stakerTwo = address(0x567); - - erc1155.mint(stakerOne, 0, 100); // mint 100 tokens with id 0 to stakerOne - erc1155.mint(stakerOne, 1, 100); // mint 100 tokens with id 1 to stakerOne - - erc1155.mint(stakerTwo, 0, 100); // mint 100 tokens with id 0 to stakerTwo - erc1155.mint(stakerTwo, 1, 100); // mint 100 tokens with id 1 to stakerTwo - - erc20.mint(deployer, 1000 ether); // mint reward tokens to contract admin - - stakeContract = EditionStake(payable(getContract("EditionStake"))); - - // set approvals - vm.prank(stakerOne); - erc1155.setApprovalForAll(address(stakeContract), true); - - vm.prank(stakerTwo); - erc1155.setApprovalForAll(address(stakeContract), true); - - vm.startPrank(deployer); - erc20.approve(address(stakeContract), type(uint256).max); - stakeContract.depositRewardTokens(100 ether); - // erc20.transfer(address(stakeContract), 100 ether); - vm.stopPrank(); - assertEq(stakeContract.getRewardTokenBalance(), 100 ether); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: EditionStake - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_editionStake_stake() public { - vm.pauseGasMetering(); - - vm.warp(1); - vm.prank(stakerOne); - vm.resumeGasMetering(); - stakeContract.stake(0, 50); - } - - function test_benchmark_editionStake_claimRewards() public { - vm.pauseGasMetering(); - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake(0, 50); - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - - vm.prank(stakerOne); - vm.resumeGasMetering(); - stakeContract.claimRewards(0); - } - - function test_benchmark_editionStake_withdraw() public { - vm.pauseGasMetering(); - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - - vm.prank(stakerTwo); - stakeContract.stake(1, 20); - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - // withdraw partially for stakerOne - vm.prank(stakerOne); - vm.resumeGasMetering(); - stakeContract.withdraw(0, 40); - } -} diff --git a/src/test/benchmark/MultiwrapBenchmark.t.sol b/src/test/benchmark/MultiwrapBenchmark.t.sol deleted file mode 100644 index aef57eccc..000000000 --- a/src/test/benchmark/MultiwrapBenchmark.t.sol +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { Multiwrap } from "contracts/prebuilts/multiwrap/Multiwrap.sol"; -import { ITokenBundle } from "contracts/extension/interface/ITokenBundle.sol"; - -// Test imports -import { MockERC20 } from "../mocks/MockERC20.sol"; -import { Wallet } from "../utils/Wallet.sol"; -import "../utils/BaseTest.sol"; - -contract MultiwrapBenchmarkTest is BaseTest { - /// @dev Emitted when tokens are wrapped. - event TokensWrapped( - address indexed wrapper, - address indexed recipientOfWrappedToken, - uint256 indexed tokenIdOfWrappedToken, - ITokenBundle.Token[] wrappedContents - ); - - /// @dev Emitted when tokens are unwrapped. - event TokensUnwrapped( - address indexed unwrapper, - address indexed recipientOfWrappedContents, - uint256 indexed tokenIdOfWrappedToken - ); - - /*/////////////////////////////////////////////////////////////// - Setup - //////////////////////////////////////////////////////////////*/ - - Multiwrap internal multiwrap; - - Wallet internal tokenOwner; - string internal uriForWrappedToken; - ITokenBundle.Token[] internal wrappedContent; - - function setUp() public override { - super.setUp(); - - // Get target contract - multiwrap = Multiwrap(payable(getContract("Multiwrap"))); - - // Set test vars - tokenOwner = getWallet(); - uriForWrappedToken = "ipfs://baseURI/"; - - wrappedContent.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }) - ); - wrappedContent.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 0, - totalAmount: 1 - }) - ); - wrappedContent.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 0, - totalAmount: 100 - }) - ); - - // Mint tokens-to-wrap to `tokenOwner` - erc20.mint(address(tokenOwner), 10 ether); - erc721.mint(address(tokenOwner), 1); - erc1155.mint(address(tokenOwner), 0, 100); - - // Token owner approves `Multiwrap` to transfer tokens. - tokenOwner.setAllowanceERC20(address(erc20), address(multiwrap), type(uint256).max); - tokenOwner.setApprovalForAllERC721(address(erc721), address(multiwrap), true); - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(multiwrap), true); - - // Grant MINTER_ROLE / requisite wrapping permissions to `tokenOwer` - vm.prank(deployer); - multiwrap.grantRole(keccak256("MINTER_ROLE"), address(tokenOwner)); - } - - /*/////////////////////////////////////////////////////////////// - Multiwrap benchmark - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_multiwrap_wrap() public { - vm.pauseGasMetering(); - address recipient = address(0x123); - vm.prank(address(tokenOwner)); - vm.resumeGasMetering(); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - } - - function test_benchmark_multiwrap_unwrap() public { - vm.pauseGasMetering(); - // ===== setup: wrap tokens ===== - uint256 expectedIdForWrappedToken = multiwrap.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); - - // ===== target test content ===== - - vm.prank(recipient); - vm.resumeGasMetering(); - multiwrap.unwrap(expectedIdForWrappedToken, recipient); - } -} diff --git a/src/test/benchmark/NFTStakeBenchmark.t.sol b/src/test/benchmark/NFTStakeBenchmark.t.sol deleted file mode 100644 index 0f81eef3d..000000000 --- a/src/test/benchmark/NFTStakeBenchmark.t.sol +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { NFTStake } from "contracts/prebuilts/staking/NFTStake.sol"; - -// Test imports -import "../utils/BaseTest.sol"; - -contract NFTStakeBenchmarkTest is BaseTest { - NFTStake internal stakeContract; - - address internal stakerOne; - address internal stakerTwo; - - uint256 internal timeUnit; - uint256 internal rewardsPerUnitTime; - - function setUp() public override { - super.setUp(); - - timeUnit = 60; - rewardsPerUnitTime = 1; - - stakerOne = address(0x345); - stakerTwo = address(0x567); - - erc721.mint(stakerOne, 5); // mint token id 0 to 4 - erc721.mint(stakerTwo, 5); // mint token id 5 to 9 - erc20.mint(deployer, 1000 ether); // mint reward tokens to contract admin - - stakeContract = NFTStake(payable(getContract("NFTStake"))); - - // set approvals - vm.prank(stakerOne); - erc721.setApprovalForAll(address(stakeContract), true); - - vm.prank(stakerTwo); - erc721.setApprovalForAll(address(stakeContract), true); - - vm.startPrank(deployer); - erc20.approve(address(stakeContract), type(uint256).max); - stakeContract.depositRewardTokens(100 ether); - // erc20.transfer(address(stakeContract), 100 ether); - vm.stopPrank(); - assertEq(stakeContract.getRewardTokenBalance(), 100 ether); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: NFTStake - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_nftStake_stake_five_tokens() public { - vm.pauseGasMetering(); - - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](5); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - _tokenIdsOne[3] = 3; - _tokenIdsOne[4] = 4; - - // stake 3 tokens - vm.prank(stakerOne); - vm.resumeGasMetering(); - stakeContract.stake(_tokenIdsOne); - } - - function test_benchmark_nftStake_claimRewards() public { - vm.pauseGasMetering(); - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - vm.prank(stakerOne); - vm.resumeGasMetering(); - stakeContract.claimRewards(); - } - - function test_benchmark_nftStake_withdraw() public { - vm.pauseGasMetering(); - //================ first staker ====================== - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - uint256[] memory _tokensToWithdraw = new uint256[](1); - _tokensToWithdraw[0] = 1; - - vm.prank(stakerOne); - vm.resumeGasMetering(); - stakeContract.withdraw(_tokensToWithdraw); - } - - // function test_benchmark_nftStake_stake_one_token() public { - // vm.pauseGasMetering(); - - // vm.warp(1); - // uint256[] memory _tokenIdsOne = new uint256[](1); - // _tokenIdsOne[0] = 0; - - // // stake 3 tokens - // vm.prank(stakerOne); - // vm.resumeGasMetering(); - // stakeContract.stake(_tokenIdsOne); - // } - - // function test_benchmark_nftStake_stake_two_tokens() public { - // vm.pauseGasMetering(); - - // vm.warp(1); - // uint256[] memory _tokenIdsOne = new uint256[](2); - // _tokenIdsOne[0] = 0; - // _tokenIdsOne[1] = 1; - - // // stake 3 tokens - // vm.prank(stakerOne); - // vm.resumeGasMetering(); - // stakeContract.stake(_tokenIdsOne); - // } - - // function test_benchmark_nftStake_stake_three_tokens() public { - // vm.pauseGasMetering(); - - // vm.warp(1); - // uint256[] memory _tokenIdsOne = new uint256[](3); - // _tokenIdsOne[0] = 0; - // _tokenIdsOne[1] = 1; - // _tokenIdsOne[2] = 2; - - // // stake 3 tokens - // vm.prank(stakerOne); - // vm.resumeGasMetering(); - // stakeContract.stake(_tokenIdsOne); - // } -} diff --git a/src/test/benchmark/PackBenchmark.t.sol b/src/test/benchmark/PackBenchmark.t.sol deleted file mode 100644 index 5048713b0..000000000 --- a/src/test/benchmark/PackBenchmark.t.sol +++ /dev/null @@ -1,222 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { Pack, IERC2981Upgradeable, IERC721Receiver, IERC1155Upgradeable } from "contracts/prebuilts/pack/Pack.sol"; -import { IPack } from "contracts/prebuilts/interface/IPack.sol"; -import { ITokenBundle } from "contracts/extension/interface/ITokenBundle.sol"; - -// Test imports -import { MockERC20 } from "../mocks/MockERC20.sol"; -import { Wallet } from "../utils/Wallet.sol"; -import "../utils/BaseTest.sol"; - -contract PackBenchmarkTest is BaseTest { - /// @notice Emitted when a set of packs is created. - event PackCreated(uint256 indexed packId, address recipient, uint256 totalPacksCreated); - - /// @notice Emitted when a pack is opened. - event PackOpened( - uint256 indexed packId, - address indexed opener, - uint256 numOfPacksOpened, - ITokenBundle.Token[] rewardUnitsDistributed - ); - - Pack internal pack; - - Wallet internal tokenOwner; - string internal packUri; - ITokenBundle.Token[] internal packContents; - ITokenBundle.Token[] internal additionalContents; - uint256[] internal numOfRewardUnits; - uint256[] internal additionalContentsRewardUnits; - - function setUp() public override { - super.setUp(); - - pack = Pack(payable(getContract("Pack"))); - - tokenOwner = getWallet(); - packUri = "ipfs://"; - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 0, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 0, - totalAmount: 100 - }) - ); - numOfRewardUnits.push(20); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }) - ); - numOfRewardUnits.push(50); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 1, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 2, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }) - ); - numOfRewardUnits.push(100); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 3, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 4, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 5, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 1, - totalAmount: 500 - }) - ); - numOfRewardUnits.push(50); - - erc20.mint(address(tokenOwner), 2000 ether); - erc721.mint(address(tokenOwner), 6); - erc1155.mint(address(tokenOwner), 0, 100); - erc1155.mint(address(tokenOwner), 1, 500); - - // additional contents, to check `addPackContents` - additionalContents.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 2, - totalAmount: 200 - }) - ); - additionalContentsRewardUnits.push(50); - - additionalContents.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }) - ); - additionalContentsRewardUnits.push(100); - - tokenOwner.setAllowanceERC20(address(erc20), address(pack), type(uint256).max); - tokenOwner.setApprovalForAllERC721(address(erc721), address(pack), true); - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(pack), true); - - vm.prank(deployer); - pack.grantRole(keccak256("MINTER_ROLE"), address(tokenOwner)); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: Pack - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_pack_createPack() public { - vm.pauseGasMetering(); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - vm.resumeGasMetering(); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - function test_benchmark_pack_addPackContents() public { - vm.pauseGasMetering(); - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, packContents.length); - - erc20.mint(address(tokenOwner), 1000 ether); - erc1155.mint(address(tokenOwner), 2, 200); - - vm.prank(address(tokenOwner)); - vm.resumeGasMetering(); - pack.addPackContents(packId, additionalContents, additionalContentsRewardUnits, recipient); - } - - function test_benchmark_pack_openPack() public { - vm.pauseGasMetering(); - vm.warp(1000); - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 3; - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); - - vm.prank(recipient, recipient); - vm.resumeGasMetering(); - pack.openPack(packId, packsToOpen); - } -} diff --git a/src/test/benchmark/PackVRFDirectBenchmark.t.sol b/src/test/benchmark/PackVRFDirectBenchmark.t.sol deleted file mode 100644 index b20af8f7d..000000000 --- a/src/test/benchmark/PackVRFDirectBenchmark.t.sol +++ /dev/null @@ -1,220 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { PackVRFDirect, IERC2981Upgradeable, IERC721Receiver, IERC1155Upgradeable } from "contracts/prebuilts/pack/PackVRFDirect.sol"; -import { IPack } from "contracts/prebuilts/interface/IPack.sol"; -import { ITokenBundle } from "contracts/extension/interface/ITokenBundle.sol"; - -// Test imports -import { MockERC20 } from "../mocks/MockERC20.sol"; -import { Wallet } from "../utils/Wallet.sol"; -import "../utils/BaseTest.sol"; - -contract PackVRFDirectBenchmarkTest is BaseTest { - /// @notice Emitted when a set of packs is created. - event PackCreated(uint256 indexed packId, address recipient, uint256 totalPacksCreated); - - /// @notice Emitted when the opening of a pack is requested. - event PackOpenRequested(address indexed opener, uint256 indexed packId, uint256 amountToOpen, uint256 requestId); - - /// @notice Emitted when Chainlink VRF fulfills a random number request. - event PackRandomnessFulfilled(uint256 indexed packId, uint256 indexed requestId); - - /// @notice Emitted when a pack is opened. - event PackOpened( - uint256 indexed packId, - address indexed opener, - uint256 numOfPacksOpened, - ITokenBundle.Token[] rewardUnitsDistributed - ); - - PackVRFDirect internal pack; - - Wallet internal tokenOwner; - string internal packUri; - ITokenBundle.Token[] internal packContents; - ITokenBundle.Token[] internal additionalContents; - uint256[] internal numOfRewardUnits; - uint256[] internal additionalContentsRewardUnits; - - function setUp() public virtual override { - super.setUp(); - - pack = PackVRFDirect(payable(getContract("PackVRFDirect"))); - - tokenOwner = getWallet(); - packUri = "ipfs://"; - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 0, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 0, - totalAmount: 100 - }) - ); - numOfRewardUnits.push(20); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }) - ); - numOfRewardUnits.push(50); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 1, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 2, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }) - ); - numOfRewardUnits.push(100); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 3, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 4, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 5, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 1, - totalAmount: 500 - }) - ); - numOfRewardUnits.push(50); - - erc20.mint(address(tokenOwner), 2000 ether); - erc721.mint(address(tokenOwner), 6); - erc1155.mint(address(tokenOwner), 0, 100); - erc1155.mint(address(tokenOwner), 1, 500); - - // additional contents, to check `addPackContents` - additionalContents.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 2, - totalAmount: 200 - }) - ); - additionalContentsRewardUnits.push(50); - - additionalContents.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }) - ); - additionalContentsRewardUnits.push(100); - - tokenOwner.setAllowanceERC20(address(erc20), address(pack), type(uint256).max); - tokenOwner.setApprovalForAllERC721(address(erc721), address(pack), true); - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(pack), true); - - vm.prank(deployer); - pack.grantRole(keccak256("MINTER_ROLE"), address(tokenOwner)); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: PackVRFDirect - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_packvrf_createPack() public { - vm.pauseGasMetering(); - address recipient = address(1); - vm.prank(address(tokenOwner)); - vm.resumeGasMetering(); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - function test_benchmark_packvrf_openPackAndClaimRewards() public { - vm.pauseGasMetering(); - vm.warp(1000); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); - - vm.prank(recipient, recipient); - vm.resumeGasMetering(); - } - - function test_benchmark_packvrf_openPack() public { - vm.pauseGasMetering(); - vm.warp(1000); - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 3; - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); - - vm.prank(recipient, recipient); - vm.resumeGasMetering(); - pack.openPack(packId, packsToOpen); - } -} diff --git a/src/test/benchmark/SignatureDropBenchmark.t.sol b/src/test/benchmark/SignatureDropBenchmark.t.sol deleted file mode 100644 index 1c6607374..000000000 --- a/src/test/benchmark/SignatureDropBenchmark.t.sol +++ /dev/null @@ -1,214 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { SignatureDrop, IDropSinglePhase, IDelayedReveal, ISignatureMintERC721, ERC721AUpgradeable, IPermissions, ILazyMint } from "contracts/prebuilts/signature-drop/SignatureDrop.sol"; - -// Test imports -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "../utils/BaseTest.sol"; - -contract SignatureDropBenchmarkTest is BaseTest { - using Strings for uint256; - using Strings for address; - - event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI); - event TokenURIRevealed(uint256 indexed index, string revealedURI); - event TokensMintedWithSignature( - address indexed signer, - address indexed mintedTo, - uint256 indexed tokenIdMinted, - SignatureDrop.MintRequest mintRequest - ); - - SignatureDrop public sigdrop; - address internal deployerSigner; - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - deployerSigner = signer; - sigdrop = SignatureDrop(getContract("SignatureDrop")); - - erc20.mint(deployerSigner, 1_000 ether); - vm.deal(deployerSigner, 1_000 ether); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,string uri,uint256 quantity,uint256 pricePerToken,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("SignatureMintERC721")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(sigdrop))); - } - - /*/////////////////////////////////////////////////////////////// - SignatureDrop benchmark - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_signatureDrop_claim_five_tokens() public { - vm.pauseGasMetering(); - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - SignatureDrop.AllowlistProof memory alp; - alp.proof = proofs; - - SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.prank(deployerSigner); - sigdrop.setClaimConditions(conditions[0], false); - - vm.prank(getActor(5), getActor(5)); - vm.resumeGasMetering(); - sigdrop.claim(receiver, 5, address(0), 0, alp, ""); - } - - function test_benchmark_signatureDrop_setClaimConditions() public { - vm.pauseGasMetering(); - vm.warp(1); - bytes32[] memory proofs = new bytes32[](0); - - SignatureDrop.AllowlistProof memory alp; - alp.proof = proofs; - - SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployerSigner); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.prank(deployerSigner); - vm.resumeGasMetering(); - sigdrop.setClaimConditions(conditions[0], false); - } - - function test_benchmark_signatureDrop_lazyMint() public { - vm.pauseGasMetering(); - vm.prank(deployerSigner); - vm.resumeGasMetering(); - sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - } - - function test_benchmark_signatureDrop_lazyMint_for_delayed_reveal() public { - vm.pauseGasMetering(); - uint256 amountToLazyMint = 100; - string memory baseURI = "ipfs://"; - bytes memory encryptedBaseURI = "encryptedBaseURI://"; - bytes32 provenanceHash = bytes32("whatever"); - - vm.prank(deployerSigner); - vm.resumeGasMetering(); - sigdrop.lazyMint(amountToLazyMint, baseURI, abi.encode(encryptedBaseURI, provenanceHash)); - } - - function test_benchmark_signatureDrop_reveal() public { - vm.pauseGasMetering(); - - bytes memory key = "key"; - uint256 amountToLazyMint = 100; - bytes memory secretURI = "ipfs://"; - string memory placeholderURI = "abcd://"; - bytes memory encryptedURI = sigdrop.encryptDecrypt(secretURI, key); - bytes32 provenanceHash = keccak256(abi.encodePacked(secretURI, key, block.chainid)); - - vm.prank(deployerSigner); - sigdrop.lazyMint(amountToLazyMint, placeholderURI, abi.encode(encryptedURI, provenanceHash)); - - vm.prank(deployerSigner); - vm.resumeGasMetering(); - sigdrop.reveal(0, key); - } - - // function test_benchmark_signatureDrop_claim_one_token() public { - // vm.pauseGasMetering(); - // vm.warp(1); - - // address receiver = getActor(0); - // bytes32[] memory proofs = new bytes32[](0); - - // SignatureDrop.AllowlistProof memory alp; - // alp.proof = proofs; - - // SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - // conditions[0].maxClaimableSupply = 100; - // conditions[0].quantityLimitPerWallet = 100; - - // vm.prank(deployerSigner); - // sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // vm.prank(deployerSigner); - // sigdrop.setClaimConditions(conditions[0], false); - - // vm.prank(getActor(5), getActor(5)); - // vm.resumeGasMetering(); - // sigdrop.claim(receiver, 1, address(0), 0, alp, ""); - // } - - // function test_benchmark_signatureDrop_claim_two_tokens() public { - // vm.pauseGasMetering(); - // vm.warp(1); - - // address receiver = getActor(0); - // bytes32[] memory proofs = new bytes32[](0); - - // SignatureDrop.AllowlistProof memory alp; - // alp.proof = proofs; - - // SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - // conditions[0].maxClaimableSupply = 100; - // conditions[0].quantityLimitPerWallet = 100; - - // vm.prank(deployerSigner); - // sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // vm.prank(deployerSigner); - // sigdrop.setClaimConditions(conditions[0], false); - - // vm.prank(getActor(5), getActor(5)); - // vm.resumeGasMetering(); - // sigdrop.claim(receiver, 2, address(0), 0, alp, ""); - // } - - // function test_benchmark_signatureDrop_claim_three_tokens() public { - // vm.pauseGasMetering(); - // vm.warp(1); - - // address receiver = getActor(0); - // bytes32[] memory proofs = new bytes32[](0); - - // SignatureDrop.AllowlistProof memory alp; - // alp.proof = proofs; - - // SignatureDrop.ClaimCondition[] memory conditions = new SignatureDrop.ClaimCondition[](1); - // conditions[0].maxClaimableSupply = 100; - // conditions[0].quantityLimitPerWallet = 100; - - // vm.prank(deployerSigner); - // sigdrop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // vm.prank(deployerSigner); - // sigdrop.setClaimConditions(conditions[0], false); - - // vm.prank(getActor(5), getActor(5)); - // vm.resumeGasMetering(); - // sigdrop.claim(receiver, 3, address(0), 0, alp, ""); - // } -} diff --git a/src/test/benchmark/TokenERC1155Benchmark.t.sol b/src/test/benchmark/TokenERC1155Benchmark.t.sol deleted file mode 100644 index 4cd2563d0..000000000 --- a/src/test/benchmark/TokenERC1155Benchmark.t.sol +++ /dev/null @@ -1,181 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { TokenERC1155, IPlatformFee } from "contracts/prebuilts/token/TokenERC1155.sol"; - -// Test imports -import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract TokenERC1155BenchmarkTest is BaseTest { - using Strings for uint256; - - event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri, uint256 quantityMinted); - event TokensMintedWithSignature( - address indexed signer, - address indexed mintedTo, - uint256 indexed tokenIdMinted, - TokenERC1155.MintRequest mintRequest - ); - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); - event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps); - event PrimarySaleRecipientUpdated(address indexed recipient); - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - - TokenERC1155 public tokenContract; - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - TokenERC1155.MintRequest _mintrequest; - bytes _signature; - - address internal deployerSigner; - address internal recipient; - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - deployerSigner = signer; - recipient = address(0x123); - tokenContract = TokenERC1155(getContract("TokenERC1155")); - - erc20.mint(deployerSigner, 1_000); - vm.deal(deployerSigner, 1_000); - - erc20.mint(recipient, 1_000); - vm.deal(recipient, 1_000); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,uint256 tokenId,string uri,uint256 quantity,uint256 pricePerToken,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("TokenERC1155")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) - ); - - // construct default mintrequest - _mintrequest.to = recipient; - _mintrequest.royaltyRecipient = royaltyRecipient; - _mintrequest.royaltyBps = royaltyBps; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.tokenId = type(uint256).max; - _mintrequest.uri = "ipfs://"; - _mintrequest.quantity = 100; - _mintrequest.pricePerToken = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - TokenERC1155.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = bytes.concat( - abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - _request.tokenId, - keccak256(bytes(_request.uri)) - ), - abi.encode( - _request.quantity, - _request.pricePerToken, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ) - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: TokenERC1155 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_tokenERC1155_mintWithSignature_pay_with_ERC20() public { - vm.pauseGasMetering(); - vm.warp(1000); - - // update mintrequest data - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - // approve erc20 tokens to tokenContract - vm.prank(recipient); - erc20.approve(address(tokenContract), _mintrequest.pricePerToken * _mintrequest.quantity); - - // mint with signature - vm.prank(recipient); - vm.resumeGasMetering(); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_benchmark_tokenERC1155_mintWithSignature_pay_with_native_token() public { - vm.pauseGasMetering(); - vm.warp(1000); - - // update mintrequest data - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - // mint with signature - vm.prank(recipient); - vm.resumeGasMetering(); - tokenContract.mintWithSignature{ value: _mintrequest.pricePerToken * _mintrequest.quantity }( - _mintrequest, - _signature - ); - } - - function test_benchmark_tokenERC1155_mintTo() public { - vm.pauseGasMetering(); - string memory _tokenURI = "tokenURI"; - uint256 _amount = 100; - - vm.prank(deployerSigner); - vm.resumeGasMetering(); - tokenContract.mintTo(recipient, type(uint256).max, _tokenURI, _amount); - } - - function test_benchmark_tokenERC1155_burn() public { - vm.pauseGasMetering(); - string memory _tokenURI = "tokenURI"; - uint256 _amount = 100; - - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, type(uint256).max, _tokenURI, _amount); - - vm.prank(recipient); - vm.resumeGasMetering(); - tokenContract.burn(recipient, nextTokenId, _amount); - } -} diff --git a/src/test/benchmark/TokenERC20Benchmark.t.sol b/src/test/benchmark/TokenERC20Benchmark.t.sol deleted file mode 100644 index 6b93bb25a..000000000 --- a/src/test/benchmark/TokenERC20Benchmark.t.sol +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { TokenERC20 } from "contracts/prebuilts/token/TokenERC20.sol"; - -// Test imports -import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract TokenERC20BenchmarkTest is BaseTest { - using Strings for uint256; - - event TokensMinted(address indexed mintedTo, uint256 quantityMinted); - event TokensMintedWithSignature( - address indexed signer, - address indexed mintedTo, - TokenERC20.MintRequest mintRequest - ); - - event PrimarySaleRecipientUpdated(address indexed recipient); - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - - TokenERC20 public tokenContract; - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - bytes32 internal permitTypehash; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - TokenERC20.MintRequest _mintrequest; - bytes _signature; - - address internal deployerSigner; - address internal recipient; - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - deployerSigner = signer; - recipient = address(0x123); - tokenContract = TokenERC20(getContract("TokenERC20")); - - erc20.mint(deployerSigner, 1_000); - vm.deal(deployerSigner, 1_000); - - erc20.mint(recipient, 1_000); - vm.deal(recipient, 1_000); - - typehashMintRequest = keccak256( - "MintRequest(address to,address primarySaleRecipient,uint256 quantity,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes(NAME)); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) - ); - permitTypehash = keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ); - - // construct default mintrequest - _mintrequest.to = recipient; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.quantity = 100; - _mintrequest.price = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - TokenERC20.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.primarySaleRecipient, - _request.quantity, - _request.price, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: TokenERC20 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_tokenERC20_mintWithSignature_pay_with_ERC20() public { - vm.pauseGasMetering(); - vm.warp(1000); - - // update mintrequest data - _mintrequest.price = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - // approve erc20 tokens to tokenContract - vm.prank(recipient); - erc20.approve(address(tokenContract), _mintrequest.price); - - // mint with signature - vm.prank(recipient); - vm.resumeGasMetering(); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_benchmark_tokenERC20_mintWithSignature_pay_with_native_token() public { - vm.pauseGasMetering(); - vm.warp(1000); - - // update mintrequest data - _mintrequest.price = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - // mint with signature - vm.prank(recipient); - vm.resumeGasMetering(); - tokenContract.mintWithSignature{ value: _mintrequest.price }(_mintrequest, _signature); - } - - function test_benchmark_tokenERC20_mintTo() public { - vm.pauseGasMetering(); - uint256 _amount = 100; - - vm.prank(deployerSigner); - vm.resumeGasMetering(); - tokenContract.mintTo(recipient, _amount); - } -} diff --git a/src/test/benchmark/TokenERC721Benchmark.t.sol b/src/test/benchmark/TokenERC721Benchmark.t.sol deleted file mode 100644 index 410709462..000000000 --- a/src/test/benchmark/TokenERC721Benchmark.t.sol +++ /dev/null @@ -1,168 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { TokenERC721 } from "contracts/prebuilts/token/TokenERC721.sol"; - -// Test imports -import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract TokenERC721BenchmarkTest is BaseTest { - using Strings for uint256; - - event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri); - event TokensMintedWithSignature( - address indexed signer, - address indexed mintedTo, - uint256 indexed tokenIdMinted, - TokenERC721.MintRequest mintRequest - ); - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); - event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps); - event PrimarySaleRecipientUpdated(address indexed recipient); - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - - TokenERC721 public tokenContract; - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - TokenERC721.MintRequest _mintrequest; - bytes _signature; - - address internal deployerSigner; - address internal recipient; - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - deployerSigner = signer; - recipient = address(0x123); - tokenContract = TokenERC721(getContract("TokenERC721")); - - erc20.mint(deployerSigner, 1_000); - vm.deal(deployerSigner, 1_000); - - erc20.mint(recipient, 1_000); - vm.deal(recipient, 1_000); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,string uri,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("TokenERC721")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) - ); - - // construct default mintrequest - _mintrequest.to = recipient; - _mintrequest.royaltyRecipient = royaltyRecipient; - _mintrequest.royaltyBps = royaltyBps; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.uri = "ipfs://"; - _mintrequest.price = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - TokenERC721.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - keccak256(bytes(_request.uri)), - _request.price, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: TokenERC721 - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_tokenERC721_mintWithSignature_pay_with_ERC20() public { - vm.pauseGasMetering(); - vm.warp(1000); - - // update mintrequest data - _mintrequest.price = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - // approve erc20 tokens to tokenContract - vm.prank(recipient); - erc20.approve(address(tokenContract), 1); - - // mint with signature - vm.prank(recipient); - vm.resumeGasMetering(); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_benchmark_tokenERC721_mintWithSignature_pay_with_native_token() public { - vm.pauseGasMetering(); - vm.warp(1000); - - // update mintrequest data - _mintrequest.price = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - // mint with signature - vm.prank(recipient); - vm.resumeGasMetering(); - tokenContract.mintWithSignature{ value: 1 }(_mintrequest, _signature); - } - - function test_benchmark_tokenERC721_mintTo() public { - vm.pauseGasMetering(); - string memory _tokenURI = "tokenURI"; - - vm.prank(deployerSigner); - vm.resumeGasMetering(); - tokenContract.mintTo(recipient, _tokenURI); - } - - function test_benchmark_tokenERC721_burn() public { - vm.pauseGasMetering(); - string memory _tokenURI = "tokenURI"; - - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, _tokenURI); - - vm.prank(recipient); - vm.resumeGasMetering(); - tokenContract.burn(nextTokenId); - } -} diff --git a/src/test/benchmark/TokenStakeBenchmark.t.sol b/src/test/benchmark/TokenStakeBenchmark.t.sol deleted file mode 100644 index 5f639614e..000000000 --- a/src/test/benchmark/TokenStakeBenchmark.t.sol +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { TokenStake } from "contracts/prebuilts/staking/TokenStake.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; - -contract TokenStakeBenchmarkTest is BaseTest { - TokenStake internal stakeContract; - - address internal stakerOne; - address internal stakerTwo; - - uint256 internal timeUnit; - uint256 internal rewardRatioNumerator; - uint256 internal rewardRatioDenominator; - - function setUp() public override { - super.setUp(); - - timeUnit = 60; - rewardRatioNumerator = 3; - rewardRatioDenominator = 50; - - stakerOne = address(0x345); - stakerTwo = address(0x567); - - erc20Aux.mint(stakerOne, 1000); // mint 1000 tokens to stakerOne - erc20Aux.mint(stakerTwo, 1000); // mint 1000 tokens to stakerTwo - - erc20.mint(deployer, 1000 ether); // mint reward tokens to contract admin - - stakeContract = TokenStake(payable(getContract("TokenStake"))); - - // set approvals - vm.prank(stakerOne); - erc20Aux.approve(address(stakeContract), type(uint256).max); - - vm.prank(stakerTwo); - erc20Aux.approve(address(stakeContract), type(uint256).max); - - vm.startPrank(deployer); - erc20.approve(address(stakeContract), type(uint256).max); - stakeContract.depositRewardTokens(100 ether); - // erc20.transfer(address(stakeContract), 100 ether); - vm.stopPrank(); - assertEq(stakeContract.getRewardTokenBalance(), 100 ether); - } - - /*/////////////////////////////////////////////////////////////// - Benchmark: TokenStake - //////////////////////////////////////////////////////////////*/ - - function test_benchmark_tokenStake_stake() public { - vm.pauseGasMetering(); - - vm.warp(1); - // stake 400 tokens - vm.prank(stakerOne); - vm.resumeGasMetering(); - stakeContract.stake(400); - } - - function test_benchmark_tokenStake_claimRewards() public { - vm.pauseGasMetering(); - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake(400); - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - vm.prank(stakerOne); - vm.resumeGasMetering(); - stakeContract.claimRewards(); - } - - function test_benchmark_tokenStake_withdraw() public { - vm.pauseGasMetering(); - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(400); - - vm.prank(stakerTwo); - stakeContract.stake(200); - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - // withdraw partially for stakerOne - vm.prank(stakerOne); - vm.resumeGasMetering(); - stakeContract.withdraw(100); - } -} diff --git a/src/test/burn-to-claim-drop-BTT/BurnToClaimDropERC721.t.sol b/src/test/burn-to-claim-drop-BTT/BurnToClaimDropERC721.t.sol deleted file mode 100644 index 7c26fec50..000000000 --- a/src/test/burn-to-claim-drop-BTT/BurnToClaimDropERC721.t.sol +++ /dev/null @@ -1,1885 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../utils/BaseTest.sol"; -import { BurnToClaimDropERC721 } from "contracts/prebuilts/unaudited/burn-to-claim-drop/BurnToClaimDropERC721.sol"; -import { BurnToClaimDrop721Logic, ERC721AUpgradeable, DelayedReveal, LazyMint, Drop, BurnToClaim, PrimarySale, PlatformFee } from "contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Logic.sol"; -import { PermissionsEnumerableImpl } from "contracts/extension/upgradeable/impl/PermissionsEnumerableImpl.sol"; -import { Royalty } from "contracts/extension/upgradeable/Royalty.sol"; -import { BatchMintMetadata } from "contracts/extension/upgradeable/BatchMintMetadata.sol"; -import { IBurnToClaim } from "contracts/extension/interface/IBurnToClaim.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import { Permissions } from "contracts/extension/Permissions.sol"; -import { PermissionsEnumerable } from "contracts/extension/PermissionsEnumerable.sol"; -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; - -contract BurnToClaimDropERC721Test is BaseTest, IExtension { - using Strings for uint256; - using Strings for address; - - event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI); - event TokenURIRevealed(uint256 indexed index, string revealedURI); - - BurnToClaimDrop721Logic public drop; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address dropImpl = address(new BurnToClaimDropERC721(extensions)); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - drop = BurnToClaimDrop721Logic( - payable( - address( - new TWProxy( - dropImpl, - abi.encodeCall( - BurnToClaimDropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ) - ); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](2); - - // Extension: Permissions - address permissions = address(new PermissionsEnumerableImpl()); - - Extension memory extension_permissions; - extension_permissions.metadata = ExtensionMetadata({ - name: "Permissions", - metadataURI: "ipfs://Permissions", - implementation: permissions - }); - - extension_permissions.functions = new ExtensionFunction[](7); - extension_permissions.functions[0] = ExtensionFunction( - Permissions.hasRole.selector, - "hasRole(bytes32,address)" - ); - extension_permissions.functions[1] = ExtensionFunction( - Permissions.hasRoleWithSwitch.selector, - "hasRoleWithSwitch(bytes32,address)" - ); - extension_permissions.functions[2] = ExtensionFunction( - Permissions.grantRole.selector, - "grantRole(bytes32,address)" - ); - extension_permissions.functions[3] = ExtensionFunction( - Permissions.renounceRole.selector, - "renounceRole(bytes32,address)" - ); - extension_permissions.functions[4] = ExtensionFunction( - Permissions.revokeRole.selector, - "revokeRole(bytes32,address)" - ); - extension_permissions.functions[5] = ExtensionFunction( - PermissionsEnumerable.getRoleMemberCount.selector, - "getRoleMemberCount(bytes32)" - ); - extension_permissions.functions[6] = ExtensionFunction( - PermissionsEnumerable.getRoleMember.selector, - "getRoleMember(bytes32,uint256)" - ); - - extensions[0] = extension_permissions; - - address dropLogic = address(new BurnToClaimDrop721Logic()); - - Extension memory extension_drop; - extension_drop.metadata = ExtensionMetadata({ - name: "BurnToClaimDrop721Logic", - metadataURI: "ipfs://BurnToClaimDrop721Logic", - implementation: dropLogic - }); - - extension_drop.functions = new ExtensionFunction[](32); - extension_drop.functions[0] = ExtensionFunction(BurnToClaimDrop721Logic.tokenURI.selector, "tokenURI(uint256)"); - extension_drop.functions[1] = ExtensionFunction( - BurnToClaimDrop721Logic.lazyMint.selector, - "lazyMint(uint256,string,bytes)" - ); - extension_drop.functions[2] = ExtensionFunction( - BurnToClaimDrop721Logic.reveal.selector, - "reveal(uint256,bytes)" - ); - extension_drop.functions[3] = ExtensionFunction(Drop.claimCondition.selector, "claimCondition()"); - extension_drop.functions[4] = ExtensionFunction( - BatchMintMetadata.getBaseURICount.selector, - "getBaseURICount()" - ); - extension_drop.functions[5] = ExtensionFunction( - Drop.claim.selector, - "claim(address,uint256,address,uint256,(bytes32[],uint256,uint256,address),bytes)" - ); - extension_drop.functions[6] = ExtensionFunction( - Drop.setClaimConditions.selector, - "setClaimConditions((uint256,uint256,uint256,uint256,bytes32,uint256,address,string)[],bool)" - ); - extension_drop.functions[7] = ExtensionFunction( - Drop.getActiveClaimConditionId.selector, - "getActiveClaimConditionId()" - ); - extension_drop.functions[8] = ExtensionFunction( - Drop.getClaimConditionById.selector, - "getClaimConditionById(uint256)" - ); - extension_drop.functions[9] = ExtensionFunction( - Drop.getSupplyClaimedByWallet.selector, - "getSupplyClaimedByWallet(uint256,address)" - ); - extension_drop.functions[10] = ExtensionFunction(BurnToClaimDrop721Logic.totalMinted.selector, "totalMinted()"); - extension_drop.functions[11] = ExtensionFunction( - BurnToClaimDrop721Logic.nextTokenIdToMint.selector, - "nextTokenIdToMint()" - ); - extension_drop.functions[12] = ExtensionFunction( - IERC721Upgradeable.setApprovalForAll.selector, - "setApprovalForAll(address,bool)" - ); - extension_drop.functions[13] = ExtensionFunction( - IERC721Upgradeable.approve.selector, - "approve(address,uint256)" - ); - extension_drop.functions[14] = ExtensionFunction( - IERC721Upgradeable.transferFrom.selector, - "transferFrom(address,address,uint256)" - ); - extension_drop.functions[15] = ExtensionFunction(ERC721AUpgradeable.balanceOf.selector, "balanceOf(address)"); - extension_drop.functions[16] = ExtensionFunction( - DelayedReveal.encryptDecrypt.selector, - "encryptDecrypt(bytes,bytes)" - ); - extension_drop.functions[17] = ExtensionFunction( - BurnToClaimDrop721Logic.supportsInterface.selector, - "supportsInterface(bytes4)" - ); - extension_drop.functions[18] = ExtensionFunction(Royalty.royaltyInfo.selector, "royaltyInfo(uint256,uint256)"); - extension_drop.functions[19] = ExtensionFunction( - Royalty.getRoyaltyInfoForToken.selector, - "getRoyaltyInfoForToken(uint256)" - ); - extension_drop.functions[20] = ExtensionFunction( - Royalty.getDefaultRoyaltyInfo.selector, - "getDefaultRoyaltyInfo()" - ); - extension_drop.functions[21] = ExtensionFunction( - Royalty.setDefaultRoyaltyInfo.selector, - "setDefaultRoyaltyInfo(address,uint256)" - ); - extension_drop.functions[22] = ExtensionFunction( - Royalty.setRoyaltyInfoForToken.selector, - "setRoyaltyInfoForToken(uint256,address,uint256)" - ); - extension_drop.functions[23] = ExtensionFunction(IERC721.ownerOf.selector, "ownerOf(uint256)"); - extension_drop.functions[24] = ExtensionFunction(IERC1155.balanceOf.selector, "balanceOf(address,uint256)"); - extension_drop.functions[25] = ExtensionFunction( - BurnToClaim.setBurnToClaimInfo.selector, - "setBurnToClaimInfo((address,uint8,uint256,uint256,address))" - ); - extension_drop.functions[26] = ExtensionFunction( - BurnToClaim.getBurnToClaimInfo.selector, - "getBurnToClaimInfo()" - ); - extension_drop.functions[27] = ExtensionFunction( - BurnToClaim.verifyBurnToClaim.selector, - "verifyBurnToClaim(address,uint256,uint256)" - ); - extension_drop.functions[28] = ExtensionFunction( - BurnToClaimDrop721Logic.burnAndClaim.selector, - "burnAndClaim(uint256,uint256)" - ); - extension_drop.functions[29] = ExtensionFunction( - BurnToClaimDrop721Logic.nextTokenIdToClaim.selector, - "nextTokenIdToClaim()" - ); - extension_drop.functions[30] = ExtensionFunction( - PrimarySale.setPrimarySaleRecipient.selector, - "setPrimarySaleRecipient(address)" - ); - extension_drop.functions[31] = ExtensionFunction( - PlatformFee.setPlatformFeeInfo.selector, - "setPlatformFeeInfo(address,uint256)" - ); - - extensions[1] = extension_drop; - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc. - //////////////////////////////////////////////////////////////*/ - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_revert_nonHolder_renounceRole() public { - address caller = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(caller); - vm.expectRevert( - abi.encodePacked( - "Permissions: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - - Permissions(address(drop)).renounceRole(role, caller); - } - - /** - * note: Tests whether contract reverts when a role admin revokes a role for a non-holder. - */ - function test_revert_revokeRoleForNonHolder() public { - address target = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(deployer); - vm.expectRevert( - abi.encodePacked( - "Permissions: account ", - Strings.toHexString(uint160(target), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - - Permissions(address(drop)).revokeRole(role, target); - } - - /** - * @dev Tests whether contract reverts when a role is granted to an existent role holder. - */ - function test_revert_grant_role_to_account_with_role() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - - Permissions(address(drop)).grantRole(role, receiver); - - vm.expectRevert("Can only grant to non holders"); - Permissions(address(drop)).grantRole(role, receiver); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_grant_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - // check if admin and address(0) have transfer role in the beginning - bool checkAddressZero = Permissions(address(drop)).hasRole(role, address(0)); - bool checkAdmin = Permissions(address(drop)).hasRole(role, deployer); - assertTrue(checkAddressZero); - assertTrue(checkAdmin); - - // check if transfer role can be granted to a non-holder - address receiver = getActor(0); - vm.startPrank(deployer); - Permissions(address(drop)).grantRole(role, receiver); - - // expect revert when granting to a holder - vm.expectRevert("Can only grant to non holders"); - Permissions(address(drop)).grantRole(role, receiver); - - // check if receiver has transfer role - bool checkReceiver = Permissions(address(drop)).hasRole(role, receiver); - assertTrue(checkReceiver); - - // check if role is correctly revoked - Permissions(address(drop)).revokeRole(role, receiver); - checkReceiver = Permissions(address(drop)).hasRole(role, receiver); - assertFalse(checkReceiver); - Permissions(address(drop)).revokeRole(role, address(0)); - checkAddressZero = Permissions(address(drop)).hasRole(role, address(0)); - assertFalse(checkAddressZero); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_getRoleMember_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - uint256 roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - assertEq(roleMemberCount, 2); - - address roleMember = PermissionsEnumerable(address(drop)).getRoleMember(role, 1); - assertEq(roleMember, address(0)); - - vm.startPrank(deployer); - Permissions(address(drop)).grantRole(role, address(2)); - Permissions(address(drop)).grantRole(role, address(3)); - Permissions(address(drop)).grantRole(role, address(4)); - - roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(address(drop)).getRoleMember(role, i)); - } - console.log(""); - - Permissions(address(drop)).revokeRole(role, address(2)); - roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(address(drop)).getRoleMember(role, i)); - } - console.log(""); - - Permissions(address(drop)).revokeRole(role, address(0)); - roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(address(drop)).getRoleMember(role, i)); - } - console.log(""); - - Permissions(address(drop)).grantRole(role, address(5)); - roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(address(drop)).getRoleMember(role, i)); - } - console.log(""); - - Permissions(address(drop)).grantRole(role, address(0)); - roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(address(drop)).getRoleMember(role, i)); - } - console.log(""); - - Permissions(address(drop)).grantRole(role, address(6)); - roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(address(drop)).getRoleMember(role, i)); - } - console.log(""); - } - - /** - * note: Testing transfer of tokens when transfer-role is restricted - */ - function test_claim_transferRole() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - - // revoke transfer role from address(0) - vm.prank(deployer); - Permissions(address(drop)).revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.startPrank(receiver); - vm.expectRevert("!Transfer-Role"); - drop.transferFrom(receiver, address(123), 0); - } - - /** - * @dev Tests whether role member count is incremented correctly. - */ - function test_member_count_incremented_properly_when_role_granted() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - uint256 roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - - assertEq(roleMemberCount, 0); - - Permissions(address(drop)).grantRole(role, receiver); - - assertEq(PermissionsEnumerable(address(drop)).getRoleMemberCount(role), 1); - - vm.stopPrank(); - } - - function test_claimCondition_with_startTimestamp() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].startTimestamp = 100; - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.warp(99); - vm.prank(getActor(5), getActor(5)); - vm.expectRevert("!CONDITION."); - drop.claim(receiver, 1, address(0), 0, alp, ""); - - vm.warp(100); - vm.prank(getActor(4), getActor(4)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - Primary sale and Platform fee tests - //////////////////////////////////////////////////////////////*/ - - /// note: Test whether transaction reverts when adding address(0) as primary sale recipient at deploy time - function test_revert_deploy_emptyPrimarySaleRecipient() public { - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address dropImpl = address(new BurnToClaimDropERC721(extensions)); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - vm.expectRevert("Invalid recipient"); - drop = BurnToClaimDrop721Logic( - payable( - address( - new TWProxy( - dropImpl, - abi.encodeCall( - BurnToClaimDropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - address(0), - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ) - ); - } - - /// note: Test whether transaction reverts when adding address(0) as primary sale recipient - function test_revert_emptyPrimarySaleRecipient() public { - vm.prank(deployer); - vm.expectRevert("Invalid recipient"); - drop.setPrimarySaleRecipient(address(0)); - } - - /// note: Test whether transaction reverts when adding address(0) as platform fee recipient at deploy time - function test_revert_deploy_emptyPlatformFeeRecipient() public { - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address dropImpl = address(new BurnToClaimDropERC721(extensions)); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - vm.expectRevert("Invalid recipient"); - drop = BurnToClaimDrop721Logic( - payable( - address( - new TWProxy( - dropImpl, - abi.encodeCall( - BurnToClaimDropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - address(0) - ) - ) - ) - ) - ) - ); - } - - /// note: Test whether transaction reverts when adding address(0) as platform fee recipient - function test_revert_emptyPlatformFeeRecipient() public { - vm.prank(deployer); - vm.expectRevert("Invalid recipient"); - drop.setPlatformFeeInfo(address(0), 100); - } - - /*/////////////////////////////////////////////////////////////// - Lazy Mint Tests - //////////////////////////////////////////////////////////////*/ - - /* - * note: Testing state changes; lazy mint a batch of tokens with no encrypted base URI. - */ - function test_state_lazyMint_noEncryptedURI() public { - uint256 amountToLazyMint = 100; - string memory baseURI = "ipfs://"; - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, emptyEncodedBytes); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.tokenURI(i); - console.log(uri); - assertEq(uri, string(abi.encodePacked(baseURI, i.toString()))); - } - - vm.stopPrank(); - } - - /* - * note: Testing state changes; lazy mint a batch of tokens with encrypted base URI. - */ - function test_state_lazyMint_withEncryptedURI() public { - uint256 amountToLazyMint = 100; - string memory baseURI = "ipfs://"; - bytes memory encryptedBaseURI = "encryptedBaseURI://"; - bytes32 provenanceHash = bytes32("whatever"); - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, abi.encode(encryptedBaseURI, provenanceHash)); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.tokenURI(i); - console.log(uri); - assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - } - - vm.stopPrank(); - } - - /** - * note: Testing revert condition; an address without MINTER_ROLE calls lazyMint function. - */ - function test_revert_lazyMint_MINTER_ROLE() public { - vm.expectRevert("Not authorized"); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - } - - /* - * note: Testing revert condition; calling tokenURI for invalid batch id. - */ - function test_revert_lazyMint_URIForNonLazyMintedToken() public { - vm.startPrank(deployer); - - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.expectRevert("Invalid tokenId"); - drop.tokenURI(100); - - vm.stopPrank(); - } - - /** - * note: Testing event emission; tokens lazy minted. - */ - function test_event_lazyMint_TokensLazyMinted() public { - vm.startPrank(deployer); - - vm.expectEmit(true, false, false, true); - emit TokensLazyMinted(0, 99, "ipfs://", emptyEncodedBytes); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.stopPrank(); - } - - /* - * note: Fuzz testing state changes; lazy mint a batch of tokens with no encrypted base URI. - */ - function test_fuzz_lazyMint_noEncryptedURI(uint256 x) public { - vm.assume(x > 0); - - uint256 amountToLazyMint = x; - string memory baseURI = "ipfs://"; - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, emptyEncodedBytes); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - string memory uri = drop.tokenURI(0); - assertEq(uri, string(abi.encodePacked(baseURI, uint256(0).toString()))); - - uri = drop.tokenURI(x - 1); - assertEq(uri, string(abi.encodePacked(baseURI, uint256(x - 1).toString()))); - - /** - * note: this loop takes too long to run with fuzz tests. - */ - // for(uint256 i = 0; i < amountToLazyMint; i += 1) { - // string memory uri = drop.tokenURI(i); - // console.log(uri); - // assertEq(uri, string(abi.encodePacked(baseURI, i.toString()))); - // } - - vm.stopPrank(); - } - - /* - * note: Fuzz testing state changes; lazy mint a batch of tokens with encrypted base URI. - */ - function test_fuzz_lazyMint_withEncryptedURI(uint256 x) public { - vm.assume(x > 0); - - uint256 amountToLazyMint = x; - string memory baseURI = "ipfs://"; - bytes memory encryptedBaseURI = "encryptedBaseURI://"; - bytes32 provenanceHash = bytes32("whatever"); - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, abi.encode(encryptedBaseURI, provenanceHash)); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - string memory uri = drop.tokenURI(0); - assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - - uri = drop.tokenURI(x - 1); - assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - - /** - * note: this loop takes too long to run with fuzz tests. - */ - // for(uint256 i = 0; i < amountToLazyMint; i += 1) { - // string memory uri = drop.tokenURI(1); - // assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - // } - - vm.stopPrank(); - } - - /* - * note: Fuzz testing; a batch of tokens, and nextTokenIdToMint - */ - function test_fuzz_lazyMint_batchMintAndNextTokenIdToMint(uint256 x) public { - vm.assume(x > 0); - vm.startPrank(deployer); - - if (x == 0) { - vm.expectRevert("Zero amount"); - } - drop.lazyMint(x, "ipfs://", emptyEncodedBytes); - - uint256 slot = stdstore.target(address(drop)).sig("nextTokenIdToMint()").find(); - bytes32 loc = bytes32(slot); - uint256 nextTokenIdToMint = uint256(vm.load(address(drop), loc)); - - assertEq(nextTokenIdToMint, x); - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Delayed Reveal Tests - //////////////////////////////////////////////////////////////*/ - - /* - * note: Testing state changes; URI revealed for a batch of tokens. - */ - function test_state_reveal() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - uint256 amountToLazyMint = 100; - bytes memory secretURI = "ipfs://"; - string memory placeholderURI = "abcd://"; - bytes memory encryptedURI = drop.encryptDecrypt(secretURI, key); - bytes32 provenanceHash = keccak256(abi.encodePacked(secretURI, key, block.chainid)); - - drop.lazyMint(amountToLazyMint, placeholderURI, abi.encode(encryptedURI, provenanceHash)); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.tokenURI(i); - assertEq(uri, string(abi.encodePacked(placeholderURI, "0"))); - } - - string memory revealedURI = drop.reveal(0, key); - assertEq(revealedURI, string(secretURI)); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.tokenURI(i); - assertEq(uri, string(abi.encodePacked(secretURI, i.toString()))); - } - - vm.stopPrank(); - } - - /** - * note: Testing revert condition; an address without MINTER_ROLE calls reveal function. - */ - function test_revert_reveal_MINTER_ROLE() public { - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - vm.prank(deployer); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.prank(deployer); - drop.reveal(0, "key"); - - vm.expectRevert("not minter."); - drop.reveal(0, "key"); - } - - /* - * note: Testing revert condition; trying to reveal URI for non-existent batch. - */ - function test_revert_reveal_revealingNonExistentBatch() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - drop.reveal(0, "key"); - - console.log(drop.getBaseURICount()); - - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - vm.expectRevert("Invalid index"); - drop.reveal(2, "key"); - - vm.stopPrank(); - } - - /* - * note: Testing revert condition; already revealed URI. - */ - function test_revert_delayedReveal_alreadyRevealed() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - drop.reveal(0, "key"); - - vm.expectRevert("Nothing to reveal"); - drop.reveal(0, "key"); - - vm.stopPrank(); - } - - /* - * note: Testing state changes; revealing URI with an incorrect key. - */ - function test_revert_reveal_incorrectKey() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.expectRevert(); - string memory revealedURI = drop.reveal(0, "keyy"); - - vm.stopPrank(); - } - - /** - * note: Testing event emission; TokenURIRevealed. - */ - function test_event_reveal_TokenURIRevealed() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.expectEmit(true, false, false, true); - emit TokenURIRevealed(0, "ipfs://"); - drop.reveal(0, "key"); - - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Claim Tests - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing revert condition; not enough minted tokens. - */ - function test_revert_claimCondition_notEnoughMintedTokens() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 200; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.expectRevert("!Tokens"); - vm.prank(getActor(6), getActor(6)); - drop.claim(receiver, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing revert condition; exceed max claimable supply. - */ - function test_revert_claimCondition_exceedMaxClaimableSupply() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 200; - - vm.prank(deployer); - drop.lazyMint(200, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 100, address(0), 0, alp, ""); - - vm.expectRevert("!MaxSupply"); - vm.prank(getActor(6), getActor(6)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction when no allowlist present. - */ - function test_fuzz_claim_noAllowlist(uint256 x) public { - vm.assume(x != 0); - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - bytes memory errorQty = "!Qty"; - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(errorQty); - drop.claim(receiver, 0, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(errorQty); - drop.claim(receiver, 101, address(0), 0, alp, ""); - - vm.prank(deployer); - drop.setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(errorQty); - drop.claim(receiver, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to 0 - */ - function test_state_claim_allowlisted_SetQuantityZeroPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 100, address(erc20), 0, alp, ""); // claims for free, because allowlist price is 0 - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 100); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to non-zero value - */ - function test_state_claim_allowlisted_SetQuantityPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "5"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 5; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(receiver, receiver); - vm.expectRevert("!PriceOrCurrency"); - drop.claim(receiver, 100, address(erc20), 0, alp, ""); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 100, address(erc20), 5, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 100); - assertEq(erc20.balanceOf(receiver), 10000 - 500); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price not set; should default to general price and currency - */ - function test_state_claim_allowlisted_SetQuantityDefaultPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = Strings.toString(type(uint256).max); // this implies that general price is applicable - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = type(uint256).max; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 100, address(erc20), 10, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 100); - assertEq(erc20.balanceOf(receiver), 10000 - 1000); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to 0 => should default to general limit - * - allowlist price set to some value different than general price - */ - function test_state_claim_allowlisted_DefaultQuantitySomePrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "0"; // this implies that general limit is applicable - inputs[3] = "5"; - inputs[4] = "0x0000000000000000000000000000000000000000"; // general currency will be applicable - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 0; - alp.pricePerToken = 5; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - bytes memory errorQty = "!Qty"; - vm.prank(receiver, receiver); - vm.expectRevert(errorQty); - drop.claim(receiver, 100, address(erc20), 5, alp, ""); // trying to claim more than general limit - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 10, address(erc20), 5, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 10); - assertEq(erc20.balanceOf(receiver), 10000 - 50); - } - - function test_fuzz_claim_merkleProof(uint256 x) public { - vm.assume(x > 10 && x < 500); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(x); - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - alp.pricePerToken = 0; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - // bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = x; - conditions[0].quantityLimitPerWallet = 1; - conditions[0].merkleRoot = root; - - vm.prank(deployer); - drop.lazyMint(2 * x, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, x - 5, address(0), 0, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), x - 5); - - bytes memory errorQty = "!Qty"; - - vm.prank(receiver, receiver); - vm.expectRevert(errorQty); - drop.claim(receiver, 6, address(0), 0, alp, ""); - - vm.prank(receiver, receiver); - drop.claim(receiver, 5, address(0), 0, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), x); - - vm.prank(receiver, receiver); - vm.expectRevert(errorQty); - drop.claim(receiver, 5, address(0), 0, alp, ""); // quantity limit already claimed - } - - /** - * note: Testing state changes; reset eligibility of claim conditions and claiming again for same condition id. - */ - function test_state_claimCondition_resetEligibility() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 100, address(0), 0, alp, ""); - - bytes memory errorQty = "!Qty"; - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(errorQty); - drop.claim(receiver, 100, address(0), 0, alp, ""); - - vm.prank(deployer); - drop.setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 100, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - setClaimConditions - //////////////////////////////////////////////////////////////*/ - - function test_claimCondition_startIdAndCount() public { - vm.startPrank(deployer); - - uint256 currentStartId = 0; - uint256 count = 0; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](2); - conditions[0].startTimestamp = 0; - conditions[0].maxClaimableSupply = 10; - conditions[1].startTimestamp = 1; - conditions[1].maxClaimableSupply = 10; - - drop.setClaimConditions(conditions, false); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - drop.setClaimConditions(conditions, false); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - drop.setClaimConditions(conditions, true); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 2); - assertEq(count, 2); - - drop.setClaimConditions(conditions, true); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 4); - assertEq(count, 2); - } - - function test_claimCondition_startPhase() public { - vm.startPrank(deployer); - - uint256 activeConditionId = 0; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](3); - conditions[0].startTimestamp = 10; - conditions[0].maxClaimableSupply = 11; - conditions[0].quantityLimitPerWallet = 12; - conditions[1].startTimestamp = 20; - conditions[1].maxClaimableSupply = 21; - conditions[1].quantityLimitPerWallet = 22; - conditions[2].startTimestamp = 30; - conditions[2].maxClaimableSupply = 31; - conditions[2].quantityLimitPerWallet = 32; - drop.setClaimConditions(conditions, false); - - vm.expectRevert("!CONDITION."); - drop.getActiveClaimConditionId(); - - vm.warp(10); - activeConditionId = drop.getActiveClaimConditionId(); - assertEq(activeConditionId, 0); - assertEq(drop.getClaimConditionById(activeConditionId).startTimestamp, 10); - assertEq(drop.getClaimConditionById(activeConditionId).maxClaimableSupply, 11); - assertEq(drop.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 12); - - vm.warp(20); - activeConditionId = drop.getActiveClaimConditionId(); - assertEq(activeConditionId, 1); - assertEq(drop.getClaimConditionById(activeConditionId).startTimestamp, 20); - assertEq(drop.getClaimConditionById(activeConditionId).maxClaimableSupply, 21); - assertEq(drop.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 22); - - vm.warp(30); - activeConditionId = drop.getActiveClaimConditionId(); - assertEq(activeConditionId, 2); - assertEq(drop.getClaimConditionById(activeConditionId).startTimestamp, 30); - assertEq(drop.getClaimConditionById(activeConditionId).maxClaimableSupply, 31); - assertEq(drop.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 32); - - vm.warp(40); - assertEq(drop.getActiveClaimConditionId(), 2); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_delayedReveal_withNewLazyMintedEmptyBatch() public { - vm.startPrank(deployer); - - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", "key"); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", "key", block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - drop.reveal(0, "key"); - - string memory uri = drop.tokenURI(1); - assertEq(uri, string(abi.encodePacked("ipfs://", "1"))); - - bytes memory newEncryptedURI = drop.encryptDecrypt("ipfs://secret", "key"); - vm.expectRevert("0 amt"); - drop.lazyMint(0, "", abi.encode(newEncryptedURI, provenanceHash)); - - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Burn To Claim - //////////////////////////////////////////////////////////////*/ - - function test_state_burnAndClaim_1155Origin_zeroMintPrice() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc1155); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC1155; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 0; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // check details correctly saved - BurnToClaimDrop721Logic.BurnToClaimInfo memory savedInfo = drop.getBurnToClaimInfo(); - assertEq(savedInfo.originContractAddress, burnToClaimInfo.originContractAddress); - assertTrue(savedInfo.tokenType == burnToClaimInfo.tokenType); - assertEq(savedInfo.tokenId, burnToClaimInfo.tokenId); - assertEq(savedInfo.mintPriceForNewToken, burnToClaimInfo.mintPriceForNewToken); - assertEq(savedInfo.currency, burnToClaimInfo.currency); - - // mint some erc1155 to a claimer - address claimer = getActor(0); - erc1155.mint(claimer, 0, 10); - assertEq(erc1155.balanceOf(claimer, 0), 10); - vm.prank(claimer); - erc1155.setApprovalForAll(address(drop), true); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - drop.burnAndClaim(0, 10); - - // check state - assertEq(erc1155.balanceOf(claimer, 0), 0); - assertEq(drop.balanceOf(claimer), 10); - assertEq(drop.nextTokenIdToClaim(), 10); - } - - function test_state_burnAndClaim_1155Origin_nonZeroMintPrice() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc1155); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC1155; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 1; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // check details correctly saved - BurnToClaimDrop721Logic.BurnToClaimInfo memory savedInfo = drop.getBurnToClaimInfo(); - assertEq(savedInfo.originContractAddress, burnToClaimInfo.originContractAddress); - assertTrue(savedInfo.tokenType == burnToClaimInfo.tokenType); - assertEq(savedInfo.tokenId, burnToClaimInfo.tokenId); - assertEq(savedInfo.mintPriceForNewToken, burnToClaimInfo.mintPriceForNewToken); - assertEq(savedInfo.currency, burnToClaimInfo.currency); - - // mint some erc1155 to a claimer - address claimer = getActor(0); - erc1155.mint(claimer, 0, 10); - assertEq(erc1155.balanceOf(claimer, 0), 10); - vm.prank(claimer); - erc1155.setApprovalForAll(address(drop), true); - - // mint erc20 to claimer, to pay claim price - erc20.mint(claimer, 100); - vm.prank(claimer); - erc20.approve(address(drop), type(uint256).max); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - drop.burnAndClaim(0, 10); - - // check state - assertEq(erc1155.balanceOf(claimer, 0), 0); - assertEq(erc20.balanceOf(claimer), 90); - assertEq(erc20.balanceOf(saleRecipient), 10); - assertEq(drop.balanceOf(claimer), 10); - assertEq(drop.nextTokenIdToClaim(), 10); - } - - function test_state_burnAndClaim_1155Origin_nonZeroMintPrice_nativeToken() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc1155); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC1155; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 1; - burnToClaimInfo.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // check details correctly saved - BurnToClaimDrop721Logic.BurnToClaimInfo memory savedInfo = drop.getBurnToClaimInfo(); - assertEq(savedInfo.originContractAddress, burnToClaimInfo.originContractAddress); - assertTrue(savedInfo.tokenType == burnToClaimInfo.tokenType); - assertEq(savedInfo.tokenId, burnToClaimInfo.tokenId); - assertEq(savedInfo.mintPriceForNewToken, burnToClaimInfo.mintPriceForNewToken); - assertEq(savedInfo.currency, burnToClaimInfo.currency); - - // mint some erc1155 to a claimer - address claimer = getActor(0); - erc1155.mint(claimer, 0, 10); - assertEq(erc1155.balanceOf(claimer, 0), 10); - vm.prank(claimer); - erc1155.setApprovalForAll(address(drop), true); - - // deal ether to claimer, to pay claim price - vm.deal(claimer, 100); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - drop.burnAndClaim{ value: 10 }(0, 10); - - // check state - assertEq(erc1155.balanceOf(claimer, 0), 0); - assertEq(claimer.balance, 90); - assertEq(saleRecipient.balance, 10); - assertEq(drop.balanceOf(claimer), 10); - assertEq(drop.nextTokenIdToClaim(), 10); - } - - function test_state_burnAndClaim_721Origin_zeroMintPrice() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc721); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC721; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 0; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // check details correctly saved - BurnToClaimDrop721Logic.BurnToClaimInfo memory savedInfo = drop.getBurnToClaimInfo(); - assertEq(savedInfo.originContractAddress, burnToClaimInfo.originContractAddress); - assertTrue(savedInfo.tokenType == burnToClaimInfo.tokenType); - assertEq(savedInfo.tokenId, burnToClaimInfo.tokenId); - assertEq(savedInfo.mintPriceForNewToken, burnToClaimInfo.mintPriceForNewToken); - assertEq(savedInfo.currency, burnToClaimInfo.currency); - - // mint some erc721 to a claimer - address claimer = getActor(0); - erc721.mint(claimer, 10); - assertEq(erc721.balanceOf(claimer), 10); - vm.prank(claimer); - erc721.setApprovalForAll(address(drop), true); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - drop.burnAndClaim(0, 1); - - // check state - assertEq(erc721.balanceOf(claimer), 9); - assertEq(drop.balanceOf(claimer), 1); - assertEq(drop.nextTokenIdToClaim(), 1); - - vm.expectRevert("ERC721: invalid token ID"); // because the token doesn't exist anymore - erc721.ownerOf(0); - } - - function test_state_burnAndClaim_721Origin_nonZeroMintPrice() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc721); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC721; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 1; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // check details correctly saved - BurnToClaimDrop721Logic.BurnToClaimInfo memory savedInfo = drop.getBurnToClaimInfo(); - assertEq(savedInfo.originContractAddress, burnToClaimInfo.originContractAddress); - assertTrue(savedInfo.tokenType == burnToClaimInfo.tokenType); - assertEq(savedInfo.tokenId, burnToClaimInfo.tokenId); - assertEq(savedInfo.mintPriceForNewToken, burnToClaimInfo.mintPriceForNewToken); - assertEq(savedInfo.currency, burnToClaimInfo.currency); - - // mint some erc721 to a claimer - address claimer = getActor(0); - erc721.mint(claimer, 10); - assertEq(erc721.balanceOf(claimer), 10); - vm.prank(claimer); - erc721.setApprovalForAll(address(drop), true); - - // mint erc20 to claimer, to pay claim price - erc20.mint(claimer, 100); - vm.prank(claimer); - erc20.approve(address(drop), type(uint256).max); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - drop.burnAndClaim(0, 1); - - // check state - assertEq(erc721.balanceOf(claimer), 9); - assertEq(drop.balanceOf(claimer), 1); - assertEq(drop.nextTokenIdToClaim(), 1); - assertEq(erc20.balanceOf(claimer), 99); - assertEq(erc20.balanceOf(saleRecipient), 1); - - vm.expectRevert("ERC721: invalid token ID"); // because the token doesn't exist anymore - erc721.ownerOf(0); - } - - function test_state_burnAndClaim_721Origin_nonZeroMintPrice_nativeToken() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc721); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC721; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 1; - burnToClaimInfo.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // check details correctly saved - BurnToClaimDrop721Logic.BurnToClaimInfo memory savedInfo = drop.getBurnToClaimInfo(); - assertEq(savedInfo.originContractAddress, burnToClaimInfo.originContractAddress); - assertTrue(savedInfo.tokenType == burnToClaimInfo.tokenType); - assertEq(savedInfo.tokenId, burnToClaimInfo.tokenId); - assertEq(savedInfo.mintPriceForNewToken, burnToClaimInfo.mintPriceForNewToken); - assertEq(savedInfo.currency, burnToClaimInfo.currency); - - // mint some erc721 to a claimer - address claimer = getActor(0); - erc721.mint(claimer, 10); - assertEq(erc721.balanceOf(claimer), 10); - vm.prank(claimer); - erc721.setApprovalForAll(address(drop), true); - - // deal ether to claimer, to pay claim price - vm.deal(claimer, 100); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - drop.burnAndClaim{ value: 1 }(0, 1); - - // check state - assertEq(erc721.balanceOf(claimer), 9); - assertEq(drop.balanceOf(claimer), 1); - assertEq(drop.nextTokenIdToClaim(), 1); - assertEq(claimer.balance, 99); - assertEq(saleRecipient.balance, 1); - - vm.expectRevert("ERC721: invalid token ID"); // because the token doesn't exist anymore - erc721.ownerOf(0); - } - - function test_revert_burnAndClaim_originNotSet() public { - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.expectRevert(); - drop.burnAndClaim(0, 1); - } - - function test_revert_burnAndClaim_noLazyMintedTokens() public { - // burn and claim - vm.expectRevert("!Tokens"); - drop.burnAndClaim(0, 1); - } - - function test_revert_burnAndClaim_invalidTokenId() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc1155); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC1155; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 0; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // mint some erc1155 to a claimer - address claimer = getActor(0); - erc1155.mint(claimer, 0, 10); - assertEq(erc1155.balanceOf(claimer, 0), 10); - vm.prank(claimer); - erc1155.setApprovalForAll(address(drop), true); - - // burn and claim - vm.prank(claimer); - vm.expectRevert("Invalid token Id"); - drop.burnAndClaim(1, 1); - } - - function test_revert_burnAndClaim_notEnoughBalance() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc1155); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC1155; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 0; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // mint some erc1155 to a claimer - address claimer = getActor(0); - erc1155.mint(claimer, 0, 10); - assertEq(erc1155.balanceOf(claimer, 0), 10); - vm.prank(claimer); - erc1155.setApprovalForAll(address(drop), true); - - // burn and claim - vm.prank(claimer); - vm.expectRevert("!Balance"); - drop.burnAndClaim(0, 11); - } - - function test_revert_burnAndClaim_notOwnerOfToken() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc721); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC721; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 1; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // mint some erc721 to a claimer - address claimer = getActor(0); - erc721.mint(claimer, 10); - assertEq(erc721.balanceOf(claimer), 10); - vm.prank(claimer); - erc721.setApprovalForAll(address(drop), true); - - // mint erc721 to another address - erc721.mint(address(0x567), 5); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - vm.expectRevert("!Owner"); - drop.burnAndClaim(11, 1); - } - - /*/////////////////////////////////////////////////////////////// - Extension Role and Upgradeability - //////////////////////////////////////////////////////////////*/ - - // function test_addExtension() public { - // address permissionsNew = address(new PermissionsEnumerableImpl()); - - // Extension memory extension_permissions_new; - // extension_permissions_new.metadata = ExtensionMetadata({ - // name: "PermissionsNew", - // metadataURI: "ipfs://PermissionsNew", - // implementation: permissionsNew - // }); - - // extension_permissions_new.functions = new ExtensionFunction[](4); - // extension_permissions_new.functions[0] = ExtensionFunction( - // Permissions.hasRole.selector, - // "hasRole(bytes32,address)" - // ); - // extension_permissions_new.functions[1] = ExtensionFunction( - // Permissions.hasRoleWithSwitch.selector, - // "hasRoleWithSwitch(bytes32,address)" - // ); - // extension_permissions_new.functions[2] = ExtensionFunction( - // Permissions.grantRole.selector, - // "grantRole(bytes32,address)" - // ); - // extension_permissions_new.functions[3] = ExtensionFunction( - // PermissionsEnumerable.getRoleMemberCount.selector, - // "getRoleMemberCount(bytes32)" - // ); - - // // cast drop to router type - // BurnToClaimDropERC721 dropRouter = BurnToClaimDropERC721(payable(address(drop))); - - // vm.prank(deployer); - // dropRouter.addExtension(extension_permissions_new); - - // // assertEq( - // // dropRouter.getExtensionForFunction(PermissionsEnumerable.getRoleMemberCount.selector).name, - // // "PermissionsNew" - // // ); - - // // assertEq( - // // dropRouter.getExtensionForFunction(PermissionsEnumerable.getRoleMemberCount.selector).implementation, - // // permissionsNew - // // ); - // } - - function test_revert_addExtension_NotAuthorized() public { - Extension memory extension_permissions_new; - - // cast drop to router type - BurnToClaimDropERC721 dropRouter = BurnToClaimDropERC721(payable(address(drop))); - - vm.prank(address(0x123)); - vm.expectRevert("ExtensionManager: unauthorized."); - dropRouter.addExtension(extension_permissions_new); - } - - function test_revert_addExtension_deployerRenounceExtensionRole() public { - Extension memory extension_permissions_new; - - // cast drop to router type - BurnToClaimDropERC721 dropRouter = BurnToClaimDropERC721(payable(address(drop))); - - vm.prank(deployer); - Permissions(address(drop)).renounceRole(keccak256("EXTENSION_ROLE"), deployer); - - vm.prank(deployer); - vm.expectRevert("ExtensionManager: unauthorized."); - dropRouter.addExtension(extension_permissions_new); - - vm.startPrank(deployer); - vm.expectRevert( - abi.encodePacked( - "Permissions: account ", - Strings.toHexString(uint160(deployer), 20), - " is missing role ", - Strings.toHexString(uint256(keccak256("EXTENSION_ROLE")), 32) - ) - ); - Permissions(address(drop)).grantRole(keccak256("EXTENSION_ROLE"), address(0x12345)); - vm.stopPrank(); - } -} diff --git a/src/test/burn-to-claim-drop-BTT/logic/burn-and-claim/burnAndClaim.t.sol b/src/test/burn-to-claim-drop-BTT/logic/burn-and-claim/burnAndClaim.t.sol deleted file mode 100644 index eb188e56a..000000000 --- a/src/test/burn-to-claim-drop-BTT/logic/burn-and-claim/burnAndClaim.t.sol +++ /dev/null @@ -1,802 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import { BurnToClaimDropERC721 } from "contracts/prebuilts/unaudited/burn-to-claim-drop/BurnToClaimDropERC721.sol"; -import { BurnToClaimDrop721Logic, ERC721AUpgradeable, DelayedReveal, LazyMint, Drop, BurnToClaim, PrimarySale, PlatformFee } from "contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Logic.sol"; -import { PermissionsEnumerableImpl } from "contracts/extension/upgradeable/impl/PermissionsEnumerableImpl.sol"; -import { Royalty } from "contracts/extension/upgradeable/Royalty.sol"; -import { BatchMintMetadata } from "contracts/extension/upgradeable/BatchMintMetadata.sol"; -import { IBurnToClaim } from "contracts/extension/interface/IBurnToClaim.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import { Permissions } from "contracts/extension/Permissions.sol"; -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; - -contract BurnToClaimDropERC721Logic_BurnAndClaim is BaseTest, IExtension { - using Strings for uint256; - using Strings for address; - - event TokensBurnedAndClaimed( - address indexed originContract, - address indexed tokenOwner, - uint256 indexed burnTokenId, - uint256 quantity - ); - - BurnToClaimDrop721Logic public drop; - uint256 internal _tokenId; - uint256 internal _quantity; - uint256 internal _msgValue; - uint256[] internal batchIds; - address internal caller; - bytes internal data; - IBurnToClaim.BurnToClaimInfo internal info; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address dropImpl = address(new BurnToClaimDropERC721(extensions)); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - drop = BurnToClaimDrop721Logic( - payable( - address( - new TWProxy( - dropImpl, - abi.encodeCall( - BurnToClaimDropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ) - ); - - caller = getActor(5); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - erc20.mint(caller, 1_000 ether); - vm.deal(caller, 1_000 ether); - - erc721.mint(deployer, 100); - erc721NonBurnable.mint(deployer, 100); - - erc1155NonBurnable.mint(deployer, 0, 100); - erc1155.mint(deployer, 0, 100); - erc1155.mint(deployer, 1, 100); - - vm.startPrank(deployer); - erc721.setApprovalForAll(address(drop), true); - erc1155.setApprovalForAll(address(drop), true); - erc20.approve(address(drop), type(uint256).max); - vm.stopPrank(); - - vm.startPrank(caller); - erc721.setApprovalForAll(address(drop), true); - erc1155.setApprovalForAll(address(drop), true); - erc20.approve(address(drop), type(uint256).max); - vm.stopPrank(); - - // startId = 0; - // mint 5 batches - // vm.startPrank(deployer); - // for (uint256 i = 0; i < 5; i++) { - // uint256 _amount = (i + 1) * 10; - // uint256 batchId = startId + _amount; - // batchIds.push(batchId); - - // string memory baseURI = Strings.toString(batchId); - // startId = drop.lazyMint(_amount, baseURI, ""); - // } - // vm.stopPrank(); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](2); - - // Extension: Permissions - address permissions = address(new PermissionsEnumerableImpl()); - - Extension memory extension_permissions; - extension_permissions.metadata = ExtensionMetadata({ - name: "Permissions", - metadataURI: "ipfs://Permissions", - implementation: permissions - }); - - extension_permissions.functions = new ExtensionFunction[](1); - extension_permissions.functions[0] = ExtensionFunction( - Permissions.hasRole.selector, - "hasRole(bytes32,address)" - ); - - extensions[0] = extension_permissions; - - address dropLogic = address(new BurnToClaimDrop721Logic()); - - Extension memory extension_drop; - extension_drop.metadata = ExtensionMetadata({ - name: "BurnToClaimDrop721Logic", - metadataURI: "ipfs://BurnToClaimDrop721Logic", - implementation: dropLogic - }); - - extension_drop.functions = new ExtensionFunction[](10); - extension_drop.functions[0] = ExtensionFunction(BurnToClaimDrop721Logic.tokenURI.selector, "tokenURI(uint256)"); - extension_drop.functions[1] = ExtensionFunction( - BurnToClaimDrop721Logic.lazyMint.selector, - "lazyMint(uint256,string,bytes)" - ); - extension_drop.functions[2] = ExtensionFunction( - BurnToClaimDrop721Logic.setMaxTotalMinted.selector, - "setMaxTotalMinted(uint256)" - ); - extension_drop.functions[3] = ExtensionFunction( - BurnToClaimDrop721Logic.nextTokenIdToMint.selector, - "nextTokenIdToMint()" - ); - extension_drop.functions[4] = ExtensionFunction( - BurnToClaimDrop721Logic.burnAndClaim.selector, - "burnAndClaim(uint256,uint256)" - ); - extension_drop.functions[5] = ExtensionFunction( - BurnToClaim.getBurnToClaimInfo.selector, - "getBurnToClaimInfo()" - ); - extension_drop.functions[6] = ExtensionFunction( - BurnToClaim.setBurnToClaimInfo.selector, - "setBurnToClaimInfo((address,uint8,uint256,uint256,address))" - ); - extension_drop.functions[7] = ExtensionFunction( - BurnToClaimDrop721Logic.nextTokenIdToClaim.selector, - "nextTokenIdToClaim()" - ); - extension_drop.functions[8] = ExtensionFunction(ERC721AUpgradeable.balanceOf.selector, "balanceOf(address)"); - extension_drop.functions[9] = ExtensionFunction(ERC721AUpgradeable.ownerOf.selector, "ownerOf(uint256)"); - - extensions[1] = extension_drop; - } - - function test_burnAndClaim_notEnoughLazyMintedTokens() public { - vm.expectRevert("!Tokens"); - drop.burnAndClaim(0, 1); - } - - modifier whenEnoughLazyMintedTokens() { - vm.prank(deployer); - drop.lazyMint(1000, "ipfs://", ""); - _; - } - - function test_burnAndClaim_exceedMaxTotalMint() public whenEnoughLazyMintedTokens { - vm.prank(deployer); - drop.setMaxTotalMinted(1); //set max total mint cap as 1 - - vm.expectRevert("exceed max total mint cap."); - drop.burnAndClaim(0, 2); - } - - modifier whenNotExceedMaxTotalMinted() { - vm.prank(deployer); - drop.setMaxTotalMinted(1000); - _; - } - - function test_burnAndClaim_burnToClaimInfoNotSet() public whenEnoughLazyMintedTokens whenNotExceedMaxTotalMinted { - // it will fail when verifyClaim tries to check owner/balance on nft contract which is still address(0) - vm.expectRevert(); - drop.burnAndClaim(0, 1); - } - - // ================== - // ======= Test branch: burn-to-claim origin contract is ERC721 - // ================== - - modifier whenBurnToClaimInfoSetERC721() { - info = IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc721NonBurnable), - tokenType: IBurnToClaim.TokenType.ERC721, - tokenId: 0, - mintPriceForNewToken: 0, - currency: address(erc20) - }); - - vm.prank(deployer); - drop.setBurnToClaimInfo(info); - - _; - } - - function test_burnAndClaim_ERC721_invalidQuantity() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSetERC721 - { - vm.expectRevert("Invalid amount"); - drop.burnAndClaim(0, 0); - } - - modifier whenValidQuantityERC721() { - _quantity = 1; - _; - } - - function test_burnAndClaim_ERC721_notOwner() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSetERC721 - whenValidQuantityERC721 - { - vm.expectRevert("!Owner"); - drop.burnAndClaim(_tokenId, _quantity); - } - - modifier whenCorrectOwnerERC721() { - vm.startPrank(deployer); - erc721NonBurnable.transferFrom(deployer, caller, _tokenId); - erc721.transferFrom(deployer, caller, _tokenId); - vm.stopPrank(); - _; - } - - function test_burnAndClaim_ERC721_notBurnable() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSetERC721 - whenValidQuantityERC721 - whenCorrectOwnerERC721 - { - vm.expectRevert(); // `EvmError: Revert` when trying to burn on a non-burnable contract - vm.prank(caller); - drop.burnAndClaim(_tokenId, _quantity); - } - - modifier whenBurnToClaimInfoSet_ERC721Burnable() { - info = IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc721), - tokenType: IBurnToClaim.TokenType.ERC721, - tokenId: 0, - mintPriceForNewToken: 0, - currency: address(erc20) - }); - - vm.prank(deployer); - drop.setBurnToClaimInfo(info); - - _; - } - - function test_burnAndClaim_ERC721_mintPriceZero_msgValueNonZero() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC721Burnable - whenValidQuantityERC721 - whenCorrectOwnerERC721 - { - vm.expectRevert("!Value"); - vm.prank(caller); - drop.burnAndClaim{ value: 1 }(_tokenId, _quantity); - } - - modifier whenMsgValueZero() { - _msgValue = 0; - _; - } - - function test_burnAndClaim_ERC721_mintPriceZero() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC721Burnable - whenValidQuantityERC721 - whenCorrectOwnerERC721 - whenMsgValueZero - { - // state before - uint256 _nextTokenIdToClaim = drop.nextTokenIdToClaim(); - - // burn and claim - vm.prank(caller); - drop.burnAndClaim{ value: _msgValue }(_tokenId, _quantity); - - // check state after - vm.expectRevert(); // because token non-existent after burning - erc721.ownerOf(_tokenId); - - assertEq(drop.balanceOf(caller), _quantity); - assertEq(drop.ownerOf(_nextTokenIdToClaim), caller); - assertEq(drop.nextTokenIdToClaim(), _nextTokenIdToClaim + _quantity); - } - - function test_burnAndClaim_ERC721_mintPriceZero_event() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC721Burnable - whenValidQuantityERC721 - whenCorrectOwnerERC721 - whenMsgValueZero - { - vm.prank(caller); - vm.expectEmit(true, true, true, true); - emit TokensBurnedAndClaimed(address(erc721), caller, _tokenId, _quantity); - drop.burnAndClaim{ value: _msgValue }(_tokenId, _quantity); - } - - modifier whenBurnToClaimInfoSet_ERC721Burnable_nonZeroPriceNativeToken() { - info = IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc721), - tokenType: IBurnToClaim.TokenType.ERC721, - tokenId: 0, - mintPriceForNewToken: 100, - currency: NATIVE_TOKEN - }); - - vm.prank(deployer); - drop.setBurnToClaimInfo(info); - - _; - } - - function test_burnAndClaim_ERC721_mintPriceNonZero_nativeToken_incorrectMsgValue() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC721Burnable_nonZeroPriceNativeToken - whenValidQuantityERC721 - whenCorrectOwnerERC721 - { - uint256 incorrectTotalPrice = (info.mintPriceForNewToken * _quantity) + 1; - - vm.prank(caller); - vm.expectRevert("Invalid msg value"); - drop.burnAndClaim{ value: incorrectTotalPrice }(_tokenId, _quantity); - } - - modifier whenCorrectMsgValue() { - _msgValue = info.mintPriceForNewToken * _quantity; - _; - } - - function test_burnAndClaim_ERC721_mintPriceNonZero_nativeToken() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC721Burnable_nonZeroPriceNativeToken - whenValidQuantityERC721 - whenCorrectOwnerERC721 - whenCorrectMsgValue - { - // state before - uint256 _nextTokenIdToClaim = drop.nextTokenIdToClaim(); - assertEq(platformFeeRecipient.balance, 0); - assertEq(saleRecipient.balance, 0); - assertEq(caller.balance, 1000 ether); - - // burn and claim - vm.prank(caller); - drop.burnAndClaim{ value: _msgValue }(_tokenId, _quantity); - - // check state after - uint256 totalPrice = (info.mintPriceForNewToken * _quantity); - uint256 _platformFee = (totalPrice * platformFeeBps) / 10_000; - uint256 _saleProceeds = totalPrice - _platformFee; - vm.expectRevert(); // because token non-existent after burning - erc721.ownerOf(_tokenId); - - assertEq(drop.balanceOf(caller), _quantity); - assertEq(drop.ownerOf(_nextTokenIdToClaim), caller); - assertEq(drop.nextTokenIdToClaim(), _nextTokenIdToClaim + _quantity); - assertEq(platformFeeRecipient.balance, _platformFee); - assertEq(saleRecipient.balance, _saleProceeds); - assertEq(caller.balance, 1000 ether - totalPrice); - } - - function test_burnAndClaim_ERC721_mintPriceNonZero_nativeToken_event() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC721Burnable_nonZeroPriceNativeToken - whenValidQuantityERC721 - whenCorrectOwnerERC721 - whenCorrectMsgValue - { - vm.prank(caller); - vm.expectEmit(true, true, true, true); - emit TokensBurnedAndClaimed(address(erc721), caller, _tokenId, _quantity); - drop.burnAndClaim{ value: _msgValue }(_tokenId, _quantity); - } - - modifier whenBurnToClaimInfoSet_ERC721Burnable_nonZeroPriceERC20() { - info = IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc721), - tokenType: IBurnToClaim.TokenType.ERC721, - tokenId: 0, - mintPriceForNewToken: 100, - currency: address(erc20) - }); - - vm.prank(deployer); - drop.setBurnToClaimInfo(info); - - _; - } - - function test_burnAndClaim_ERC721_mintPriceNonZero_ERC20_nonZeroMsgValue() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC721Burnable_nonZeroPriceERC20 - whenValidQuantityERC721 - whenCorrectOwnerERC721 - { - vm.prank(caller); - vm.expectRevert("Invalid msg value"); - drop.burnAndClaim{ value: 1 }(_tokenId, _quantity); - } - - function test_burnAndClaim_ERC721_mintPriceNonZero_ERC20() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC721Burnable_nonZeroPriceERC20 - whenValidQuantityERC721 - whenCorrectOwnerERC721 - whenMsgValueZero - { - // state before - uint256 _nextTokenIdToClaim = drop.nextTokenIdToClaim(); - assertEq(erc20.balanceOf(platformFeeRecipient), 0); - assertEq(erc20.balanceOf(saleRecipient), 0); - assertEq(erc20.balanceOf(caller), 1000 ether); - - // burn and claim - vm.prank(caller); - drop.burnAndClaim{ value: _msgValue }(_tokenId, _quantity); - - // check state after - uint256 totalPrice = (info.mintPriceForNewToken * _quantity); - uint256 _platformFee = (totalPrice * platformFeeBps) / 10_000; - uint256 _saleProceeds = totalPrice - _platformFee; - vm.expectRevert(); // because token non-existent after burning - erc721.ownerOf(_tokenId); - - assertEq(drop.balanceOf(caller), _quantity); - assertEq(drop.ownerOf(_nextTokenIdToClaim), caller); - assertEq(drop.nextTokenIdToClaim(), _nextTokenIdToClaim + _quantity); - assertEq(erc20.balanceOf(platformFeeRecipient), _platformFee); - assertEq(erc20.balanceOf(saleRecipient), _saleProceeds); - assertEq(erc20.balanceOf(caller), 1000 ether - totalPrice); - } - - function test_burnAndClaim_ERC721_mintPriceNonZero_ERC20_event() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC721Burnable_nonZeroPriceERC20 - whenValidQuantityERC721 - whenCorrectOwnerERC721 - whenMsgValueZero - { - vm.prank(caller); - vm.expectEmit(true, true, true, true); - emit TokensBurnedAndClaimed(address(erc721), caller, _tokenId, _quantity); - drop.burnAndClaim{ value: _msgValue }(_tokenId, _quantity); - } - - // ================== - // ======= Test branch: burn-to-claim origin contract is ERC1155 - // ================== - - modifier whenBurnToClaimInfoSetERC1155() { - info = IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc1155NonBurnable), - tokenType: IBurnToClaim.TokenType.ERC1155, - tokenId: 0, - mintPriceForNewToken: 0, - currency: address(erc20) - }); - - vm.prank(deployer); - drop.setBurnToClaimInfo(info); - - _; - } - - function test_burnAndClaim_ERC1155_invalidTokenId() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSetERC1155 - { - vm.expectRevert("Invalid token Id"); - drop.burnAndClaim(1, 1); - } - - modifier whenValidTokenIdERC1155() { - _quantity = 1; - _tokenId = 0; - _; - } - - function test_burnAndClaim_ERC1155_notEnoughBalance() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSetERC1155 - whenValidTokenIdERC1155 - { - vm.expectRevert("!Balance"); - vm.prank(caller); - drop.burnAndClaim(_tokenId, _quantity); - } - - modifier whenEnoughBalanceERC1155() { - vm.startPrank(deployer); - erc1155NonBurnable.safeTransferFrom(deployer, caller, _tokenId, 100, ""); - erc1155.safeTransferFrom(deployer, caller, _tokenId, 100, ""); - vm.stopPrank(); - _; - } - - function test_burnAndClaim_ERC1155_notBurnable() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSetERC1155 - whenValidTokenIdERC1155 - whenEnoughBalanceERC1155 - { - vm.expectRevert(); // `EvmError: Revert` when trying to burn on a non-burnable contract - vm.prank(caller); - drop.burnAndClaim(_tokenId, _quantity); - } - - modifier whenBurnToClaimInfoSet_ERC1155Burnable() { - info = IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc1155), - tokenType: IBurnToClaim.TokenType.ERC1155, - tokenId: 0, - mintPriceForNewToken: 0, - currency: address(erc20) - }); - - vm.prank(deployer); - drop.setBurnToClaimInfo(info); - - _; - } - - function test_burnAndClaim_ERC1155_mintPriceZero_msgValueNonZero() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC1155Burnable - whenValidTokenIdERC1155 - whenEnoughBalanceERC1155 - { - vm.expectRevert("!Value"); - vm.prank(caller); - drop.burnAndClaim{ value: 1 }(_tokenId, _quantity); - } - - function test_burnAndClaim_ERC1155_mintPriceZero() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC1155Burnable - whenValidTokenIdERC1155 - whenEnoughBalanceERC1155 - whenMsgValueZero - { - // state before - uint256 _nextTokenIdToClaim = drop.nextTokenIdToClaim(); - - // burn and claim - vm.prank(caller); - drop.burnAndClaim{ value: _msgValue }(_tokenId, _quantity); - - // check state after - assertEq(erc1155.balanceOf(caller, _tokenId), 100 - _quantity); - assertEq(drop.balanceOf(caller), _quantity); - assertEq(drop.ownerOf(_nextTokenIdToClaim), caller); - assertEq(drop.nextTokenIdToClaim(), _nextTokenIdToClaim + _quantity); - } - - function test_burnAndClaim_ERC1155_mintPriceZero_event() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC1155Burnable - whenValidTokenIdERC1155 - whenEnoughBalanceERC1155 - whenMsgValueZero - { - vm.prank(caller); - vm.expectEmit(true, true, true, true); - emit TokensBurnedAndClaimed(address(erc1155), caller, _tokenId, _quantity); - drop.burnAndClaim{ value: _msgValue }(_tokenId, _quantity); - } - - modifier whenBurnToClaimInfoSet_ERC1155Burnable_nonZeroPriceNativeToken() { - info = IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc1155), - tokenType: IBurnToClaim.TokenType.ERC1155, - tokenId: 0, - mintPriceForNewToken: 100, - currency: NATIVE_TOKEN - }); - - vm.prank(deployer); - drop.setBurnToClaimInfo(info); - - _; - } - - function test_burnAndClaim_ERC1155_mintPriceNonZero_nativeToken_incorrectMsgValue() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC1155Burnable_nonZeroPriceNativeToken - whenValidTokenIdERC1155 - whenEnoughBalanceERC1155 - { - uint256 incorrectTotalPrice = (info.mintPriceForNewToken * _quantity) + 1; - - vm.prank(caller); - vm.expectRevert("Invalid msg value"); - drop.burnAndClaim{ value: incorrectTotalPrice }(_tokenId, _quantity); - } - - function test_burnAndClaim_ERC1155_mintPriceNonZero_nativeToken() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC1155Burnable_nonZeroPriceNativeToken - whenValidTokenIdERC1155 - whenEnoughBalanceERC1155 - whenCorrectMsgValue - { - // state before - uint256 _nextTokenIdToClaim = drop.nextTokenIdToClaim(); - assertEq(platformFeeRecipient.balance, 0); - assertEq(saleRecipient.balance, 0); - assertEq(caller.balance, 1000 ether); - - // burn and claim - vm.prank(caller); - drop.burnAndClaim{ value: _msgValue }(_tokenId, _quantity); - - // check state after - uint256 totalPrice = (info.mintPriceForNewToken * _quantity); - uint256 _platformFee = (totalPrice * platformFeeBps) / 10_000; - uint256 _saleProceeds = totalPrice - _platformFee; - - assertEq(erc1155.balanceOf(caller, _tokenId), 100 - _quantity); - assertEq(drop.balanceOf(caller), _quantity); - assertEq(drop.ownerOf(_nextTokenIdToClaim), caller); - assertEq(drop.nextTokenIdToClaim(), _nextTokenIdToClaim + _quantity); - assertEq(platformFeeRecipient.balance, _platformFee); - assertEq(saleRecipient.balance, _saleProceeds); - assertEq(caller.balance, 1000 ether - totalPrice); - } - - function test_burnAndClaim_ERC1155_mintPriceNonZero_nativeToken_event() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC1155Burnable_nonZeroPriceNativeToken - whenValidTokenIdERC1155 - whenEnoughBalanceERC1155 - whenCorrectMsgValue - { - vm.prank(caller); - vm.expectEmit(true, true, true, true); - emit TokensBurnedAndClaimed(address(erc1155), caller, _tokenId, _quantity); - drop.burnAndClaim{ value: _msgValue }(_tokenId, _quantity); - } - - modifier whenBurnToClaimInfoSet_ERC1155Burnable_nonZeroPriceERC20() { - info = IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc1155), - tokenType: IBurnToClaim.TokenType.ERC1155, - tokenId: 0, - mintPriceForNewToken: 100, - currency: address(erc20) - }); - - vm.prank(deployer); - drop.setBurnToClaimInfo(info); - - _; - } - - function test_burnAndClaim_ERC1155_mintPriceNonZero_ERC20_nonZeroMsgValue() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC1155Burnable_nonZeroPriceERC20 - whenValidTokenIdERC1155 - whenEnoughBalanceERC1155 - { - vm.prank(caller); - vm.expectRevert("Invalid msg value"); - drop.burnAndClaim{ value: 1 }(_tokenId, _quantity); - } - - function test_burnAndClaim_ERC1155_mintPriceNonZero_ERC20() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC1155Burnable_nonZeroPriceERC20 - whenValidTokenIdERC1155 - whenEnoughBalanceERC1155 - whenMsgValueZero - { - // state before - uint256 _nextTokenIdToClaim = drop.nextTokenIdToClaim(); - assertEq(erc20.balanceOf(platformFeeRecipient), 0); - assertEq(erc20.balanceOf(saleRecipient), 0); - assertEq(erc20.balanceOf(caller), 1000 ether); - - // burn and claim - vm.prank(caller); - drop.burnAndClaim{ value: _msgValue }(_tokenId, _quantity); - - // check state after - uint256 totalPrice = (info.mintPriceForNewToken * _quantity); - uint256 _platformFee = (totalPrice * platformFeeBps) / 10_000; - uint256 _saleProceeds = totalPrice - _platformFee; - - assertEq(erc1155.balanceOf(caller, _tokenId), 100 - _quantity); - assertEq(drop.balanceOf(caller), _quantity); - assertEq(drop.ownerOf(_nextTokenIdToClaim), caller); - assertEq(drop.nextTokenIdToClaim(), _nextTokenIdToClaim + _quantity); - assertEq(erc20.balanceOf(platformFeeRecipient), _platformFee); - assertEq(erc20.balanceOf(saleRecipient), _saleProceeds); - assertEq(erc20.balanceOf(caller), 1000 ether - totalPrice); - } - - function test_burnAndClaim_ERC1155_mintPriceNonZero_ERC20_event() - public - whenEnoughLazyMintedTokens - whenNotExceedMaxTotalMinted - whenBurnToClaimInfoSet_ERC1155Burnable_nonZeroPriceERC20 - whenValidTokenIdERC1155 - whenEnoughBalanceERC1155 - whenMsgValueZero - { - vm.prank(caller); - vm.expectEmit(true, true, true, true); - emit TokensBurnedAndClaimed(address(erc1155), caller, _tokenId, _quantity); - drop.burnAndClaim{ value: _msgValue }(_tokenId, _quantity); - } -} diff --git a/src/test/burn-to-claim-drop-BTT/logic/burn-and-claim/burnAndClaim.tree b/src/test/burn-to-claim-drop-BTT/logic/burn-and-claim/burnAndClaim.tree deleted file mode 100644 index 00e6af1b9..000000000 --- a/src/test/burn-to-claim-drop-BTT/logic/burn-and-claim/burnAndClaim.tree +++ /dev/null @@ -1,83 +0,0 @@ -burnAndClaim(uint256 _burnTokenId, uint256 _quantity) -├── when the sum of `_quantity` and total minted is greater than nextTokenIdToLazyMint -│ └── it should revert ✅ -└── when the sum of `_quantity` and total minted less than or equal to nextTokenIdToLazyMint - └── when maxTotalMinted is not zero ✅ // TODO when zero - └── when the sum of `_quantity` and total minted greater than maxTotalMinted - │ └── it should revert ✅ - └── when the sum of `_quantity` and total minted less than or equal to maxTotalMinted - ├── when burn-to-claim info is not set - │ └── it should revert ✅ - └── when burn-to-claim info is set, with token type ERC721 - │ ├── when `_quantity` is not 1 - │ │ └── it should revert ✅ - │ └── when `_quantity` param is 1 - │ ├── when caller (i.e. _dropMsgSender) is not the actual token owner - │ │ └── it should revert ✅ - │ └── when caller is the actual token owner - │ ├── when the origin ERC721 contract is not burnable - │ │ └── it should revert ✅ - │ └── when the origin ERC721 contract is burnable - │ └── when mint price (i.e. pricePerToken) is zero - │ │ └── when msg.value is not zero - │ │ │ └── it should revert ✅ - │ │ └── when msg.value is zero - │ │ └── it should successfully burn the token with given tokenId for the token owner ✅ - │ │ └── it should mint new tokens to caller ✅ - │ │ └── it should emit TokensBurnedAndClaimed event ✅ - │ └── when mint price is not zero - │ └── when currency is native token - │ │ └── when msg.value is not equal to total price - │ │ │ └── it should revert ✅ - │ │ └── when msg.value is equal to total price - │ │ └── it should successfully burn the token with given tokenId for the token owner ✅ - │ │ └── it should mint new tokens to caller ✅ - │ │ └── (transfer to sale recipient) ✅ - │ │ └── (transfer to fee recipient) ✅ - │ │ └── it should emit TokensBurnedAndClaimed event ✅ - │ └── when currency is some ERC20 token - │ └── when msg.value is not zero - │ │ └── it should revert ✅ - │ └── when msg.value is zero - │ └── it should successfully burn the token with given tokenId for the token owner ✅ - │ └── it should mint new tokens to caller ✅ - │ └── (transfer to sale recipient) ✅ - │ └── (transfer to fee recipient) ✅ - │ └── it should emit TokensBurnedAndClaimed event ✅ - └── when burn-to-claim info is set, with token type ERC1155 - ├── when `_burnTokenId` param doesn't match eligible tokenId - │ └── it should revert ✅ - └── when `_burnTokenId` param matches eligible tokenId - ├── when caller (i.e. _dropMsgSender) has balance less than quantity param - │ └── it should revert ✅ - └── when caller has balance greater than or equal to quantity param - ├── when the origin ERC1155 contract is not burnable - │ └── it should revert ✅ - └── when the origin ERC1155 contract is burnable - └── when mint price (i.e. pricePerToken) is zero - │ └── when msg.value is not zero - │ │ └── it should revert ✅ - │ └── when msg.value is zero - │ └── it should successfully burn the token with given tokenId for the token owner ✅ - │ └── it should mint new tokens to caller ✅ - │ └── it should emit TokensBurnedAndClaimed event ✅ - └── when mint price is not zero - └── when currency is native token - │ └── when msg.value is not equal to total price - │ │ └── it should revert ✅ - │ └── when msg.value is equal to total price - │ └── it should successfully burn the token with given tokenId for the token owner ✅ - │ └── it should mint new tokens to caller ✅ - │ └── (transfer to sale recipient) ✅ - │ └── (transfer to fee recipient) ✅ - │ └── it should emit TokensBurnedAndClaimed event ✅ - └── when currency is some ERC20 token - └── when msg.value is not zero - │ └── it should revert ✅ - └── when msg.value is zero - └── it should successfully burn the token with given tokenId for the token owner ✅ - └── it should mint new tokens to caller ✅ - └── (transfer to sale recipient) ✅ - └── (transfer to fee recipient) ✅ - └── it should emit TokensBurnedAndClaimed event ✅ - diff --git a/src/test/burn-to-claim-drop-BTT/logic/lazy-mint/lazyMint.t.sol b/src/test/burn-to-claim-drop-BTT/logic/lazy-mint/lazyMint.t.sol deleted file mode 100644 index e9b19f812..000000000 --- a/src/test/burn-to-claim-drop-BTT/logic/lazy-mint/lazyMint.t.sol +++ /dev/null @@ -1,265 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import { BurnToClaimDropERC721 } from "contracts/prebuilts/unaudited/burn-to-claim-drop/BurnToClaimDropERC721.sol"; -import { BurnToClaimDrop721Logic, ERC721AUpgradeable, DelayedReveal, LazyMint, Drop, BurnToClaim, PrimarySale, PlatformFee } from "contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Logic.sol"; -import { PermissionsEnumerableImpl } from "contracts/extension/upgradeable/impl/PermissionsEnumerableImpl.sol"; -import { Royalty } from "contracts/extension/upgradeable/Royalty.sol"; -import { BatchMintMetadata } from "contracts/extension/upgradeable/BatchMintMetadata.sol"; -import { IBurnToClaim } from "contracts/extension/interface/IBurnToClaim.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import { Permissions } from "contracts/extension/Permissions.sol"; - -contract BurnToClaimDropERC721Logic_LazyMint is BaseTest, IExtension { - using Strings for uint256; - using Strings for address; - - event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI); - - BurnToClaimDrop721Logic public drop; - uint256 internal startId; - uint256 internal amount; - uint256[] internal batchIds; - address internal caller; - bytes internal data; - bytes internal encryptedUri; - bytes32 internal provenanceHash; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address dropImpl = address(new BurnToClaimDropERC721(extensions)); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - drop = BurnToClaimDrop721Logic( - payable( - address( - new TWProxy( - dropImpl, - abi.encodeCall( - BurnToClaimDropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ) - ); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - - startId = 0; - // mint 5 batches - vm.startPrank(deployer); - for (uint256 i = 0; i < 5; i++) { - uint256 _amount = (i + 1) * 10; - uint256 batchId = startId + _amount; - batchIds.push(batchId); - - string memory baseURI = Strings.toString(batchId); - startId = drop.lazyMint(_amount, baseURI, ""); - } - vm.stopPrank(); - - encryptedUri = bytes("ipfs://encryptedURI"); - provenanceHash = keccak256("provenanceHash"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](2); - - // Extension: Permissions - address permissions = address(new PermissionsEnumerableImpl()); - - Extension memory extension_permissions; - extension_permissions.metadata = ExtensionMetadata({ - name: "Permissions", - metadataURI: "ipfs://Permissions", - implementation: permissions - }); - - extension_permissions.functions = new ExtensionFunction[](1); - extension_permissions.functions[0] = ExtensionFunction( - Permissions.hasRole.selector, - "hasRole(bytes32,address)" - ); - - extensions[0] = extension_permissions; - - address dropLogic = address(new BurnToClaimDrop721Logic()); - - Extension memory extension_drop; - extension_drop.metadata = ExtensionMetadata({ - name: "BurnToClaimDrop721Logic", - metadataURI: "ipfs://BurnToClaimDrop721Logic", - implementation: dropLogic - }); - - extension_drop.functions = new ExtensionFunction[](6); - extension_drop.functions[0] = ExtensionFunction(BurnToClaimDrop721Logic.tokenURI.selector, "tokenURI(uint256)"); - extension_drop.functions[1] = ExtensionFunction( - BurnToClaimDrop721Logic.lazyMint.selector, - "lazyMint(uint256,string,bytes)" - ); - extension_drop.functions[2] = ExtensionFunction( - BatchMintMetadata.getBaseURICount.selector, - "getBaseURICount()" - ); - extension_drop.functions[3] = ExtensionFunction( - BurnToClaimDrop721Logic.nextTokenIdToMint.selector, - "nextTokenIdToMint()" - ); - extension_drop.functions[4] = ExtensionFunction( - DelayedReveal.encryptDecrypt.selector, - "encryptDecrypt(bytes,bytes)" - ); - extension_drop.functions[5] = ExtensionFunction( - DelayedReveal.isEncryptedBatch.selector, - "isEncryptedBatch(uint256)" - ); - - extensions[1] = extension_drop; - } - - // ================== - // ======= Test branch: when `data` empty - // ================== - - function test_lazyMint_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert("Not authorized"); - drop.lazyMint(amount, "", ""); - } - - modifier whenCallerAuthorized() { - caller = deployer; - _; - } - - function test_lazyMint_zeroAmount() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectRevert("0 amt"); - drop.lazyMint(amount, "", ""); - } - - modifier whenAmountNotZero() { - amount = 50; - _; - } - - function test_lazyMint() public whenCallerAuthorized whenAmountNotZero { - // check previous state - uint256 _nextTokenIdToLazyMintOld = drop.nextTokenIdToMint(); - assertEq(_nextTokenIdToLazyMintOld, batchIds[4]); - - string memory baseURI = "ipfs://baseURI"; - - // lazy mint next batch - vm.prank(address(caller)); - uint256 _batchId = drop.lazyMint(amount, baseURI, ""); - - // check new state - assertEq(_batchId, _nextTokenIdToLazyMintOld + amount); - for (uint256 i = _nextTokenIdToLazyMintOld; i < _batchId; i++) { - assertEq(drop.tokenURI(i), string(abi.encodePacked(baseURI, i.toString()))); - } - assertEq(drop.nextTokenIdToMint(), _nextTokenIdToLazyMintOld + amount); - assertEq(drop.getBaseURICount(), batchIds.length + 1); - } - - function test_lazyMint_event() public whenCallerAuthorized whenAmountNotZero { - string memory baseURI = "ipfs://baseURI"; - uint256 _nextTokenIdToLazyMintOld = drop.nextTokenIdToMint(); - - // lazy mint next batch - vm.prank(address(caller)); - vm.expectEmit(true, false, false, true); - emit TokensLazyMinted(_nextTokenIdToLazyMintOld, _nextTokenIdToLazyMintOld + amount - 1, baseURI, ""); - drop.lazyMint(amount, baseURI, ""); - } - - // ================== - // ======= Test branch: when `data` not empty - // ================== - - function test_lazyMint_withData_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert("Not authorized"); - drop.lazyMint(amount, "", data); - } - - function test_lazyMint_withData_zeroAmount() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectRevert("0 amt"); - drop.lazyMint(amount, "", data); - } - - function test_lazyMint_withData_incorrectData() public whenCallerAuthorized whenAmountNotZero { - data = bytes("random data"); // not bytes+bytes32 encoded as expected - vm.prank(address(caller)); - vm.expectRevert(); - drop.lazyMint(amount, "", data); - } - - modifier whenCorrectEncodingOfData() { - data = abi.encode(encryptedUri, provenanceHash); - _; - } - - function test_lazyMint_withData() public whenCallerAuthorized whenAmountNotZero whenCorrectEncodingOfData { - // check previous state - uint256 _nextTokenIdToLazyMintOld = drop.nextTokenIdToMint(); - assertEq(_nextTokenIdToLazyMintOld, batchIds[4]); - - string memory placeholderURI = "ipfs://placeholderURI"; - - // lazy mint next batch - vm.prank(address(caller)); - uint256 _batchId = drop.lazyMint(amount, placeholderURI, data); - - // check new state - assertTrue(drop.isEncryptedBatch(_batchId)); // encrypted batch - assertEq(_batchId, _nextTokenIdToLazyMintOld + amount); - for (uint256 i = _nextTokenIdToLazyMintOld; i < _batchId; i++) { - assertEq(drop.tokenURI(i), string(abi.encodePacked(placeholderURI, "0"))); // encrypted batch, hence token-id 0 - } - assertEq(drop.nextTokenIdToMint(), _nextTokenIdToLazyMintOld + amount); - assertEq(drop.getBaseURICount(), batchIds.length + 1); - } - - function test_lazyMint_withData_event() public whenCallerAuthorized whenAmountNotZero whenCorrectEncodingOfData { - string memory placeholderURI = "ipfs://placeholderURI"; - uint256 _nextTokenIdToLazyMintOld = drop.nextTokenIdToMint(); - - // lazy mint next batch - vm.prank(address(caller)); - vm.expectEmit(true, false, false, true); - emit TokensLazyMinted(_nextTokenIdToLazyMintOld, _nextTokenIdToLazyMintOld + amount - 1, placeholderURI, data); - drop.lazyMint(amount, placeholderURI, data); - } -} diff --git a/src/test/burn-to-claim-drop-BTT/logic/lazy-mint/lazyMint.tree b/src/test/burn-to-claim-drop-BTT/logic/lazy-mint/lazyMint.tree deleted file mode 100644 index 39b512286..000000000 --- a/src/test/burn-to-claim-drop-BTT/logic/lazy-mint/lazyMint.tree +++ /dev/null @@ -1,38 +0,0 @@ -lazyMint( - uint256 _amount, - string calldata _baseURIForTokens, - bytes calldata _data -) -// Assume `_data` empty -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── when amount to lazy mint is 0 - │ └── it should revert ✅ - └── when amount to lazy mint is not 0 - └── it should save the batch of tokens starting at `nextTokenIdToLazyMint` ✅ - └── it should store batch id equal to the sum of `nextTokenIdToLazyMint` and `_amount` ✅ - └── it should map the new batch id to `_baseURIForTokens` ✅ - └── it should increase `nextTokenIdToLazyMint` by `_amount` ✅ - └── it should return the new `batchId` ✅ - └── it should emit TokensLazyMinted event ✅ - -// Assume `_data` not empty -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── when amount to lazy mint is 0 - │ └── it should revert ✅ - └── when amount to lazy mint is not 0 - └── when data can't be decoded - │ └── it should revert ✅ - └── when data can be decoded successfully - └── when decoded encryptedURI and provenanceHash are non-empty - └── it should set encrypted data for the new batch equal to _data ✅ - └── it should save the batch of tokens starting at `nextTokenIdToLazyMint` ✅ - └── it should store batch id equal to the sum of `nextTokenIdToLazyMint` and `_amount` ✅ - └── it should map the new batch id to `_baseURIForTokens` ✅ - └── it should increase `nextTokenIdToLazyMint` by `_amount` ✅ - └── it should return the new `batchId` ✅ - └── it should emit TokensLazyMinted event ✅ - diff --git a/src/test/burn-to-claim-drop-BTT/logic/other-functions/other.t.sol b/src/test/burn-to-claim-drop-BTT/logic/other-functions/other.t.sol deleted file mode 100644 index 0f99059b6..000000000 --- a/src/test/burn-to-claim-drop-BTT/logic/other-functions/other.t.sol +++ /dev/null @@ -1,395 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import { BurnToClaimDropERC721 } from "contracts/prebuilts/unaudited/burn-to-claim-drop/BurnToClaimDropERC721.sol"; -import { BurnToClaimDrop721Logic, IERC2981 } from "contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Logic.sol"; -import { IDrop } from "contracts/extension/interface/IDrop.sol"; -import { IStaking721 } from "contracts/extension/interface/IStaking721.sol"; -import { PermissionsEnumerableImpl } from "contracts/extension/upgradeable/impl/PermissionsEnumerableImpl.sol"; - -import { ERC721AStorage } from "contracts/extension/upgradeable/init/ERC721AInit.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { Permissions } from "contracts/extension/Permissions.sol"; - -contract MyBurnToClaimDrop721Logic is BurnToClaimDrop721Logic { - function canSetPlatformFeeInfo() external view returns (bool) { - return _canSetPlatformFeeInfo(); - } - - function canSetPrimarySaleRecipient() external view returns (bool) { - return _canSetPrimarySaleRecipient(); - } - - function canSetOwner() external view returns (bool) { - return _canSetOwner(); - } - - function canSetRoyaltyInfo() external view returns (bool) { - return _canSetRoyaltyInfo(); - } - - function canSetContractURI() external view returns (bool) { - return _canSetContractURI(); - } - - function canSetClaimConditions() external view returns (bool) { - return _canSetClaimConditions(); - } - - function canLazyMint() external view returns (bool) { - return _canLazyMint(); - } - - function canSetBurnToClaim() external view returns (bool) { - return _canSetBurnToClaim(); - } - - function beforeTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) external { - _beforeTokenTransfers(from, to, startTokenId, quantity); - } - - function transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) external returns (uint256 startTokenId) { - ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage(); - startTokenId = data._currentIndex; - _safeMint(_to, _quantityBeingClaimed); - } - - function beforeClaim(uint256 _quantity, AllowlistProof calldata proof) external { - _beforeClaim(address(0), _quantity, address(0), 0, proof, ""); - } - - function mintTo(address _recipient) external { - _safeMint(_recipient, 1); - } -} - -contract BurnToClaimDrop721Logic_OtherFunctions is BaseTest, IExtension { - MyBurnToClaimDrop721Logic public drop; - address internal caller; - address internal recipient; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address dropImpl = address(new BurnToClaimDropERC721(extensions)); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - drop = MyBurnToClaimDrop721Logic( - payable( - address( - new TWProxy( - dropImpl, - abi.encodeCall( - BurnToClaimDropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ) - ); - - caller = getActor(5); - recipient = getActor(6); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](2); - - // Extension: Permissions - address permissions = address(new PermissionsEnumerableImpl()); - - Extension memory extension_permissions; - extension_permissions.metadata = ExtensionMetadata({ - name: "Permissions", - metadataURI: "ipfs://Permissions", - implementation: permissions - }); - - extension_permissions.functions = new ExtensionFunction[](3); - extension_permissions.functions[0] = ExtensionFunction( - Permissions.hasRole.selector, - "hasRole(bytes32,address)" - ); - extension_permissions.functions[1] = ExtensionFunction( - Permissions.grantRole.selector, - "grantRole(bytes32,address)" - ); - extension_permissions.functions[2] = ExtensionFunction( - Permissions.revokeRole.selector, - "revokeRole(bytes32,address)" - ); - - extensions[0] = extension_permissions; - - address dropLogic = address(new MyBurnToClaimDrop721Logic()); - - Extension memory extension_drop; - extension_drop.metadata = ExtensionMetadata({ - name: "MyBurnToClaimDrop721Logic", - metadataURI: "ipfs://MyBurnToClaimDrop721Logic", - implementation: dropLogic - }); - - extension_drop.functions = new ExtensionFunction[](18); - extension_drop.functions[0] = ExtensionFunction( - MyBurnToClaimDrop721Logic.canSetPlatformFeeInfo.selector, - "canSetPlatformFeeInfo()" - ); - extension_drop.functions[1] = ExtensionFunction( - MyBurnToClaimDrop721Logic.canSetPrimarySaleRecipient.selector, - "canSetPrimarySaleRecipient()" - ); - extension_drop.functions[2] = ExtensionFunction( - MyBurnToClaimDrop721Logic.canSetOwner.selector, - "canSetOwner()" - ); - extension_drop.functions[3] = ExtensionFunction( - MyBurnToClaimDrop721Logic.canSetRoyaltyInfo.selector, - "canSetRoyaltyInfo()" - ); - extension_drop.functions[4] = ExtensionFunction( - MyBurnToClaimDrop721Logic.canSetClaimConditions.selector, - "canSetClaimConditions()" - ); - extension_drop.functions[5] = ExtensionFunction( - MyBurnToClaimDrop721Logic.canSetContractURI.selector, - "canSetContractURI()" - ); - extension_drop.functions[6] = ExtensionFunction( - MyBurnToClaimDrop721Logic.canLazyMint.selector, - "canLazyMint()" - ); - extension_drop.functions[7] = ExtensionFunction( - MyBurnToClaimDrop721Logic.canSetBurnToClaim.selector, - "canSetBurnToClaim()" - ); - extension_drop.functions[8] = ExtensionFunction( - MyBurnToClaimDrop721Logic.beforeTokenTransfers.selector, - "beforeTokenTransfers(address,address,uint256,uint256)" - ); - extension_drop.functions[9] = ExtensionFunction(BurnToClaimDrop721Logic.totalMinted.selector, "totalMinted()"); - extension_drop.functions[10] = ExtensionFunction( - MyBurnToClaimDrop721Logic.transferTokensOnClaim.selector, - "transferTokensOnClaim(address,uint256)" - ); - extension_drop.functions[11] = ExtensionFunction( - BurnToClaimDrop721Logic.supportsInterface.selector, - "supportsInterface(bytes4)" - ); - extension_drop.functions[12] = ExtensionFunction( - MyBurnToClaimDrop721Logic.beforeClaim.selector, - "beforeClaim(uint256,(bytes32[],uint256,uint256,address))" - ); - extension_drop.functions[13] = ExtensionFunction( - BurnToClaimDrop721Logic.lazyMint.selector, - "lazyMint(uint256,string,bytes)" - ); - extension_drop.functions[14] = ExtensionFunction( - BurnToClaimDrop721Logic.setMaxTotalMinted.selector, - "setMaxTotalMinted(uint256)" - ); - extension_drop.functions[15] = ExtensionFunction(BurnToClaimDrop721Logic.burn.selector, "burn(uint256)"); - extension_drop.functions[16] = ExtensionFunction(MyBurnToClaimDrop721Logic.mintTo.selector, "mintTo(address)"); - extension_drop.functions[17] = ExtensionFunction( - IERC721.setApprovalForAll.selector, - "setApprovalForAll(address,bool)" - ); - - extensions[1] = extension_drop; - } - - modifier whenCallerAuthorized() { - caller = deployer; - _; - } - - function test_canSetPlatformFeeInfo_notAuthorized() public { - vm.prank(caller); - drop.canSetPlatformFeeInfo(); - } - - function test_canSetPlatformFeeInfo() public whenCallerAuthorized { - vm.prank(caller); - assertTrue(drop.canSetPlatformFeeInfo()); - } - - function test_canSetPrimarySaleRecipient_notAuthorized() public { - vm.prank(caller); - drop.canSetPrimarySaleRecipient(); - } - - function test_canSetPrimarySaleRecipient() public whenCallerAuthorized { - vm.prank(caller); - assertTrue(drop.canSetPrimarySaleRecipient()); - } - - function test_canSetOwner_notAuthorized() public { - vm.prank(caller); - drop.canSetOwner(); - } - - function test_canSetOwner() public whenCallerAuthorized { - vm.prank(caller); - assertTrue(drop.canSetOwner()); - } - - function test_canSetRoyaltyInfo_notAuthorized() public { - vm.prank(caller); - drop.canSetRoyaltyInfo(); - } - - function test_canSetRoyaltyInfo() public whenCallerAuthorized { - vm.prank(caller); - assertTrue(drop.canSetRoyaltyInfo()); - } - - function test_canSetContractURI_notAuthorized() public { - vm.prank(caller); - drop.canSetContractURI(); - } - - function test_canSetContractURI() public whenCallerAuthorized { - vm.prank(caller); - assertTrue(drop.canSetContractURI()); - } - - function test_canSetClaimConditions_notAuthorized() public { - vm.prank(caller); - drop.canSetClaimConditions(); - } - - function test_canSetClaimConditions() public whenCallerAuthorized { - vm.prank(caller); - assertTrue(drop.canSetClaimConditions()); - } - - function test_canLazyMint_notAuthorized() public { - vm.prank(caller); - drop.canLazyMint(); - } - - function test_canLazyMint() public whenCallerAuthorized { - vm.prank(caller); - assertTrue(drop.canLazyMint()); - } - - function test_canSetBurnToClaim_notAuthorized() public { - vm.prank(caller); - drop.canSetBurnToClaim(); - } - - function test_canSetBurnToClaim() public whenCallerAuthorized { - vm.prank(caller); - assertTrue(drop.canSetBurnToClaim()); - } - - function test_beforeTokenTransfers_restricted_notTransferRole() public { - vm.prank(deployer); - Permissions(address(drop)).revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.expectRevert("!Transfer-Role"); - drop.beforeTokenTransfers(caller, address(0x123), 0, 1); - } - - modifier whenTransferRole() { - vm.prank(deployer); - Permissions(address(drop)).grantRole(keccak256("TRANSFER_ROLE"), caller); - _; - } - - function test_beforeTokenTransfers_restricted() public whenTransferRole { - drop.beforeTokenTransfers(caller, address(0x123), 0, 1); - } - - function test_totalMinted() public { - uint256 totalMinted = drop.totalMinted(); - assertEq(totalMinted, 0); - - // mint tokens - drop.transferTokensOnClaim(caller, 10); - totalMinted = drop.totalMinted(); - assertEq(totalMinted, 10); - } - - function test_supportsInterface() public { - assertTrue(drop.supportsInterface(type(IERC2981).interfaceId)); - assertFalse(drop.supportsInterface(type(IStaking721).interfaceId)); - } - - function test_beforeClaim() public { - bytes32[] memory emptyBytes32Array = new bytes32[](0); - IDrop.AllowlistProof memory proof = IDrop.AllowlistProof(emptyBytes32Array, 0, 0, address(0)); - drop.beforeClaim(0, proof); - - vm.expectRevert("!Tokens"); - drop.beforeClaim(1, proof); - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", ""); - - vm.prank(deployer); - drop.setMaxTotalMinted(1); - - vm.expectRevert("exceed max total mint cap."); - drop.beforeClaim(10, proof); - - vm.prank(deployer); - drop.setMaxTotalMinted(0); - - drop.beforeClaim(10, proof); // no revert if max total mint cap is set to 0 - } - - //=========== burn tests ========= - - function test_burn_whenNotOwnerNorApproved() public { - // mint - drop.mintTo(recipient); - - // burn - vm.expectRevert(); - drop.burn(0); - } - - function test_burn_whenOwner() public { - // mint - drop.mintTo(recipient); - - // burn - vm.prank(recipient); - drop.burn(0); - - vm.expectRevert(); // checking non-existent token, because burned - drop.ownerOf(0); - } - - function test_burn_whenApproved() public { - drop.mintTo(recipient); - - vm.prank(recipient); - drop.setApprovalForAll(caller, true); - - // burn - vm.prank(caller); - drop.burn(0); - - vm.expectRevert(); // checking non-existent token, because burned - drop.ownerOf(0); - } -} diff --git a/src/test/burn-to-claim-drop-BTT/logic/other-functions/other.tree b/src/test/burn-to-claim-drop-BTT/logic/other-functions/other.tree deleted file mode 100644 index a7ed4a957..000000000 --- a/src/test/burn-to-claim-drop-BTT/logic/other-functions/other.tree +++ /dev/null @@ -1,86 +0,0 @@ -_canSetPlatformFeeInfo() -├── when the caller doesn't have DEFAULT_ADMIN_ROLE -│ └── it should revert ✅ -└── when the caller has DEFAULT_ADMIN_ROLE - └── it should return true ✅ - -_canSetPrimarySaleRecipient() -├── when the caller doesn't have DEFAULT_ADMIN_ROLE -│ └── it should revert ✅ -└── when the caller has DEFAULT_ADMIN_ROLE - └── it should return true ✅ - -_canSetOwner() -├── when the caller doesn't have DEFAULT_ADMIN_ROLE -│ └── it should revert ✅ -└── when the caller has DEFAULT_ADMIN_ROLE - └── it should return true ✅ - -_canSetRoyaltyInfo() -├── when the caller doesn't have DEFAULT_ADMIN_ROLE -│ └── it should revert ✅ -└── when the caller has DEFAULT_ADMIN_ROLE - └── it should return true ✅ - -_canSetContractURI() -├── when the caller doesn't have DEFAULT_ADMIN_ROLE -│ └── it should revert ✅ -└── when the caller has DEFAULT_ADMIN_ROLE - └── it should return true ✅ - -_canSetClaimConditions() -├── when the caller doesn't have DEFAULT_ADMIN_ROLE -│ └── it should revert ✅ -└── when the caller has DEFAULT_ADMIN_ROLE - └── it should return true ✅ - -_canLazyMint() -├── when the caller doesn't have MINTER_ROLE -│ └── it should revert ✅ -└── when the caller has MINTER_ROLE - └── it should return true ✅ - -_canSetBurnToClaim() -├── when the caller doesn't have DEFAULT_ADMIN_ROLE -│ └── it should revert ✅ -└── when the caller has DEFAULT_ADMIN_ROLE - └── it should return true ✅ - -burn(uint256 tokenId) -├── when the caller isn't the owner of `tokenId` or token not approved to caller -│ └── it should revert ✅ -└── when the caller owns `tokenId` -│ └── it should burn the token ✅ -└── when the `tokenId` is approved to caller - └── it should burn the token ✅ - -_beforeTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity -) -├── when transfers are restricted (i.e. address(0) doesn't have transfer role, or from-to addresses are not address(0) -│ └── when from and to don't have transfer role -│ └── it should revert ✅ - -totalMinted() -├── should return the quantity of tokens minted (i.e. claimed) so far ✅ - -supportsInterface(bytes4 interfaceId) -├── it should return true for supported interface ✅ -├── it should return false for not supported interface ✅ - -_beforeClaim( - address, - uint256 _quantity, - address, - uint256, - AllowlistProof calldata, - bytes memory -) -├── when `_quantity` exceeds lazy minted quantity -│ └── it should revert ✅ -├── when `_quantity` exceeds max total mint cap (if not zero) -│ └── it should revert ✅ - diff --git a/src/test/burn-to-claim-drop-BTT/logic/reveal/reveal.t.sol b/src/test/burn-to-claim-drop-BTT/logic/reveal/reveal.t.sol deleted file mode 100644 index f60d11351..000000000 --- a/src/test/burn-to-claim-drop-BTT/logic/reveal/reveal.t.sol +++ /dev/null @@ -1,241 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import { BurnToClaimDropERC721 } from "contracts/prebuilts/unaudited/burn-to-claim-drop/BurnToClaimDropERC721.sol"; -import { BurnToClaimDrop721Logic, ERC721AUpgradeable, DelayedReveal, LazyMint, Drop, BurnToClaim, PrimarySale, PlatformFee } from "contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Logic.sol"; -import { PermissionsEnumerableImpl } from "contracts/extension/upgradeable/impl/PermissionsEnumerableImpl.sol"; -import { Royalty } from "contracts/extension/upgradeable/Royalty.sol"; -import { BatchMintMetadata } from "contracts/extension/upgradeable/BatchMintMetadata.sol"; -import { IBurnToClaim } from "contracts/extension/interface/IBurnToClaim.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; - -contract BurnToClaimDropERC721Logic_Reveal is BaseTest, IExtension { - using Strings for uint256; - using Strings for address; - - event TokenURIRevealed(uint256 indexed index, string revealedURI); - - BurnToClaimDrop721Logic public drop; - uint256 internal startId; - uint256 internal amount; - uint256[] internal batchIds; - address internal caller; - bytes internal data; - string internal placeholderURI; - bytes internal originalURI; - uint256 internal _index; - bytes internal _key; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address dropImpl = address(new BurnToClaimDropERC721(extensions)); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - drop = BurnToClaimDrop721Logic( - payable( - address( - new TWProxy( - dropImpl, - abi.encodeCall( - BurnToClaimDropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ) - ); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - - startId = 0; - originalURI = bytes("ipfs://originalURI"); - placeholderURI = "ipfs://placeholderURI"; - _key = "key123"; - // mint 3 batches - vm.startPrank(deployer); - for (uint256 i = 0; i < 3; i++) { - uint256 _amount = (i + 1) * 10; - uint256 batchId = startId + _amount; - batchIds.push(batchId); - - // set encrypted uri for one of the batches - if (i == 1) { - bytes memory _encryptedURI = drop.encryptDecrypt(originalURI, _key); - bytes32 _provenanceHash = keccak256(abi.encodePacked(originalURI, _key, block.chainid)); - - startId = drop.lazyMint(_amount, placeholderURI, abi.encode(_encryptedURI, _provenanceHash)); - } else { - startId = drop.lazyMint(_amount, string(originalURI), ""); - } - } - vm.stopPrank(); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](2); - - // Extension: Permissions - address permissions = address(new PermissionsEnumerableImpl()); - - Extension memory extension_permissions; - extension_permissions.metadata = ExtensionMetadata({ - name: "Permissions", - metadataURI: "ipfs://Permissions", - implementation: permissions - }); - - extension_permissions.functions = new ExtensionFunction[](1); - extension_permissions.functions[0] = ExtensionFunction( - Permissions.hasRole.selector, - "hasRole(bytes32,address)" - ); - - extensions[0] = extension_permissions; - - address dropLogic = address(new BurnToClaimDrop721Logic()); - - Extension memory extension_drop; - extension_drop.metadata = ExtensionMetadata({ - name: "BurnToClaimDrop721Logic", - metadataURI: "ipfs://BurnToClaimDrop721Logic", - implementation: dropLogic - }); - - extension_drop.functions = new ExtensionFunction[](6); - extension_drop.functions[0] = ExtensionFunction(BurnToClaimDrop721Logic.tokenURI.selector, "tokenURI(uint256)"); - extension_drop.functions[1] = ExtensionFunction( - BurnToClaimDrop721Logic.lazyMint.selector, - "lazyMint(uint256,string,bytes)" - ); - extension_drop.functions[2] = ExtensionFunction( - BurnToClaimDrop721Logic.reveal.selector, - "reveal(uint256,bytes)" - ); - extension_drop.functions[3] = ExtensionFunction( - DelayedReveal.encryptDecrypt.selector, - "encryptDecrypt(bytes,bytes)" - ); - extension_drop.functions[4] = ExtensionFunction( - DelayedReveal.isEncryptedBatch.selector, - "isEncryptedBatch(uint256)" - ); - extension_drop.functions[5] = ExtensionFunction( - DelayedReveal.getRevealURI.selector, - "getRevealURI(uint256,bytes)" - ); - - extensions[1] = extension_drop; - } - - function test_reveal_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert("Not authorized"); - drop.lazyMint(amount, "", ""); - } - - modifier whenCallerAuthorized() { - caller = deployer; - _; - } - - function test_reveal_invalidIndex() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectRevert("Invalid index"); - drop.reveal(4, "key"); - } - - modifier whenValidIndex() { - _; - } - - function test_reveal_noEncryptedURI() public whenCallerAuthorized whenValidIndex { - _index = 2; - vm.prank(address(caller)); - vm.expectRevert("Nothing to reveal"); - drop.reveal(_index, "key"); - } - - modifier whenEncryptedURI() { - _index = 1; - _; - } - - function test_reveal_incorrectKey() public whenCallerAuthorized whenValidIndex whenEncryptedURI { - vm.prank(address(caller)); - vm.expectRevert("Incorrect key"); - drop.reveal(_index, "incorrect key"); - } - - modifier whenCorrectKey() { - _; - } - - function test_reveal() public whenCallerAuthorized whenValidIndex whenEncryptedURI { - //state before - for (uint256 i = 0; i < 3; i++) { - uint256 _startId = i > 0 ? batchIds[i - 1] : 0; - - for (uint256 j = _startId; j < batchIds[i]; j += 1) { - string memory uri = drop.tokenURI(j); - if (i == 1) { - assertEq(uri, string(abi.encodePacked(placeholderURI, "0"))); // <-- placeholder URI for encrypted batch - } else { - assertEq(uri, string(abi.encodePacked(string(originalURI), j.toString()))); - } - } - } - - // reveal - vm.prank(address(caller)); - string memory revealedURI = drop.reveal(_index, _key); - - // check state after - vm.expectRevert("Nothing to reveal"); - drop.getRevealURI(_index, _key); - - assertEq(revealedURI, string(originalURI)); - - for (uint256 i = 0; i < 3; i++) { - uint256 _startId = i > 0 ? batchIds[i - 1] : 0; - - for (uint256 j = _startId; j < batchIds[i]; j += 1) { - string memory uri = drop.tokenURI(j); - assertEq(uri, string(abi.encodePacked(string(originalURI), j.toString()))); - } - } - } - - function test_reveal_event() public whenCallerAuthorized whenValidIndex whenEncryptedURI { - vm.prank(address(caller)); - vm.expectEmit(true, false, false, true); - emit TokenURIRevealed(1, string(originalURI)); - drop.reveal(_index, _key); - } -} diff --git a/src/test/burn-to-claim-drop-BTT/logic/reveal/reveal.tree b/src/test/burn-to-claim-drop-BTT/logic/reveal/reveal.tree deleted file mode 100644 index febcdb258..000000000 --- a/src/test/burn-to-claim-drop-BTT/logic/reveal/reveal.tree +++ /dev/null @@ -1,16 +0,0 @@ -reveal(uint256 index, bytes calldata key) -├── when caller doesn't have minter_role -│ └── it should revert ✅ -└── when caller has minter role - ├── when index is more than number of batches - │ └── it should revert ✅ - └── when index is within total number of batches - ├── when there is no encrypted uri associated with the batch index - │ └── it should revert ✅ - └── when there is an encrypted uri present - ├── when the provenance hash generated is incorrect for the given key - │ └── it should revert ✅ - └── when provenance hash is correct - └── it should set the encrypted data for this batch to "" ✅ - └── it should set base URI for this batch to correct revealed URI ✅ - └── it should emit TokenURIRevealed event ✅ \ No newline at end of file diff --git a/src/test/burn-to-claim-drop-BTT/router/initialize/initialize.t.sol b/src/test/burn-to-claim-drop-BTT/router/initialize/initialize.t.sol deleted file mode 100644 index cb1da0318..000000000 --- a/src/test/burn-to-claim-drop-BTT/router/initialize/initialize.t.sol +++ /dev/null @@ -1,455 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import { BurnToClaimDropERC721 } from "contracts/prebuilts/unaudited/burn-to-claim-drop/BurnToClaimDropERC721.sol"; -import { BurnToClaimDrop721Logic } from "contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Logic.sol"; -import { PermissionsEnumerableImpl } from "contracts/extension/upgradeable/impl/PermissionsEnumerableImpl.sol"; - -import { ERC721AStorage } from "contracts/extension/upgradeable/init/ERC721AInit.sol"; -import { ERC2771ContextStorage } from "contracts/extension/upgradeable/init/ERC2771ContextInit.sol"; -import { ContractMetadataStorage } from "contracts/extension/upgradeable/init/ContractMetadataInit.sol"; -import { OwnableStorage } from "contracts/extension/upgradeable/init/OwnableInit.sol"; -import { PlatformFeeStorage } from "contracts/extension/upgradeable/init/PlatformFeeInit.sol"; -import { RoyaltyStorage } from "contracts/extension/upgradeable/init/RoyaltyInit.sol"; -import { PrimarySaleStorage } from "contracts/extension/upgradeable/init/PrimarySaleInit.sol"; -import { PermissionsStorage } from "contracts/extension/upgradeable/init/PermissionsInit.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract BurnToClaimDropERC721Router is BurnToClaimDropERC721 { - constructor(Extension[] memory _extensions) BurnToClaimDropERC721(_extensions) {} - - function hasRole(bytes32 role, address addr) public view returns (bool) { - return _hasRole(role, addr); - } - - function roleAdmin(bytes32 role) public view returns (bytes32) { - PermissionsStorage.Data storage data = PermissionsStorage.data(); - return data._getRoleAdmin[role]; - } - - function name() public view returns (string memory) { - ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage(); - return data._name; - } - - function symbol() public view returns (string memory) { - ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage(); - return data._symbol; - } - - function trustedForwarders(address[] memory _trustedForwarders) public view returns (bool) { - ERC2771ContextStorage.Data storage data = ERC2771ContextStorage.data(); - - for (uint256 i = 0; i < _trustedForwarders.length; i++) { - if (!data.trustedForwarder[_trustedForwarders[i]]) { - return false; - } - } - return true; - } - - function contractURI() public view returns (string memory) { - ContractMetadataStorage.Data storage data = ContractMetadataStorage.data(); - return data.contractURI; - } - - function owner() public view returns (address) { - OwnableStorage.Data storage data = OwnableStorage.data(); - return data._owner; - } - - function platformFeeRecipient() public view returns (address) { - PlatformFeeStorage.Data storage data = PlatformFeeStorage.data(); - return data.platformFeeRecipient; - } - - function platformFeeBps() public view returns (uint16) { - PlatformFeeStorage.Data storage data = PlatformFeeStorage.data(); - return data.platformFeeBps; - } - - function royaltyRecipient() public view returns (address) { - RoyaltyStorage.Data storage data = RoyaltyStorage.data(); - return data.royaltyRecipient; - } - - function royaltyBps() public view returns (uint16) { - RoyaltyStorage.Data storage data = RoyaltyStorage.data(); - return data.royaltyBps; - } - - function primarySaleRecipient() public view returns (address) { - PrimarySaleStorage.Data storage data = PrimarySaleStorage.data(); - return data.recipient; - } -} - -contract BurnToClaimDropERC721_Initialize is BaseTest, IExtension { - address public implementation; - address public proxy; - - event ContractURIUpdated(string prevURI, string newURI); - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); - event PrimarySaleRecipientUpdated(address indexed recipient); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); // setup just a couple of extension/functions for testing here - implementation = address(new BurnToClaimDropERC721Router(extensions)); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - BurnToClaimDropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](2); - - // Extension: Permissions - address permissions = address(new PermissionsEnumerableImpl()); - - Extension memory extension_permissions; - extension_permissions.metadata = ExtensionMetadata({ - name: "Permissions", - metadataURI: "ipfs://Permissions", - implementation: permissions - }); - - extension_permissions.functions = new ExtensionFunction[](1); - extension_permissions.functions[0] = ExtensionFunction( - Permissions.hasRole.selector, - "hasRole(bytes32,address)" - ); - - extensions[0] = extension_permissions; - - address dropLogic = address(new BurnToClaimDrop721Logic()); - - Extension memory extension_drop; - extension_drop.metadata = ExtensionMetadata({ - name: "BurnToClaimDrop721Logic", - metadataURI: "ipfs://BurnToClaimDrop721Logic", - implementation: dropLogic - }); - - extension_drop.functions = new ExtensionFunction[](1); - extension_drop.functions[0] = ExtensionFunction(BurnToClaimDrop721Logic.tokenURI.selector, "tokenURI(uint256)"); - extensions[1] = extension_drop; - } - - function test_initialize_initializingImplementation() public { - vm.expectRevert("Initializable: contract is already initialized"); - BurnToClaimDropERC721Router(payable(implementation)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - modifier whenNotImplementation() { - _; - } - - function test_initialize_proxyAlreadyInitialized() public whenNotImplementation { - vm.expectRevert("Initializable: contract is already initialized"); - BurnToClaimDropERC721Router(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - modifier whenProxyNotInitialized() { - proxy = address(new TWProxy(implementation, "")); - _; - } - - function test_initialize() public whenNotImplementation whenProxyNotInitialized { - BurnToClaimDropERC721(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - - // check state - BurnToClaimDropERC721Router router = BurnToClaimDropERC721Router(payable(proxy)); - assertEq(router.name(), NAME); - assertEq(router.symbol(), SYMBOL); - assertTrue(router.trustedForwarders(forwarders())); - assertEq(router.platformFeeRecipient(), platformFeeRecipient); - assertEq(router.platformFeeBps(), platformFeeBps); - assertEq(router.royaltyRecipient(), royaltyRecipient); - assertEq(router.royaltyBps(), royaltyBps); - assertEq(router.primarySaleRecipient(), saleRecipient); - assertTrue(router.hasRole(bytes32(0x00), deployer)); - assertTrue(router.hasRole(keccak256("TRANSFER_ROLE"), deployer)); - assertTrue(router.hasRole(keccak256("TRANSFER_ROLE"), address(0))); - assertTrue(router.hasRole(keccak256("MINTER_ROLE"), deployer)); - assertTrue(router.hasRole(keccak256("EXTENSION_ROLE"), deployer)); - assertEq(router.roleAdmin(keccak256("EXTENSION_ROLE")), keccak256("EXTENSION_ROLE")); - - // check default extensions - Extension[] memory _extensions = router.getAllExtensions(); - assertEq(_extensions.length, 2); - } - - function test_initialize_event_ContractURIUpdated() public whenNotImplementation whenProxyNotInitialized { - vm.expectEmit(false, false, false, true); - emit ContractURIUpdated("", CONTRACT_URI); - BurnToClaimDropERC721(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_OwnerUpdated() public whenNotImplementation whenProxyNotInitialized { - vm.expectEmit(true, true, false, false); - emit OwnerUpdated(address(0), deployer); - BurnToClaimDropERC721(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_DefaultAdmin() public whenNotImplementation whenProxyNotInitialized { - bytes32 _defaultAdminRole = bytes32(0x00); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_defaultAdminRole, deployer, deployer); - BurnToClaimDropERC721(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_MinterRole() public whenNotImplementation whenProxyNotInitialized { - bytes32 _minterRole = keccak256("MINTER_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_minterRole, deployer, deployer); - BurnToClaimDropERC721(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_TransferRole() public whenNotImplementation whenProxyNotInitialized { - bytes32 _transferRole = keccak256("TRANSFER_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_transferRole, deployer, deployer); - BurnToClaimDropERC721(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_TransferRole_AddressZero() - public - whenNotImplementation - whenProxyNotInitialized - { - bytes32 _transferRole = keccak256("TRANSFER_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_transferRole, address(0), deployer); - BurnToClaimDropERC721(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_ExtensionRole() public whenNotImplementation whenProxyNotInitialized { - bytes32 _extensionRole = keccak256("EXTENSION_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_extensionRole, deployer, deployer); - BurnToClaimDropERC721(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleAdminChanged_ExtensionRole() - public - whenNotImplementation - whenProxyNotInitialized - { - bytes32 _extensionRole = keccak256("EXTENSION_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleAdminChanged(_extensionRole, bytes32(0x00), _extensionRole); - BurnToClaimDropERC721(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_PlatformFeeInfoUpdated() public whenNotImplementation whenProxyNotInitialized { - vm.prank(deployer); - vm.expectEmit(true, false, false, true); - emit PlatformFeeInfoUpdated(platformFeeRecipient, platformFeeBps); - BurnToClaimDropERC721(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_DefaultRoyalty() public whenNotImplementation whenProxyNotInitialized { - vm.prank(deployer); - vm.expectEmit(true, false, false, true); - emit DefaultRoyalty(royaltyRecipient, royaltyBps); - BurnToClaimDropERC721(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_PrimarySaleRecipientUpdated() public whenNotImplementation whenProxyNotInitialized { - vm.prank(deployer); - vm.expectEmit(true, false, false, false); - emit PrimarySaleRecipientUpdated(saleRecipient); - BurnToClaimDropERC721(payable(proxy)).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } -} diff --git a/src/test/burn-to-claim-drop-BTT/router/initialize/initialize.tree b/src/test/burn-to-claim-drop-BTT/router/initialize/initialize.tree deleted file mode 100644 index 295d65120..000000000 --- a/src/test/burn-to-claim-drop-BTT/router/initialize/initialize.tree +++ /dev/null @@ -1,44 +0,0 @@ -initialize( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _saleRecipient, - address _royaltyRecipient, - uint128 _royaltyBps, - uint128 _platformFeeBps, - address _platformFeeRecipient -) -├── when it is the implementation contract (not proxy) -│ └── it should revert ✅ -└── when it is a proxy to the implementation - └── when it is already initialized - │ └── it should revert ✅ - └── when it is not initialized - └── it should initialize base-router with default extensions if any ✅ - └── it should set trustedForwarder mapping to true for all addresses in `_trustedForwarders` ✅ - └── it should set _name and _symbol to `_name` and `_symbol` param values respectively ✅ - └── it should set contractURI to `_contractURI` param value ✅ - └── it should emit ContractURIUpdated event ✅ - └── it should set _owner to `_defaultAdmin` param value ✅ - └── it should emit OwnerUpdated event ✅ - └── it should grant 0x00 (DEFAULT_ADMIN_ROLE) to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should grant MINTER_ROLE to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should grant TRANSFER_ROLE to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should grant TRANSFER_ROLE to address(0) ✅ - └── it should emit RoleGranted event ✅ - └── it should grant EXTENSION_ROLE to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should set EXTENSION_ROLE as role admin for EXTENSION_ROLE ✅ - └── it should emit RoleAdminChanged event ✅ - └── it should set platformFeeRecipient and platformFeeBps as `_platformFeeRecipient` and `_platformFeeBps` respectively ✅ - └── it should emit PlatformFeeInfoUpdated event ✅ - └── it should set royaltyRecipient and royaltyBps as `_royaltyRecipient` and `_royaltyBps` respectively ✅ - └── it should emit DefaultRoyalty event ✅ - └── it should set primary sale recipient as `_saleRecipient` param value ✅ - └── it should emit PrimarySaleRecipientUpdated event ✅ - diff --git a/src/test/burn-to-claim-drop-BTT/router/other-functions/other.t.sol b/src/test/burn-to-claim-drop-BTT/router/other-functions/other.t.sol deleted file mode 100644 index d9cdb0656..000000000 --- a/src/test/burn-to-claim-drop-BTT/router/other-functions/other.t.sol +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import { BurnToClaimDropERC721 } from "contracts/prebuilts/unaudited/burn-to-claim-drop/BurnToClaimDropERC721.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract BurnToClaimDropERC721Router is BurnToClaimDropERC721 { - constructor(Extension[] memory _extensions) BurnToClaimDropERC721(_extensions) {} - - function isAuthorizedCallToUpgrade() public view returns (bool) { - return _isAuthorizedCallToUpgrade(); - } -} - -contract BurnToClaimDropERC721_OtherFunctions is BaseTest, IExtension { - address public implementation; - address public proxy; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - Extension[] memory extensions; - implementation = address(new BurnToClaimDropERC721Router(extensions)); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - BurnToClaimDropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - } - - function test_contractType() public { - BurnToClaimDropERC721Router router = BurnToClaimDropERC721Router(payable(proxy)); - assertEq(router.contractType(), bytes32("BurnToClaimDropERC721")); - } - - function test_contractVersion() public { - BurnToClaimDropERC721Router router = BurnToClaimDropERC721Router(payable(proxy)); - assertEq(router.contractVersion(), uint8(5)); - } - - function test_isAuthorizedCallToUpgrade_notExtensionRole() public { - BurnToClaimDropERC721Router router = BurnToClaimDropERC721Router(payable(proxy)); - assertFalse(router.isAuthorizedCallToUpgrade()); - } - - modifier whenExtensionRole() { - _; - } - - function test_isAuthorizedCallToUpgrade() public whenExtensionRole { - BurnToClaimDropERC721Router router = BurnToClaimDropERC721Router(payable(proxy)); - - vm.prank(deployer); - assertTrue(router.isAuthorizedCallToUpgrade()); - } -} diff --git a/src/test/burn-to-claim-drop-BTT/router/other-functions/other.tree b/src/test/burn-to-claim-drop-BTT/router/other-functions/other.tree deleted file mode 100644 index 98e8df4fe..000000000 --- a/src/test/burn-to-claim-drop-BTT/router/other-functions/other.tree +++ /dev/null @@ -1,12 +0,0 @@ -contractType() -├── it should return bytes32("BurnToClaimDropERC721") ✅ - -contractVersion() -├── it should return uint8(5) ✅ - -_isAuthorizedCallToUpgrade() -├── when the caller doesn't have EXTENSION_ROLE -│ └── it should revert ✅ -└── when the caller has EXTENSION_ROLE - └── it should return true ✅ - diff --git a/src/test/burn-to-claim-drop/BurnToClaimDropERC721.t.sol b/src/test/burn-to-claim-drop/BurnToClaimDropERC721.t.sol deleted file mode 100644 index b3994d496..000000000 --- a/src/test/burn-to-claim-drop/BurnToClaimDropERC721.t.sol +++ /dev/null @@ -1,1883 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../utils/BaseTest.sol"; -import { BurnToClaimDropERC721 } from "contracts/prebuilts/unaudited/burn-to-claim-drop/BurnToClaimDropERC721.sol"; -import { BurnToClaimDrop721Logic, ERC721AUpgradeable, DelayedReveal, LazyMint, Drop, BurnToClaim, PrimarySale, PlatformFee } from "contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Logic.sol"; -import { PermissionsEnumerableImpl } from "contracts/extension/upgradeable/impl/PermissionsEnumerableImpl.sol"; -import { Royalty } from "contracts/extension/upgradeable/Royalty.sol"; -import { BatchMintMetadata } from "contracts/extension/upgradeable/BatchMintMetadata.sol"; -import { IBurnToClaim } from "contracts/extension/interface/IBurnToClaim.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; - -contract BurnToClaimDropERC721Test is BaseTest, IExtension { - using Strings for uint256; - using Strings for address; - - event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI); - event TokenURIRevealed(uint256 indexed index, string revealedURI); - - BurnToClaimDrop721Logic public drop; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address dropImpl = address(new BurnToClaimDropERC721(extensions)); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - drop = BurnToClaimDrop721Logic( - payable( - address( - new TWProxy( - dropImpl, - abi.encodeCall( - BurnToClaimDropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ) - ); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](2); - - // Extension: Permissions - address permissions = address(new PermissionsEnumerableImpl()); - - Extension memory extension_permissions; - extension_permissions.metadata = ExtensionMetadata({ - name: "Permissions", - metadataURI: "ipfs://Permissions", - implementation: permissions - }); - - extension_permissions.functions = new ExtensionFunction[](7); - extension_permissions.functions[0] = ExtensionFunction( - Permissions.hasRole.selector, - "hasRole(bytes32,address)" - ); - extension_permissions.functions[1] = ExtensionFunction( - Permissions.hasRoleWithSwitch.selector, - "hasRoleWithSwitch(bytes32,address)" - ); - extension_permissions.functions[2] = ExtensionFunction( - Permissions.grantRole.selector, - "grantRole(bytes32,address)" - ); - extension_permissions.functions[3] = ExtensionFunction( - Permissions.renounceRole.selector, - "renounceRole(bytes32,address)" - ); - extension_permissions.functions[4] = ExtensionFunction( - Permissions.revokeRole.selector, - "revokeRole(bytes32,address)" - ); - extension_permissions.functions[5] = ExtensionFunction( - PermissionsEnumerable.getRoleMemberCount.selector, - "getRoleMemberCount(bytes32)" - ); - extension_permissions.functions[6] = ExtensionFunction( - PermissionsEnumerable.getRoleMember.selector, - "getRoleMember(bytes32,uint256)" - ); - - extensions[0] = extension_permissions; - - address dropLogic = address(new BurnToClaimDrop721Logic()); - - Extension memory extension_drop; - extension_drop.metadata = ExtensionMetadata({ - name: "BurnToClaimDrop721Logic", - metadataURI: "ipfs://BurnToClaimDrop721Logic", - implementation: dropLogic - }); - - extension_drop.functions = new ExtensionFunction[](32); - extension_drop.functions[0] = ExtensionFunction(BurnToClaimDrop721Logic.tokenURI.selector, "tokenURI(uint256)"); - extension_drop.functions[1] = ExtensionFunction( - BurnToClaimDrop721Logic.lazyMint.selector, - "lazyMint(uint256,string,bytes)" - ); - extension_drop.functions[2] = ExtensionFunction( - BurnToClaimDrop721Logic.reveal.selector, - "reveal(uint256,bytes)" - ); - extension_drop.functions[3] = ExtensionFunction(Drop.claimCondition.selector, "claimCondition()"); - extension_drop.functions[4] = ExtensionFunction( - BatchMintMetadata.getBaseURICount.selector, - "getBaseURICount()" - ); - extension_drop.functions[5] = ExtensionFunction( - Drop.claim.selector, - "claim(address,uint256,address,uint256,(bytes32[],uint256,uint256,address),bytes)" - ); - extension_drop.functions[6] = ExtensionFunction( - Drop.setClaimConditions.selector, - "setClaimConditions((uint256,uint256,uint256,uint256,bytes32,uint256,address,string)[],bool)" - ); - extension_drop.functions[7] = ExtensionFunction( - Drop.getActiveClaimConditionId.selector, - "getActiveClaimConditionId()" - ); - extension_drop.functions[8] = ExtensionFunction( - Drop.getClaimConditionById.selector, - "getClaimConditionById(uint256)" - ); - extension_drop.functions[9] = ExtensionFunction( - Drop.getSupplyClaimedByWallet.selector, - "getSupplyClaimedByWallet(uint256,address)" - ); - extension_drop.functions[10] = ExtensionFunction(BurnToClaimDrop721Logic.totalMinted.selector, "totalMinted()"); - extension_drop.functions[11] = ExtensionFunction( - BurnToClaimDrop721Logic.nextTokenIdToMint.selector, - "nextTokenIdToMint()" - ); - extension_drop.functions[12] = ExtensionFunction( - IERC721Upgradeable.setApprovalForAll.selector, - "setApprovalForAll(address,bool)" - ); - extension_drop.functions[13] = ExtensionFunction( - IERC721Upgradeable.approve.selector, - "approve(address,uint256)" - ); - extension_drop.functions[14] = ExtensionFunction( - IERC721Upgradeable.transferFrom.selector, - "transferFrom(address,address,uint256)" - ); - extension_drop.functions[15] = ExtensionFunction(ERC721AUpgradeable.balanceOf.selector, "balanceOf(address)"); - extension_drop.functions[16] = ExtensionFunction( - DelayedReveal.encryptDecrypt.selector, - "encryptDecrypt(bytes,bytes)" - ); - extension_drop.functions[17] = ExtensionFunction( - BurnToClaimDrop721Logic.supportsInterface.selector, - "supportsInterface(bytes4)" - ); - extension_drop.functions[18] = ExtensionFunction(Royalty.royaltyInfo.selector, "royaltyInfo(uint256,uint256)"); - extension_drop.functions[19] = ExtensionFunction( - Royalty.getRoyaltyInfoForToken.selector, - "getRoyaltyInfoForToken(uint256)" - ); - extension_drop.functions[20] = ExtensionFunction( - Royalty.getDefaultRoyaltyInfo.selector, - "getDefaultRoyaltyInfo()" - ); - extension_drop.functions[21] = ExtensionFunction( - Royalty.setDefaultRoyaltyInfo.selector, - "setDefaultRoyaltyInfo(address,uint256)" - ); - extension_drop.functions[22] = ExtensionFunction( - Royalty.setRoyaltyInfoForToken.selector, - "setRoyaltyInfoForToken(uint256,address,uint256)" - ); - extension_drop.functions[23] = ExtensionFunction(IERC721.ownerOf.selector, "ownerOf(uint256)"); - extension_drop.functions[24] = ExtensionFunction(IERC1155.balanceOf.selector, "balanceOf(address,uint256)"); - extension_drop.functions[25] = ExtensionFunction( - BurnToClaim.setBurnToClaimInfo.selector, - "setBurnToClaimInfo((address,uint8,uint256,uint256,address))" - ); - extension_drop.functions[26] = ExtensionFunction( - BurnToClaim.getBurnToClaimInfo.selector, - "getBurnToClaimInfo()" - ); - extension_drop.functions[27] = ExtensionFunction( - BurnToClaim.verifyBurnToClaim.selector, - "verifyBurnToClaim(address,uint256,uint256)" - ); - extension_drop.functions[28] = ExtensionFunction( - BurnToClaimDrop721Logic.burnAndClaim.selector, - "burnAndClaim(uint256,uint256)" - ); - extension_drop.functions[29] = ExtensionFunction( - BurnToClaimDrop721Logic.nextTokenIdToClaim.selector, - "nextTokenIdToClaim()" - ); - extension_drop.functions[30] = ExtensionFunction( - PrimarySale.setPrimarySaleRecipient.selector, - "setPrimarySaleRecipient(address)" - ); - extension_drop.functions[31] = ExtensionFunction( - PlatformFee.setPlatformFeeInfo.selector, - "setPlatformFeeInfo(address,uint256)" - ); - - extensions[1] = extension_drop; - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc. - //////////////////////////////////////////////////////////////*/ - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_revert_nonHolder_renounceRole() public { - address caller = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(caller); - vm.expectRevert( - abi.encodePacked( - "Permissions: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - - Permissions(address(drop)).renounceRole(role, caller); - } - - /** - * note: Tests whether contract reverts when a role admin revokes a role for a non-holder. - */ - function test_revert_revokeRoleForNonHolder() public { - address target = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(deployer); - vm.expectRevert( - abi.encodePacked( - "Permissions: account ", - Strings.toHexString(uint160(target), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - - Permissions(address(drop)).revokeRole(role, target); - } - - /** - * @dev Tests whether contract reverts when a role is granted to an existent role holder. - */ - function test_revert_grant_role_to_account_with_role() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - - Permissions(address(drop)).grantRole(role, receiver); - - vm.expectRevert("Can only grant to non holders"); - Permissions(address(drop)).grantRole(role, receiver); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_grant_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - // check if admin and address(0) have transfer role in the beginning - bool checkAddressZero = Permissions(address(drop)).hasRole(role, address(0)); - bool checkAdmin = Permissions(address(drop)).hasRole(role, deployer); - assertTrue(checkAddressZero); - assertTrue(checkAdmin); - - // check if transfer role can be granted to a non-holder - address receiver = getActor(0); - vm.startPrank(deployer); - Permissions(address(drop)).grantRole(role, receiver); - - // expect revert when granting to a holder - vm.expectRevert("Can only grant to non holders"); - Permissions(address(drop)).grantRole(role, receiver); - - // check if receiver has transfer role - bool checkReceiver = Permissions(address(drop)).hasRole(role, receiver); - assertTrue(checkReceiver); - - // check if role is correctly revoked - Permissions(address(drop)).revokeRole(role, receiver); - checkReceiver = Permissions(address(drop)).hasRole(role, receiver); - assertFalse(checkReceiver); - Permissions(address(drop)).revokeRole(role, address(0)); - checkAddressZero = Permissions(address(drop)).hasRole(role, address(0)); - assertFalse(checkAddressZero); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_getRoleMember_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - uint256 roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - assertEq(roleMemberCount, 2); - - address roleMember = PermissionsEnumerable(address(drop)).getRoleMember(role, 1); - assertEq(roleMember, address(0)); - - vm.startPrank(deployer); - Permissions(address(drop)).grantRole(role, address(2)); - Permissions(address(drop)).grantRole(role, address(3)); - Permissions(address(drop)).grantRole(role, address(4)); - - roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(address(drop)).getRoleMember(role, i)); - } - console.log(""); - - Permissions(address(drop)).revokeRole(role, address(2)); - roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(address(drop)).getRoleMember(role, i)); - } - console.log(""); - - Permissions(address(drop)).revokeRole(role, address(0)); - roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(address(drop)).getRoleMember(role, i)); - } - console.log(""); - - Permissions(address(drop)).grantRole(role, address(5)); - roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(address(drop)).getRoleMember(role, i)); - } - console.log(""); - - Permissions(address(drop)).grantRole(role, address(0)); - roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(address(drop)).getRoleMember(role, i)); - } - console.log(""); - - Permissions(address(drop)).grantRole(role, address(6)); - roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(address(drop)).getRoleMember(role, i)); - } - console.log(""); - } - - /** - * note: Testing transfer of tokens when transfer-role is restricted - */ - function test_claim_transferRole() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - - // revoke transfer role from address(0) - vm.prank(deployer); - Permissions(address(drop)).revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.startPrank(receiver); - vm.expectRevert("!Transfer-Role"); - drop.transferFrom(receiver, address(123), 0); - } - - /** - * @dev Tests whether role member count is incremented correctly. - */ - function test_member_count_incremented_properly_when_role_granted() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - uint256 roleMemberCount = PermissionsEnumerable(address(drop)).getRoleMemberCount(role); - - assertEq(roleMemberCount, 0); - - Permissions(address(drop)).grantRole(role, receiver); - - assertEq(PermissionsEnumerable(address(drop)).getRoleMemberCount(role), 1); - - vm.stopPrank(); - } - - function test_claimCondition_with_startTimestamp() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].startTimestamp = 100; - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.warp(99); - vm.prank(getActor(5), getActor(5)); - vm.expectRevert("!CONDITION."); - drop.claim(receiver, 1, address(0), 0, alp, ""); - - vm.warp(100); - vm.prank(getActor(4), getActor(4)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - Primary sale and Platform fee tests - //////////////////////////////////////////////////////////////*/ - - /// note: Test whether transaction reverts when adding address(0) as primary sale recipient at deploy time - function test_revert_deploy_emptyPrimarySaleRecipient() public { - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address dropImpl = address(new BurnToClaimDropERC721(extensions)); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - vm.expectRevert("Invalid recipient"); - drop = BurnToClaimDrop721Logic( - payable( - address( - new TWProxy( - dropImpl, - abi.encodeCall( - BurnToClaimDropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - address(0), - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ) - ); - } - - /// note: Test whether transaction reverts when adding address(0) as primary sale recipient - function test_revert_emptyPrimarySaleRecipient() public { - vm.prank(deployer); - vm.expectRevert("Invalid recipient"); - drop.setPrimarySaleRecipient(address(0)); - } - - /// note: Test whether transaction reverts when adding address(0) as platform fee recipient at deploy time - function test_revert_deploy_emptyPlatformFeeRecipient() public { - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address dropImpl = address(new BurnToClaimDropERC721(extensions)); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - vm.expectRevert("Invalid recipient"); - drop = BurnToClaimDrop721Logic( - payable( - address( - new TWProxy( - dropImpl, - abi.encodeCall( - BurnToClaimDropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - address(0) - ) - ) - ) - ) - ) - ); - } - - /// note: Test whether transaction reverts when adding address(0) as platform fee recipient - function test_revert_emptyPlatformFeeRecipient() public { - vm.prank(deployer); - vm.expectRevert("Invalid recipient"); - drop.setPlatformFeeInfo(address(0), 100); - } - - /*/////////////////////////////////////////////////////////////// - Lazy Mint Tests - //////////////////////////////////////////////////////////////*/ - - /* - * note: Testing state changes; lazy mint a batch of tokens with no encrypted base URI. - */ - function test_state_lazyMint_noEncryptedURI() public { - uint256 amountToLazyMint = 100; - string memory baseURI = "ipfs://"; - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, emptyEncodedBytes); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.tokenURI(i); - console.log(uri); - assertEq(uri, string(abi.encodePacked(baseURI, i.toString()))); - } - - vm.stopPrank(); - } - - /* - * note: Testing state changes; lazy mint a batch of tokens with encrypted base URI. - */ - function test_state_lazyMint_withEncryptedURI() public { - uint256 amountToLazyMint = 100; - string memory baseURI = "ipfs://"; - bytes memory encryptedBaseURI = "encryptedBaseURI://"; - bytes32 provenanceHash = bytes32("whatever"); - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, abi.encode(encryptedBaseURI, provenanceHash)); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.tokenURI(i); - console.log(uri); - assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - } - - vm.stopPrank(); - } - - /** - * note: Testing revert condition; an address without MINTER_ROLE calls lazyMint function. - */ - function test_revert_lazyMint_MINTER_ROLE() public { - vm.expectRevert("Not authorized"); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - } - - /* - * note: Testing revert condition; calling tokenURI for invalid batch id. - */ - function test_revert_lazyMint_URIForNonLazyMintedToken() public { - vm.startPrank(deployer); - - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.expectRevert("Invalid tokenId"); - drop.tokenURI(100); - - vm.stopPrank(); - } - - /** - * note: Testing event emission; tokens lazy minted. - */ - function test_event_lazyMint_TokensLazyMinted() public { - vm.startPrank(deployer); - - vm.expectEmit(true, false, false, true); - emit TokensLazyMinted(0, 99, "ipfs://", emptyEncodedBytes); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.stopPrank(); - } - - /* - * note: Fuzz testing state changes; lazy mint a batch of tokens with no encrypted base URI. - */ - function test_fuzz_lazyMint_noEncryptedURI(uint256 x) public { - vm.assume(x > 0); - - uint256 amountToLazyMint = x; - string memory baseURI = "ipfs://"; - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, emptyEncodedBytes); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - string memory uri = drop.tokenURI(0); - assertEq(uri, string(abi.encodePacked(baseURI, uint256(0).toString()))); - - uri = drop.tokenURI(x - 1); - assertEq(uri, string(abi.encodePacked(baseURI, uint256(x - 1).toString()))); - - /** - * note: this loop takes too long to run with fuzz tests. - */ - // for(uint256 i = 0; i < amountToLazyMint; i += 1) { - // string memory uri = drop.tokenURI(i); - // console.log(uri); - // assertEq(uri, string(abi.encodePacked(baseURI, i.toString()))); - // } - - vm.stopPrank(); - } - - /* - * note: Fuzz testing state changes; lazy mint a batch of tokens with encrypted base URI. - */ - function test_fuzz_lazyMint_withEncryptedURI(uint256 x) public { - vm.assume(x > 0); - - uint256 amountToLazyMint = x; - string memory baseURI = "ipfs://"; - bytes memory encryptedBaseURI = "encryptedBaseURI://"; - bytes32 provenanceHash = bytes32("whatever"); - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, abi.encode(encryptedBaseURI, provenanceHash)); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - string memory uri = drop.tokenURI(0); - assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - - uri = drop.tokenURI(x - 1); - assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - - /** - * note: this loop takes too long to run with fuzz tests. - */ - // for(uint256 i = 0; i < amountToLazyMint; i += 1) { - // string memory uri = drop.tokenURI(1); - // assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - // } - - vm.stopPrank(); - } - - /* - * note: Fuzz testing; a batch of tokens, and nextTokenIdToMint - */ - function test_fuzz_lazyMint_batchMintAndNextTokenIdToMint(uint256 x) public { - vm.assume(x > 0); - vm.startPrank(deployer); - - if (x == 0) { - vm.expectRevert("Zero amount"); - } - drop.lazyMint(x, "ipfs://", emptyEncodedBytes); - - uint256 slot = stdstore.target(address(drop)).sig("nextTokenIdToMint()").find(); - bytes32 loc = bytes32(slot); - uint256 nextTokenIdToMint = uint256(vm.load(address(drop), loc)); - - assertEq(nextTokenIdToMint, x); - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Delayed Reveal Tests - //////////////////////////////////////////////////////////////*/ - - /* - * note: Testing state changes; URI revealed for a batch of tokens. - */ - function test_state_reveal() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - uint256 amountToLazyMint = 100; - bytes memory secretURI = "ipfs://"; - string memory placeholderURI = "abcd://"; - bytes memory encryptedURI = drop.encryptDecrypt(secretURI, key); - bytes32 provenanceHash = keccak256(abi.encodePacked(secretURI, key, block.chainid)); - - drop.lazyMint(amountToLazyMint, placeholderURI, abi.encode(encryptedURI, provenanceHash)); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.tokenURI(i); - assertEq(uri, string(abi.encodePacked(placeholderURI, "0"))); - } - - string memory revealedURI = drop.reveal(0, key); - assertEq(revealedURI, string(secretURI)); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.tokenURI(i); - assertEq(uri, string(abi.encodePacked(secretURI, i.toString()))); - } - - vm.stopPrank(); - } - - /** - * note: Testing revert condition; an address without MINTER_ROLE calls reveal function. - */ - function test_revert_reveal_MINTER_ROLE() public { - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - vm.prank(deployer); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.prank(deployer); - drop.reveal(0, "key"); - - vm.expectRevert("not minter."); - drop.reveal(0, "key"); - } - - /* - * note: Testing revert condition; trying to reveal URI for non-existent batch. - */ - function test_revert_reveal_revealingNonExistentBatch() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - drop.reveal(0, "key"); - - console.log(drop.getBaseURICount()); - - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - vm.expectRevert("Invalid index"); - drop.reveal(2, "key"); - - vm.stopPrank(); - } - - /* - * note: Testing revert condition; already revealed URI. - */ - function test_revert_delayedReveal_alreadyRevealed() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - drop.reveal(0, "key"); - - vm.expectRevert("Nothing to reveal"); - drop.reveal(0, "key"); - - vm.stopPrank(); - } - - /* - * note: Testing state changes; revealing URI with an incorrect key. - */ - function test_revert_reveal_incorrectKey() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.expectRevert(); - string memory revealedURI = drop.reveal(0, "keyy"); - - vm.stopPrank(); - } - - /** - * note: Testing event emission; TokenURIRevealed. - */ - function test_event_reveal_TokenURIRevealed() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.expectEmit(true, false, false, true); - emit TokenURIRevealed(0, "ipfs://"); - drop.reveal(0, "key"); - - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Claim Tests - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing revert condition; not enough minted tokens. - */ - function test_revert_claimCondition_notEnoughMintedTokens() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 200; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.expectRevert("!Tokens"); - vm.prank(getActor(6), getActor(6)); - drop.claim(receiver, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing revert condition; exceed max claimable supply. - */ - function test_revert_claimCondition_exceedMaxClaimableSupply() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 200; - - vm.prank(deployer); - drop.lazyMint(200, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 100, address(0), 0, alp, ""); - - vm.expectRevert("!MaxSupply"); - vm.prank(getActor(6), getActor(6)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction when no allowlist present. - */ - function test_fuzz_claim_noAllowlist(uint256 x) public { - vm.assume(x != 0); - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - bytes memory errorQty = "!Qty"; - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(errorQty); - drop.claim(receiver, 0, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(errorQty); - drop.claim(receiver, 101, address(0), 0, alp, ""); - - vm.prank(deployer); - drop.setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(errorQty); - drop.claim(receiver, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to 0 - */ - function test_state_claim_allowlisted_SetQuantityZeroPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 100, address(erc20), 0, alp, ""); // claims for free, because allowlist price is 0 - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 100); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to non-zero value - */ - function test_state_claim_allowlisted_SetQuantityPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "5"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 5; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(receiver, receiver); - vm.expectRevert("!PriceOrCurrency"); - drop.claim(receiver, 100, address(erc20), 0, alp, ""); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 100, address(erc20), 5, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 100); - assertEq(erc20.balanceOf(receiver), 10000 - 500); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price not set; should default to general price and currency - */ - function test_state_claim_allowlisted_SetQuantityDefaultPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = Strings.toString(type(uint256).max); // this implies that general price is applicable - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = type(uint256).max; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 100, address(erc20), 10, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 100); - assertEq(erc20.balanceOf(receiver), 10000 - 1000); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to 0 => should default to general limit - * - allowlist price set to some value different than general price - */ - function test_state_claim_allowlisted_DefaultQuantitySomePrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "0"; // this implies that general limit is applicable - inputs[3] = "5"; - inputs[4] = "0x0000000000000000000000000000000000000000"; // general currency will be applicable - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 0; - alp.pricePerToken = 5; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - bytes memory errorQty = "!Qty"; - vm.prank(receiver, receiver); - vm.expectRevert(errorQty); - drop.claim(receiver, 100, address(erc20), 5, alp, ""); // trying to claim more than general limit - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 10, address(erc20), 5, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 10); - assertEq(erc20.balanceOf(receiver), 10000 - 50); - } - - function test_fuzz_claim_merkleProof(uint256 x) public { - vm.assume(x > 10 && x < 500); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(x); - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - alp.pricePerToken = 0; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - // bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = x; - conditions[0].quantityLimitPerWallet = 1; - conditions[0].merkleRoot = root; - - vm.prank(deployer); - drop.lazyMint(2 * x, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, x - 5, address(0), 0, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), x - 5); - - bytes memory errorQty = "!Qty"; - - vm.prank(receiver, receiver); - vm.expectRevert(errorQty); - drop.claim(receiver, 6, address(0), 0, alp, ""); - - vm.prank(receiver, receiver); - drop.claim(receiver, 5, address(0), 0, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), x); - - vm.prank(receiver, receiver); - vm.expectRevert(errorQty); - drop.claim(receiver, 5, address(0), 0, alp, ""); // quantity limit already claimed - } - - /** - * note: Testing state changes; reset eligibility of claim conditions and claiming again for same condition id. - */ - function test_state_claimCondition_resetEligibility() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - BurnToClaimDrop721Logic.AllowlistProof memory alp; - alp.proof = proofs; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 100, address(0), 0, alp, ""); - - bytes memory errorQty = "!Qty"; - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(errorQty); - drop.claim(receiver, 100, address(0), 0, alp, ""); - - vm.prank(deployer); - drop.setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 100, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - setClaimConditions - //////////////////////////////////////////////////////////////*/ - - function test_claimCondition_startIdAndCount() public { - vm.startPrank(deployer); - - uint256 currentStartId = 0; - uint256 count = 0; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](2); - conditions[0].startTimestamp = 0; - conditions[0].maxClaimableSupply = 10; - conditions[1].startTimestamp = 1; - conditions[1].maxClaimableSupply = 10; - - drop.setClaimConditions(conditions, false); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - drop.setClaimConditions(conditions, false); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - drop.setClaimConditions(conditions, true); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 2); - assertEq(count, 2); - - drop.setClaimConditions(conditions, true); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 4); - assertEq(count, 2); - } - - function test_claimCondition_startPhase() public { - vm.startPrank(deployer); - - uint256 activeConditionId = 0; - - BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](3); - conditions[0].startTimestamp = 10; - conditions[0].maxClaimableSupply = 11; - conditions[0].quantityLimitPerWallet = 12; - conditions[1].startTimestamp = 20; - conditions[1].maxClaimableSupply = 21; - conditions[1].quantityLimitPerWallet = 22; - conditions[2].startTimestamp = 30; - conditions[2].maxClaimableSupply = 31; - conditions[2].quantityLimitPerWallet = 32; - drop.setClaimConditions(conditions, false); - - vm.expectRevert("!CONDITION."); - drop.getActiveClaimConditionId(); - - vm.warp(10); - activeConditionId = drop.getActiveClaimConditionId(); - assertEq(activeConditionId, 0); - assertEq(drop.getClaimConditionById(activeConditionId).startTimestamp, 10); - assertEq(drop.getClaimConditionById(activeConditionId).maxClaimableSupply, 11); - assertEq(drop.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 12); - - vm.warp(20); - activeConditionId = drop.getActiveClaimConditionId(); - assertEq(activeConditionId, 1); - assertEq(drop.getClaimConditionById(activeConditionId).startTimestamp, 20); - assertEq(drop.getClaimConditionById(activeConditionId).maxClaimableSupply, 21); - assertEq(drop.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 22); - - vm.warp(30); - activeConditionId = drop.getActiveClaimConditionId(); - assertEq(activeConditionId, 2); - assertEq(drop.getClaimConditionById(activeConditionId).startTimestamp, 30); - assertEq(drop.getClaimConditionById(activeConditionId).maxClaimableSupply, 31); - assertEq(drop.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 32); - - vm.warp(40); - assertEq(drop.getActiveClaimConditionId(), 2); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_delayedReveal_withNewLazyMintedEmptyBatch() public { - vm.startPrank(deployer); - - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", "key"); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", "key", block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - drop.reveal(0, "key"); - - string memory uri = drop.tokenURI(1); - assertEq(uri, string(abi.encodePacked("ipfs://", "1"))); - - bytes memory newEncryptedURI = drop.encryptDecrypt("ipfs://secret", "key"); - vm.expectRevert("0 amt"); - drop.lazyMint(0, "", abi.encode(newEncryptedURI, provenanceHash)); - - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Burn To Claim - //////////////////////////////////////////////////////////////*/ - - function test_state_burnAndClaim_1155Origin_zeroMintPrice() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc1155); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC1155; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 0; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // check details correctly saved - BurnToClaimDrop721Logic.BurnToClaimInfo memory savedInfo = drop.getBurnToClaimInfo(); - assertEq(savedInfo.originContractAddress, burnToClaimInfo.originContractAddress); - assertTrue(savedInfo.tokenType == burnToClaimInfo.tokenType); - assertEq(savedInfo.tokenId, burnToClaimInfo.tokenId); - assertEq(savedInfo.mintPriceForNewToken, burnToClaimInfo.mintPriceForNewToken); - assertEq(savedInfo.currency, burnToClaimInfo.currency); - - // mint some erc1155 to a claimer - address claimer = getActor(0); - erc1155.mint(claimer, 0, 10); - assertEq(erc1155.balanceOf(claimer, 0), 10); - vm.prank(claimer); - erc1155.setApprovalForAll(address(drop), true); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - drop.burnAndClaim(0, 10); - - // check state - assertEq(erc1155.balanceOf(claimer, 0), 0); - assertEq(drop.balanceOf(claimer), 10); - assertEq(drop.nextTokenIdToClaim(), 10); - } - - function test_state_burnAndClaim_1155Origin_nonZeroMintPrice() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc1155); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC1155; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 1; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // check details correctly saved - BurnToClaimDrop721Logic.BurnToClaimInfo memory savedInfo = drop.getBurnToClaimInfo(); - assertEq(savedInfo.originContractAddress, burnToClaimInfo.originContractAddress); - assertTrue(savedInfo.tokenType == burnToClaimInfo.tokenType); - assertEq(savedInfo.tokenId, burnToClaimInfo.tokenId); - assertEq(savedInfo.mintPriceForNewToken, burnToClaimInfo.mintPriceForNewToken); - assertEq(savedInfo.currency, burnToClaimInfo.currency); - - // mint some erc1155 to a claimer - address claimer = getActor(0); - erc1155.mint(claimer, 0, 10); - assertEq(erc1155.balanceOf(claimer, 0), 10); - vm.prank(claimer); - erc1155.setApprovalForAll(address(drop), true); - - // mint erc20 to claimer, to pay claim price - erc20.mint(claimer, 100); - vm.prank(claimer); - erc20.approve(address(drop), type(uint256).max); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - drop.burnAndClaim(0, 10); - - // check state - assertEq(erc1155.balanceOf(claimer, 0), 0); - assertEq(erc20.balanceOf(claimer), 90); - assertEq(erc20.balanceOf(saleRecipient), 10); - assertEq(drop.balanceOf(claimer), 10); - assertEq(drop.nextTokenIdToClaim(), 10); - } - - function test_state_burnAndClaim_1155Origin_nonZeroMintPrice_nativeToken() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc1155); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC1155; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 1; - burnToClaimInfo.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // check details correctly saved - BurnToClaimDrop721Logic.BurnToClaimInfo memory savedInfo = drop.getBurnToClaimInfo(); - assertEq(savedInfo.originContractAddress, burnToClaimInfo.originContractAddress); - assertTrue(savedInfo.tokenType == burnToClaimInfo.tokenType); - assertEq(savedInfo.tokenId, burnToClaimInfo.tokenId); - assertEq(savedInfo.mintPriceForNewToken, burnToClaimInfo.mintPriceForNewToken); - assertEq(savedInfo.currency, burnToClaimInfo.currency); - - // mint some erc1155 to a claimer - address claimer = getActor(0); - erc1155.mint(claimer, 0, 10); - assertEq(erc1155.balanceOf(claimer, 0), 10); - vm.prank(claimer); - erc1155.setApprovalForAll(address(drop), true); - - // deal ether to claimer, to pay claim price - vm.deal(claimer, 100); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - drop.burnAndClaim{ value: 10 }(0, 10); - - // check state - assertEq(erc1155.balanceOf(claimer, 0), 0); - assertEq(claimer.balance, 90); - assertEq(saleRecipient.balance, 10); - assertEq(drop.balanceOf(claimer), 10); - assertEq(drop.nextTokenIdToClaim(), 10); - } - - function test_state_burnAndClaim_721Origin_zeroMintPrice() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc721); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC721; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 0; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // check details correctly saved - BurnToClaimDrop721Logic.BurnToClaimInfo memory savedInfo = drop.getBurnToClaimInfo(); - assertEq(savedInfo.originContractAddress, burnToClaimInfo.originContractAddress); - assertTrue(savedInfo.tokenType == burnToClaimInfo.tokenType); - assertEq(savedInfo.tokenId, burnToClaimInfo.tokenId); - assertEq(savedInfo.mintPriceForNewToken, burnToClaimInfo.mintPriceForNewToken); - assertEq(savedInfo.currency, burnToClaimInfo.currency); - - // mint some erc721 to a claimer - address claimer = getActor(0); - erc721.mint(claimer, 10); - assertEq(erc721.balanceOf(claimer), 10); - vm.prank(claimer); - erc721.setApprovalForAll(address(drop), true); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - drop.burnAndClaim(0, 1); - - // check state - assertEq(erc721.balanceOf(claimer), 9); - assertEq(drop.balanceOf(claimer), 1); - assertEq(drop.nextTokenIdToClaim(), 1); - - vm.expectRevert("ERC721: invalid token ID"); // because the token doesn't exist anymore - erc721.ownerOf(0); - } - - function test_state_burnAndClaim_721Origin_nonZeroMintPrice() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc721); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC721; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 1; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // check details correctly saved - BurnToClaimDrop721Logic.BurnToClaimInfo memory savedInfo = drop.getBurnToClaimInfo(); - assertEq(savedInfo.originContractAddress, burnToClaimInfo.originContractAddress); - assertTrue(savedInfo.tokenType == burnToClaimInfo.tokenType); - assertEq(savedInfo.tokenId, burnToClaimInfo.tokenId); - assertEq(savedInfo.mintPriceForNewToken, burnToClaimInfo.mintPriceForNewToken); - assertEq(savedInfo.currency, burnToClaimInfo.currency); - - // mint some erc721 to a claimer - address claimer = getActor(0); - erc721.mint(claimer, 10); - assertEq(erc721.balanceOf(claimer), 10); - vm.prank(claimer); - erc721.setApprovalForAll(address(drop), true); - - // mint erc20 to claimer, to pay claim price - erc20.mint(claimer, 100); - vm.prank(claimer); - erc20.approve(address(drop), type(uint256).max); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - drop.burnAndClaim(0, 1); - - // check state - assertEq(erc721.balanceOf(claimer), 9); - assertEq(drop.balanceOf(claimer), 1); - assertEq(drop.nextTokenIdToClaim(), 1); - assertEq(erc20.balanceOf(claimer), 99); - assertEq(erc20.balanceOf(saleRecipient), 1); - - vm.expectRevert("ERC721: invalid token ID"); // because the token doesn't exist anymore - erc721.ownerOf(0); - } - - function test_state_burnAndClaim_721Origin_nonZeroMintPrice_nativeToken() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc721); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC721; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 1; - burnToClaimInfo.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // check details correctly saved - BurnToClaimDrop721Logic.BurnToClaimInfo memory savedInfo = drop.getBurnToClaimInfo(); - assertEq(savedInfo.originContractAddress, burnToClaimInfo.originContractAddress); - assertTrue(savedInfo.tokenType == burnToClaimInfo.tokenType); - assertEq(savedInfo.tokenId, burnToClaimInfo.tokenId); - assertEq(savedInfo.mintPriceForNewToken, burnToClaimInfo.mintPriceForNewToken); - assertEq(savedInfo.currency, burnToClaimInfo.currency); - - // mint some erc721 to a claimer - address claimer = getActor(0); - erc721.mint(claimer, 10); - assertEq(erc721.balanceOf(claimer), 10); - vm.prank(claimer); - erc721.setApprovalForAll(address(drop), true); - - // deal ether to claimer, to pay claim price - vm.deal(claimer, 100); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - drop.burnAndClaim{ value: 1 }(0, 1); - - // check state - assertEq(erc721.balanceOf(claimer), 9); - assertEq(drop.balanceOf(claimer), 1); - assertEq(drop.nextTokenIdToClaim(), 1); - assertEq(claimer.balance, 99); - assertEq(saleRecipient.balance, 1); - - vm.expectRevert("ERC721: invalid token ID"); // because the token doesn't exist anymore - erc721.ownerOf(0); - } - - function test_revert_burnAndClaim_originNotSet() public { - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.expectRevert(); - drop.burnAndClaim(0, 1); - } - - function test_revert_burnAndClaim_noLazyMintedTokens() public { - // burn and claim - vm.expectRevert("!Tokens"); - drop.burnAndClaim(0, 1); - } - - function test_revert_burnAndClaim_invalidTokenId() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc1155); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC1155; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 0; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // mint some erc1155 to a claimer - address claimer = getActor(0); - erc1155.mint(claimer, 0, 10); - assertEq(erc1155.balanceOf(claimer, 0), 10); - vm.prank(claimer); - erc1155.setApprovalForAll(address(drop), true); - - // burn and claim - vm.prank(claimer); - vm.expectRevert("Invalid token Id"); - drop.burnAndClaim(1, 1); - } - - function test_revert_burnAndClaim_notEnoughBalance() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc1155); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC1155; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 0; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // mint some erc1155 to a claimer - address claimer = getActor(0); - erc1155.mint(claimer, 0, 10); - assertEq(erc1155.balanceOf(claimer, 0), 10); - vm.prank(claimer); - erc1155.setApprovalForAll(address(drop), true); - - // burn and claim - vm.prank(claimer); - vm.expectRevert("!Balance"); - drop.burnAndClaim(0, 11); - } - - function test_revert_burnAndClaim_notOwnerOfToken() public { - IBurnToClaim.BurnToClaimInfo memory burnToClaimInfo; - - burnToClaimInfo.originContractAddress = address(erc721); - burnToClaimInfo.tokenType = IBurnToClaim.TokenType.ERC721; - burnToClaimInfo.tokenId = 0; - burnToClaimInfo.mintPriceForNewToken = 1; - burnToClaimInfo.currency = address(erc20); - - // set origin contract details for burn and claim - vm.prank(deployer); - drop.setBurnToClaimInfo(burnToClaimInfo); - - // mint some erc721 to a claimer - address claimer = getActor(0); - erc721.mint(claimer, 10); - assertEq(erc721.balanceOf(claimer), 10); - vm.prank(claimer); - erc721.setApprovalForAll(address(drop), true); - - // mint erc721 to another address - erc721.mint(address(0x567), 5); - - // lazy mint tokens - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - // burn and claim - vm.prank(claimer); - vm.expectRevert("!Owner"); - drop.burnAndClaim(11, 1); - } - - /*/////////////////////////////////////////////////////////////// - Extension Role and Upgradeability - //////////////////////////////////////////////////////////////*/ - - // function test_addExtension() public { - // address permissionsNew = address(new PermissionsEnumerableImpl()); - - // Extension memory extension_permissions_new; - // extension_permissions_new.metadata = ExtensionMetadata({ - // name: "PermissionsNew", - // metadataURI: "ipfs://PermissionsNew", - // implementation: permissionsNew - // }); - - // extension_permissions_new.functions = new ExtensionFunction[](4); - // extension_permissions_new.functions[0] = ExtensionFunction( - // Permissions.hasRole.selector, - // "hasRole(bytes32,address)" - // ); - // extension_permissions_new.functions[1] = ExtensionFunction( - // Permissions.hasRoleWithSwitch.selector, - // "hasRoleWithSwitch(bytes32,address)" - // ); - // extension_permissions_new.functions[2] = ExtensionFunction( - // Permissions.grantRole.selector, - // "grantRole(bytes32,address)" - // ); - // extension_permissions_new.functions[3] = ExtensionFunction( - // PermissionsEnumerable.getRoleMemberCount.selector, - // "getRoleMemberCount(bytes32)" - // ); - - // // cast drop to router type - // BurnToClaimDropERC721 dropRouter = BurnToClaimDropERC721(payable(address(drop))); - - // vm.prank(deployer); - // dropRouter.addExtension(extension_permissions_new); - - // // assertEq( - // // dropRouter.getExtensionForFunction(PermissionsEnumerable.getRoleMemberCount.selector).name, - // // "PermissionsNew" - // // ); - - // // assertEq( - // // dropRouter.getExtensionForFunction(PermissionsEnumerable.getRoleMemberCount.selector).implementation, - // // permissionsNew - // // ); - // } - - function test_revert_addExtension_NotAuthorized() public { - Extension memory extension_permissions_new; - - // cast drop to router type - BurnToClaimDropERC721 dropRouter = BurnToClaimDropERC721(payable(address(drop))); - - vm.prank(address(0x123)); - vm.expectRevert("ExtensionManager: unauthorized."); - dropRouter.addExtension(extension_permissions_new); - } - - function test_revert_addExtension_deployerRenounceExtensionRole() public { - Extension memory extension_permissions_new; - - // cast drop to router type - BurnToClaimDropERC721 dropRouter = BurnToClaimDropERC721(payable(address(drop))); - - vm.prank(deployer); - Permissions(address(drop)).renounceRole(keccak256("EXTENSION_ROLE"), deployer); - - vm.prank(deployer); - vm.expectRevert("ExtensionManager: unauthorized."); - dropRouter.addExtension(extension_permissions_new); - - vm.startPrank(deployer); - vm.expectRevert( - abi.encodePacked( - "Permissions: account ", - Strings.toHexString(uint160(deployer), 20), - " is missing role ", - Strings.toHexString(uint256(keccak256("EXTENSION_ROLE")), 32) - ) - ); - Permissions(address(drop)).grantRole(keccak256("EXTENSION_ROLE"), address(0x12345)); - vm.stopPrank(); - } -} diff --git a/src/test/drop/DropERC1155.t.sol b/src/test/drop/DropERC1155.t.sol deleted file mode 100644 index 840f78cf6..000000000 --- a/src/test/drop/DropERC1155.t.sol +++ /dev/null @@ -1,956 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155, BatchMintMetadata, Drop1155, LazyMint, IPermissions, ILazyMint } from "contracts/prebuilts/drop/DropERC1155.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract DropERC1155Test is BaseTest { - using Strings for uint256; - using Strings for address; - - event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI); - event TokenURIRevealed(uint256 indexed index, string revealedURI); - event MaxTotalSupplyUpdated(uint256 tokenId, uint256 maxTotalSupply); - - DropERC1155 public drop; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - drop = DropERC1155(getContract("DropERC1155")); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc. - //////////////////////////////////////////////////////////////*/ - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_revert_nonHolder_renounceRole() public { - address caller = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(caller); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, caller, role)); - - drop.renounceRole(role, caller); - } - - /** - * note: Tests whether contract reverts when a role admin revokes a role for a non-holder. - */ - function test_revert_revokeRoleForNonHolder() public { - address target = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(deployer); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, target, role)); - - drop.revokeRole(role, target); - } - - /** - * @dev Tests whether contract reverts when a role is granted to an existent role holder. - */ - function test_revert_grant_role_to_account_with_role() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - - drop.grantRole(role, receiver); - - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsAlreadyGranted.selector, receiver, role)); - drop.grantRole(role, receiver); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_grant_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - // check if admin and address(0) have transfer role in the beginning - bool checkAddressZero = drop.hasRole(role, address(0)); - bool checkAdmin = drop.hasRole(role, deployer); - assertTrue(checkAddressZero); - assertTrue(checkAdmin); - - // check if transfer role can be granted to a non-holder - address receiver = getActor(0); - vm.startPrank(deployer); - drop.grantRole(role, receiver); - - // expect revert when granting to a holder - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsAlreadyGranted.selector, receiver, role)); - drop.grantRole(role, receiver); - - // check if receiver has transfer role - bool checkReceiver = drop.hasRole(role, receiver); - assertTrue(checkReceiver); - - // check if role is correctly revoked - drop.revokeRole(role, receiver); - checkReceiver = drop.hasRole(role, receiver); - assertFalse(checkReceiver); - drop.revokeRole(role, address(0)); - checkAddressZero = drop.hasRole(role, address(0)); - assertFalse(checkAddressZero); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_getRoleMember_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - uint256 roleMemberCount = drop.getRoleMemberCount(role); - assertEq(roleMemberCount, 2); - - address roleMember = drop.getRoleMember(role, 1); - assertEq(roleMember, address(0)); - - vm.startPrank(deployer); - drop.grantRole(role, address(2)); - drop.grantRole(role, address(3)); - drop.grantRole(role, address(4)); - - roleMemberCount = drop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.revokeRole(role, address(2)); - roleMemberCount = drop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.revokeRole(role, address(0)); - roleMemberCount = drop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.grantRole(role, address(5)); - roleMemberCount = drop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.grantRole(role, address(0)); - roleMemberCount = drop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.grantRole(role, address(6)); - roleMemberCount = drop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - } - - /** - * note: Testing transfer of tokens when transfer-role is restricted - */ - function test_claim_transferRole() public { - vm.warp(1); - - uint256 _tokenId = 0; - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, _tokenId, 1, address(0), 0, alp, ""); - - // revoke transfer role from address(0) - vm.prank(deployer); - drop.revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.startPrank(receiver); - vm.expectRevert("restricted to TRANSFER_ROLE holders."); - drop.safeTransferFrom(receiver, address(123), 0, 0, ""); - } - - /** - * @dev Tests whether role member count is incremented correctly. - */ - function test_member_count_incremented_properly_when_role_granted() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - uint256 roleMemberCount = drop.getRoleMemberCount(role); - - assertEq(roleMemberCount, 0); - - drop.grantRole(role, receiver); - - assertEq(drop.getRoleMemberCount(role), 1); - - vm.stopPrank(); - } - - function test_claimCondition_with_startTimestamp() public { - vm.warp(1); - - uint256 _tokenId = 0; - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].startTimestamp = 100; - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - vm.warp(99); - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(abi.encodeWithSelector(Drop1155.DropNoActiveCondition.selector)); - drop.claim(receiver, _tokenId, 1, address(0), 0, alp, ""); - - vm.warp(100); - vm.prank(getActor(4), getActor(4)); - drop.claim(receiver, _tokenId, 1, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - Lazy Mint Tests - //////////////////////////////////////////////////////////////*/ - - /* - * note: Testing state changes; lazy mint a batch of tokens with no encrypted base URI. - */ - function test_state_lazyMint_noEncryptedURI() public { - uint256 amountToLazyMint = 100; - string memory baseURI = "ipfs://"; - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, emptyEncodedBytes); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.uri(i); - console.log(uri); - assertEq(uri, string(abi.encodePacked(baseURI, i.toString()))); - } - - vm.stopPrank(); - } - - /** - * note: Testing revert condition; an address without MINTER_ROLE calls lazyMint function. - */ - function test_revert_lazyMint_MINTER_ROLE() public { - vm.expectRevert(abi.encodeWithSelector(LazyMint.LazyMintUnauthorized.selector)); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - } - - /* - * note: Testing revert condition; calling tokenURI for invalid batch id. - */ - function test_revert_lazyMint_URIForNonLazyMintedToken() public { - vm.startPrank(deployer); - - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidTokenId.selector, 100)); - drop.uri(100); - - vm.stopPrank(); - } - - /** - * note: Testing event emission; tokens lazy minted. - */ - function test_event_lazyMint_TokensLazyMinted() public { - vm.startPrank(deployer); - - vm.expectEmit(true, false, false, true); - emit TokensLazyMinted(0, 99, "ipfs://", emptyEncodedBytes); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.stopPrank(); - } - - /* - * note: Fuzz testing state changes; lazy mint a batch of tokens with no encrypted base URI. - */ - function test_fuzz_lazyMint_noEncryptedURI(uint256 x) public { - vm.assume(x > 0); - - uint256 amountToLazyMint = x; - string memory baseURI = "ipfs://"; - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, emptyEncodedBytes); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - string memory uri = drop.uri(0); - assertEq(uri, string(abi.encodePacked(baseURI, uint256(0).toString()))); - - uri = drop.uri(x - 1); - assertEq(uri, string(abi.encodePacked(baseURI, uint256(x - 1).toString()))); - - /** - * note: this loop takes too long to run with fuzz tests. - */ - // for(uint256 i = 0; i < amountToLazyMint; i += 1) { - // string memory uri = drop.uri(i); - // console.log(uri); - // assertEq(uri, string(abi.encodePacked(baseURI, i.toString()))); - // } - - vm.stopPrank(); - } - - /* - * note: Fuzz testing; a batch of tokens, and nextTokenIdToMint - */ - function test_fuzz_lazyMint_batchMintAndNextTokenIdToMint(uint256 x) public { - vm.assume(x > 0); - vm.startPrank(deployer); - - if (x == 0) { - vm.expectRevert("Zero amount"); - } - drop.lazyMint(x, "ipfs://", emptyEncodedBytes); - - uint256 slot = stdstore.target(address(drop)).sig("nextTokenIdToMint()").find(); - bytes32 loc = bytes32(slot); - uint256 nextTokenIdToMint = uint256(vm.load(address(drop), loc)); - - assertEq(nextTokenIdToMint, x); - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Claim Tests - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing revert condition; not enough minted tokens. - */ - function test_revert_claimCondition_notEnoughMintedTokens() public { - vm.warp(1); - - uint256 _tokenId = 0; - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 200; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - vm.expectRevert(abi.encodeWithSelector(Drop1155.DropNoActiveCondition.selector)); - vm.prank(getActor(6), getActor(6)); - drop.claim(receiver, 100, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing revert condition; exceed max claimable supply. - */ - function test_revert_claimCondition_exceedMaxClaimableSupply() public { - vm.warp(1); - - uint256 _tokenId = 0; - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 200; - - vm.prank(deployer); - drop.lazyMint(200, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, _tokenId, 100, address(0), 0, alp, ""); - - vm.expectRevert(abi.encodeWithSelector(Drop1155.DropClaimExceedMaxSupply.selector, 100, 101)); - vm.prank(getActor(6), getActor(6)); - drop.claim(receiver, _tokenId, 1, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction when no allowlist present. - */ - function test_fuzz_claim_noAllowlist(uint256 x) public { - vm.assume(x != 0); - vm.warp(1); - - uint256 _tokenId = 0; - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(abi.encodeWithSelector(Drop1155.DropClaimExceedLimit.selector, 100, 0)); - drop.claim(receiver, _tokenId, 0, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(abi.encodeWithSelector(Drop1155.DropClaimExceedLimit.selector, 100, 101)); - drop.claim(receiver, _tokenId, 101, address(0), 0, alp, ""); - - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, true); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(abi.encodeWithSelector(Drop1155.DropClaimExceedLimit.selector, 100, 101)); - drop.claim(receiver, _tokenId, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to 0 - */ - function test_state_claim_allowlisted_SetQuantityZeroPrice() public { - uint256 _tokenId = 0; - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, _tokenId, 100, address(erc20), 0, alp, ""); // claims for free, because allowlist price is 0 - assertEq(drop.getSupplyClaimedByWallet(_tokenId, drop.getActiveClaimConditionId(_tokenId), receiver), 100); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to non-zero value - */ - function test_state_claim_allowlisted_SetQuantityPrice() public { - uint256 _tokenId = 0; - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "5"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 5; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - vm.prank(receiver, receiver); - vm.expectRevert( - abi.encodeWithSelector(Drop1155.DropClaimInvalidTokenPrice.selector, address(erc20), 0, address(erc20), 5) - ); - drop.claim(receiver, _tokenId, 100, address(erc20), 0, alp, ""); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, _tokenId, 100, address(erc20), 5, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(_tokenId, drop.getActiveClaimConditionId(_tokenId), receiver), 100); - assertEq(erc20.balanceOf(receiver), 10000 - 500); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price not set; should default to general price and currency - */ - function test_state_claim_allowlisted_SetQuantityDefaultPrice() public { - uint256 _tokenId = 0; - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = Strings.toString(type(uint256).max); // this implies that general price is applicable - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = type(uint256).max; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, _tokenId, 100, address(erc20), 10, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(_tokenId, drop.getActiveClaimConditionId(_tokenId), receiver), 100); - assertEq(erc20.balanceOf(receiver), 10000 - 1000); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to 0 => should default to general limit - * - allowlist price set to some value different than general price - */ - function test_state_claim_allowlisted_DefaultQuantitySomePrice() public { - uint256 _tokenId = 0; - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "0"; // this implies that general limit is applicable - inputs[3] = "5"; - inputs[4] = "0x0000000000000000000000000000000000000000"; // general currency will be applicable - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 0; - alp.pricePerToken = 5; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(Drop1155.DropClaimExceedLimit.selector, 10, 100)); - drop.claim(receiver, _tokenId, 100, address(erc20), 5, alp, ""); // trying to claim more than general limit - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, _tokenId, 10, address(erc20), 5, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(_tokenId, drop.getActiveClaimConditionId(_tokenId), receiver), 10); - assertEq(erc20.balanceOf(receiver), 10000 - 50); - } - - function test_fuzz_claim_merkleProof(uint256 x) public { - vm.assume(x > 10 && x < 500); - uint256 _tokenId = 0; - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(x); - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - alp.pricePerToken = 0; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - // bytes32[] memory proofs = new bytes32[](0); - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = x; - conditions[0].quantityLimitPerWallet = 1; - conditions[0].merkleRoot = root; - - vm.prank(deployer); - drop.lazyMint(2 * x, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, _tokenId, x - 5, address(0), 0, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(_tokenId, drop.getActiveClaimConditionId(_tokenId), receiver), x - 5); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(Drop1155.DropClaimExceedLimit.selector, x, x + 1)); - drop.claim(receiver, _tokenId, 6, address(0), 0, alp, ""); - - vm.prank(receiver, receiver); - drop.claim(receiver, _tokenId, 5, address(0), 0, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(_tokenId, drop.getActiveClaimConditionId(_tokenId), receiver), x); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(Drop1155.DropClaimExceedLimit.selector, x, x + 5)); - drop.claim(receiver, _tokenId, 5, address(0), 0, alp, ""); // quantity limit already claimed - } - - /** - * note: Testing state changes; reset eligibility of claim conditions and claiming again for same condition id. - */ - function test_state_claimCondition_resetEligibility() public { - vm.warp(1); - - uint256 _tokenId = 0; - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, _tokenId, 100, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(abi.encodeWithSelector(Drop1155.DropClaimExceedLimit.selector, 100, 200)); - drop.claim(receiver, _tokenId, 100, address(0), 0, alp, ""); - - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, true); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, _tokenId, 100, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - setClaimConditions - //////////////////////////////////////////////////////////////*/ - - function test_claimCondition_startIdAndCount() public { - vm.startPrank(deployer); - - uint256 _tokenId = 0; - uint256 currentStartId = 0; - uint256 count = 0; - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](2); - conditions[0].startTimestamp = 0; - conditions[0].maxClaimableSupply = 10; - conditions[1].startTimestamp = 1; - conditions[1].maxClaimableSupply = 10; - - drop.setClaimConditions(_tokenId, conditions, false); - (currentStartId, count) = drop.claimCondition(_tokenId); - assertEq(currentStartId, 0); - assertEq(count, 2); - - drop.setClaimConditions(_tokenId, conditions, false); - (currentStartId, count) = drop.claimCondition(_tokenId); - assertEq(currentStartId, 0); - assertEq(count, 2); - - drop.setClaimConditions(_tokenId, conditions, true); - (currentStartId, count) = drop.claimCondition(_tokenId); - assertEq(currentStartId, 2); - assertEq(count, 2); - - drop.setClaimConditions(_tokenId, conditions, true); - (currentStartId, count) = drop.claimCondition(_tokenId); - assertEq(currentStartId, 4); - assertEq(count, 2); - } - - function test_claimCondition_startPhase() public { - vm.startPrank(deployer); - - uint256 _tokenId = 0; - uint256 activeConditionId = 0; - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](3); - conditions[0].startTimestamp = 10; - conditions[0].maxClaimableSupply = 11; - conditions[0].quantityLimitPerWallet = 12; - conditions[1].startTimestamp = 20; - conditions[1].maxClaimableSupply = 21; - conditions[1].quantityLimitPerWallet = 22; - conditions[2].startTimestamp = 30; - conditions[2].maxClaimableSupply = 31; - conditions[2].quantityLimitPerWallet = 32; - drop.setClaimConditions(_tokenId, conditions, false); - - vm.expectRevert(abi.encodeWithSelector(Drop1155.DropNoActiveCondition.selector)); - drop.getActiveClaimConditionId(_tokenId); - - vm.warp(10); - activeConditionId = drop.getActiveClaimConditionId(_tokenId); - assertEq(activeConditionId, 0); - assertEq(drop.getClaimConditionById(_tokenId, activeConditionId).startTimestamp, 10); - assertEq(drop.getClaimConditionById(_tokenId, activeConditionId).maxClaimableSupply, 11); - assertEq(drop.getClaimConditionById(_tokenId, activeConditionId).quantityLimitPerWallet, 12); - - vm.warp(20); - activeConditionId = drop.getActiveClaimConditionId(_tokenId); - assertEq(activeConditionId, 1); - assertEq(drop.getClaimConditionById(_tokenId, activeConditionId).startTimestamp, 20); - assertEq(drop.getClaimConditionById(_tokenId, activeConditionId).maxClaimableSupply, 21); - assertEq(drop.getClaimConditionById(_tokenId, activeConditionId).quantityLimitPerWallet, 22); - - vm.warp(30); - activeConditionId = drop.getActiveClaimConditionId(_tokenId); - assertEq(activeConditionId, 2); - assertEq(drop.getClaimConditionById(_tokenId, activeConditionId).startTimestamp, 30); - assertEq(drop.getClaimConditionById(_tokenId, activeConditionId).maxClaimableSupply, 31); - assertEq(drop.getClaimConditionById(_tokenId, activeConditionId).quantityLimitPerWallet, 32); - - vm.warp(40); - assertEq(drop.getActiveClaimConditionId(_tokenId), 2); - } - - /*/////////////////////////////////////////////////////////////// - Unit Test: updateBatchBaseURI - //////////////////////////////////////////////////////////////*/ - - function test_state_updateBatchBaseURI() public { - string memory initURI = "ipfs://init"; - string memory newURI = "ipfs://new"; - - vm.startPrank(deployer); - drop.lazyMint(100, initURI, ""); - - string memory initTokenURI = drop.uri(0); - - assertEq(initTokenURI, string(abi.encodePacked(initURI, "0"))); - - drop.updateBatchBaseURI(0, newURI); - - string memory newTokenURI = drop.uri(0); - - assertEq(newTokenURI, string(abi.encodePacked(newURI, "0"))); - } - - /*/////////////////////////////////////////////////////////////// - Unit Test: freezeBatchBaseURI - //////////////////////////////////////////////////////////////*/ - - function test_state_freezeBatchBaseURI() public { - string memory initURI = "ipfs://init"; - - vm.startPrank(deployer); - drop.lazyMint(100, initURI, ""); - - string memory initTokenURI = drop.uri(0); - - assertEq(initTokenURI, string(abi.encodePacked(initURI, "0"))); - - drop.freezeBatchBaseURI(0); - - assertEq(drop.batchFrozen(100), true); - } - - /*/////////////////////////////////////////////////////////////// - Unit Test: setMaxTotalSupply - //////////////////////////////////////////////////////////////*/ - - function test_state_setMaxTotalSupply() public { - vm.startPrank(deployer); - drop.setMaxTotalSupply(1, 100); - - assertEq(drop.maxTotalSupply(1), 100); - } - - function test_event_setMaxTotalSupply_MaxTotalSupplyUpdated() public { - vm.startPrank(deployer); - - vm.expectEmit(true, false, false, true); - emit MaxTotalSupplyUpdated(1, 100); - drop.setMaxTotalSupply(1, 100); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ -} diff --git a/src/test/drop/DropERC20.t.sol b/src/test/drop/DropERC20.t.sol deleted file mode 100644 index f0958a5fb..000000000 --- a/src/test/drop/DropERC20.t.sol +++ /dev/null @@ -1,716 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC20, Permissions, Drop } from "contracts/prebuilts/drop/DropERC20.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract DropERC20Test is BaseTest { - using Strings for uint256; - using Strings for address; - - DropERC20 public drop; - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - drop = DropERC20(getContract("DropERC20")); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc. - //////////////////////////////////////////////////////////////*/ - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_revert_nonHolder_renounceRole() public { - address caller = address(0x123); - bytes32 role = keccak256("TRANSFER_ROLE"); - - vm.prank(caller); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, caller, role)); - drop.renounceRole(role, caller); - } - - /** - * note: Tests whether contract reverts when a role admin revokes a role for a non-holder. - */ - function test_revert_revokeRoleForNonHolder() public { - address target = address(0x123); - bytes32 role = keccak256("TRANSFER_ROLE"); - - vm.prank(deployer); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, target, role)); - drop.revokeRole(role, target); - } - - /** - * @dev Tests whether contract reverts when a role is granted to an existent role holder. - */ - function test_revert_grant_role_to_account_with_role() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - - drop.grantRole(role, receiver); - - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsAlreadyGranted.selector, receiver, role)); - drop.grantRole(role, receiver); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_grant_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - // check if admin and address(0) have transfer role in the beginning - bool checkAddressZero = drop.hasRole(role, address(0)); - bool checkAdmin = drop.hasRole(role, deployer); - assertTrue(checkAddressZero); - assertTrue(checkAdmin); - - // check if transfer role can be granted to a non-holder - address receiver = getActor(0); - vm.startPrank(deployer); - drop.grantRole(role, receiver); - - // expect revert when granting to a holder - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsAlreadyGranted.selector, receiver, role)); - drop.grantRole(role, receiver); - - // check if receiver has transfer role - bool checkReceiver = drop.hasRole(role, receiver); - assertTrue(checkReceiver); - - // check if role is correctly revoked - drop.revokeRole(role, receiver); - checkReceiver = drop.hasRole(role, receiver); - assertFalse(checkReceiver); - drop.revokeRole(role, address(0)); - checkAddressZero = drop.hasRole(role, address(0)); - assertFalse(checkAddressZero); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_getRoleMember_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - uint256 roleMemberCount = drop.getRoleMemberCount(role); - assertEq(roleMemberCount, 2); - - address roleMember = drop.getRoleMember(role, 1); - assertEq(roleMember, address(0)); - - vm.startPrank(deployer); - drop.grantRole(role, address(2)); - drop.grantRole(role, address(3)); - drop.grantRole(role, address(4)); - - roleMemberCount = drop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.revokeRole(role, address(2)); - roleMemberCount = drop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.revokeRole(role, address(0)); - roleMemberCount = drop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.grantRole(role, address(5)); - roleMemberCount = drop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.grantRole(role, address(0)); - roleMemberCount = drop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.grantRole(role, address(6)); - roleMemberCount = drop.getRoleMemberCount(role); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - } - - /** - * note: Testing transfer of tokens when transfer-role is restricted - */ - function test_claim_transferRole() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC20.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - - // revoke transfer role from address(0) - vm.prank(deployer); - drop.revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.startPrank(receiver); - vm.expectRevert("transfers restricted."); - drop.transferFrom(receiver, address(123), 0); - } - - /** - * @dev Tests whether role member count is incremented correctly. - */ - function test_member_count_incremented_properly_when_role_granted() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - uint256 roleMemberCount = drop.getRoleMemberCount(role); - - assertEq(roleMemberCount, 0); - - drop.grantRole(role, receiver); - - assertEq(drop.getRoleMemberCount(role), 1); - - vm.stopPrank(); - } - - function test_claimCondition_with_startTimestamp() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC20.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); - conditions[0].startTimestamp = 100; - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.warp(99); - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(abi.encodeWithSelector(Drop.DropNoActiveCondition.selector)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - - vm.warp(100); - vm.prank(getActor(4), getActor(4)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - Claim Tests - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing revert condition; exceed max claimable supply. - */ - function test_revert_claimCondition_exceedMaxClaimableSupply() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC20.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 200; - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 100, address(0), 0, alp, ""); - - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedMaxSupply.selector, conditions[0].maxClaimableSupply, 101) - ); - vm.prank(getActor(6), getActor(6)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction when no allowlist present. - */ - function test_fuzz_claim_noAllowlist(uint256 x) public { - vm.assume(x != 0); - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC20.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 0) - ); - drop.claim(receiver, 0, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 101) - ); - drop.claim(receiver, 101, address(0), 0, alp, ""); - - vm.prank(deployer); - drop.setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 101) - ); - drop.claim(receiver, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to 0 - */ - function test_state_claim_allowlisted_SetQuantityZeroPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC20.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 100, address(erc20), 0, alp, ""); // claims for free, because allowlist price is 0 - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 100); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to non-zero value - */ - function test_state_claim_allowlisted_SetQuantityPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(uint256(300 ether)); - inputs[3] = Strings.toString(uint256(1 ether)); - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC20.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300 ether; - alp.pricePerToken = 1 ether; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500 ether; - conditions[0].quantityLimitPerWallet = 10 ether; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 5 ether; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(receiver, receiver); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimInvalidTokenPrice.selector, address(erc20), 0, address(erc20), 1 ether) - ); - drop.claim(receiver, 100 ether, address(erc20), 0, alp, ""); - - erc20.mint(receiver, 1000 ether); - vm.prank(receiver); - erc20.approve(address(drop), 1000 ether); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 100 ether, address(erc20), 1 ether, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 100 ether); - assertEq(erc20.balanceOf(receiver), 900 ether); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price not set; should default to general price and currency - */ - function test_state_claim_allowlisted_SetQuantityDefaultPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(uint256(300 ether)); - inputs[3] = Strings.toString(type(uint256).max); // this implies that general price is applicable - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC20.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300 ether; - alp.pricePerToken = type(uint256).max; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500 ether; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10 ether; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000 ether); - vm.prank(receiver); - erc20.approve(address(drop), 10000 ether); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 100 ether, address(erc20), 10 ether, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 100 ether); - assertEq(erc20.balanceOf(receiver), 10000 ether - 1000 ether); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to 0 => should default to general limit - * - allowlist price set to some value different than general price - */ - function test_state_claim_allowlisted_DefaultQuantitySomePrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "0"; // this implies that general limit is applicable - inputs[3] = Strings.toString(uint256(5 ether)); - inputs[4] = "0x0000000000000000000000000000000000000000"; // general currency will be applicable - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC20.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 0; - alp.pricePerToken = 5 ether; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500 ether; - conditions[0].quantityLimitPerWallet = 10 ether; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10 ether; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000 ether); - vm.prank(receiver); - erc20.approve(address(drop), 10000 ether); - - vm.prank(receiver, receiver); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 100 ether) - ); - drop.claim(receiver, 100 ether, address(erc20), 5 ether, alp, ""); // trying to claim more than general limit - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 10 ether, address(erc20), 5 ether, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 10 ether); - assertEq(erc20.balanceOf(receiver), 10000 ether - 50 ether); - } - - function test_fuzz_claim_merkleProof(uint256 x) public { - vm.assume(x > 10 && x < 500); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(x); - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC20.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - alp.pricePerToken = 0; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - // bytes32[] memory proofs = new bytes32[](0); - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); - conditions[0].maxClaimableSupply = x; - conditions[0].quantityLimitPerWallet = 1; - conditions[0].merkleRoot = root; - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, x - 5, address(0), 0, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), x - 5); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, x, x + 1)); - drop.claim(receiver, 6, address(0), 0, alp, ""); - - vm.prank(receiver, receiver); - drop.claim(receiver, 5, address(0), 0, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), x); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, x, x + 5)); - drop.claim(receiver, 5, address(0), 0, alp, ""); // quantity limit already claimed - } - - /** - * note: Testing state changes; reset eligibility of claim conditions and claiming again for same condition id. - */ - function test_state_claimCondition_resetEligibility() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC20.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 100, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 200) - ); - drop.claim(receiver, 100, address(0), 0, alp, ""); - - vm.prank(deployer); - drop.setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 100, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - setClaimConditions - //////////////////////////////////////////////////////////////*/ - - function test_claimCondition_startIdAndCount() public { - vm.startPrank(deployer); - - uint256 currentStartId = 0; - uint256 count = 0; - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](2); - conditions[0].startTimestamp = 0; - conditions[0].maxClaimableSupply = 10; - conditions[1].startTimestamp = 1; - conditions[1].maxClaimableSupply = 10; - - drop.setClaimConditions(conditions, false); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - drop.setClaimConditions(conditions, false); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - drop.setClaimConditions(conditions, true); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 2); - assertEq(count, 2); - - drop.setClaimConditions(conditions, true); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 4); - assertEq(count, 2); - } - - function test_claimCondition_startPhase() public { - vm.startPrank(deployer); - - uint256 activeConditionId = 0; - - DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](3); - conditions[0].startTimestamp = 10; - conditions[0].maxClaimableSupply = 11; - conditions[0].quantityLimitPerWallet = 12; - conditions[1].startTimestamp = 20; - conditions[1].maxClaimableSupply = 21; - conditions[1].quantityLimitPerWallet = 22; - conditions[2].startTimestamp = 30; - conditions[2].maxClaimableSupply = 31; - conditions[2].quantityLimitPerWallet = 32; - drop.setClaimConditions(conditions, false); - - vm.expectRevert(abi.encodeWithSelector(Drop.DropNoActiveCondition.selector)); - drop.getActiveClaimConditionId(); - - vm.warp(10); - activeConditionId = drop.getActiveClaimConditionId(); - assertEq(activeConditionId, 0); - assertEq(drop.getClaimConditionById(activeConditionId).startTimestamp, 10); - assertEq(drop.getClaimConditionById(activeConditionId).maxClaimableSupply, 11); - assertEq(drop.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 12); - - vm.warp(20); - activeConditionId = drop.getActiveClaimConditionId(); - assertEq(activeConditionId, 1); - assertEq(drop.getClaimConditionById(activeConditionId).startTimestamp, 20); - assertEq(drop.getClaimConditionById(activeConditionId).maxClaimableSupply, 21); - assertEq(drop.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 22); - - vm.warp(30); - activeConditionId = drop.getActiveClaimConditionId(); - assertEq(activeConditionId, 2); - assertEq(drop.getClaimConditionById(activeConditionId).startTimestamp, 30); - assertEq(drop.getClaimConditionById(activeConditionId).maxClaimableSupply, 31); - assertEq(drop.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 32); - - vm.warp(40); - assertEq(drop.getActiveClaimConditionId(), 2); - } -} diff --git a/src/test/drop/DropERC721.t.sol b/src/test/drop/DropERC721.t.sol deleted file mode 100644 index 14d3fdeec..000000000 --- a/src/test/drop/DropERC721.t.sol +++ /dev/null @@ -1,1203 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721, Permissions, LazyMint, BatchMintMetadata, Drop, DelayedReveal, IDelayedReveal, ERC721AUpgradeable, IPermissions, ILazyMint } from "contracts/prebuilts/drop/DropERC721.sol"; - -// Test imports -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; - -import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract DropERC721Test is BaseTest { - using Strings for uint256; - using Strings for address; - - event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI); - event TokenURIRevealed(uint256 indexed index, string revealedURI); - event MaxTotalSupplyUpdated(uint256 maxTotalSupply); - - DropERC721 public drop; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - drop = DropERC721(getContract("DropERC721")); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc. - //////////////////////////////////////////////////////////////*/ - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_revert_nonHolder_renounceRole() public { - address caller = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(caller); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, caller, role)); - drop.renounceRole(role, caller); - } - - /** - * note: Tests whether contract reverts when a role admin revokes a role for a non-holder. - */ - function test_revert_revokeRoleForNonHolder() public { - address target = address(0x123); - bytes32 role = keccak256("MINTER_ROLE"); - - vm.prank(deployer); - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, target, role)); - drop.revokeRole(role, target); - } - - /** - * @dev Tests whether contract reverts when a role is granted to an existent role holder. - */ - function test_revert_grant_role_to_account_with_role() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - - drop.grantRole(role, receiver); - - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsAlreadyGranted.selector, receiver, role)); - drop.grantRole(role, receiver); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_grant_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - // check if admin and address(0) have transfer role in the beginning - bool checkAddressZero = drop.hasRole(role, address(0)); - bool checkAdmin = drop.hasRole(role, deployer); - assertTrue(checkAddressZero); - assertTrue(checkAdmin); - - // check if transfer role can be granted to a non-holder - address receiver = getActor(0); - vm.startPrank(deployer); - drop.grantRole(role, receiver); - - // expect revert when granting to a holder - vm.expectRevert(abi.encodeWithSelector(Permissions.PermissionsAlreadyGranted.selector, receiver, role)); - drop.grantRole(role, receiver); - - // check if receiver has transfer role - bool checkReceiver = drop.hasRole(role, receiver); - assertTrue(checkReceiver); - - // check if role is correctly revoked - drop.revokeRole(role, receiver); - checkReceiver = drop.hasRole(role, receiver); - assertFalse(checkReceiver); - drop.revokeRole(role, address(0)); - checkAddressZero = drop.hasRole(role, address(0)); - assertFalse(checkAddressZero); - - vm.stopPrank(); - } - - /** - * @dev Tests contract state for Transfer role. - */ - function test_state_getRoleMember_transferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - - uint256 roleMemberCount = drop.getRoleMemberCount(role); - assertEq(roleMemberCount, 2); - - address roleMember = drop.getRoleMember(role, 1); - assertEq(roleMember, address(0)); - - vm.startPrank(deployer); - drop.grantRole(role, address(2)); - drop.grantRole(role, address(3)); - drop.grantRole(role, address(4)); - - roleMemberCount = drop.getRoleMemberCount(role); - assertEq(roleMemberCount, 5); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.revokeRole(role, address(2)); - roleMemberCount = drop.getRoleMemberCount(role); - assertEq(roleMemberCount, 4); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.revokeRole(role, address(0)); - roleMemberCount = drop.getRoleMemberCount(role); - assertEq(roleMemberCount, 3); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.grantRole(role, address(5)); - roleMemberCount = drop.getRoleMemberCount(role); - assertEq(roleMemberCount, 4); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.grantRole(role, address(0)); - roleMemberCount = drop.getRoleMemberCount(role); - assertEq(roleMemberCount, 5); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.grantRole(role, address(6)); - roleMemberCount = drop.getRoleMemberCount(role); - assertEq(roleMemberCount, 6); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.revokeRole(role, address(0)); - roleMemberCount = drop.getRoleMemberCount(role); - assertEq(roleMemberCount, 5); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - - drop.revokeRole(role, address(4)); - roleMemberCount = drop.getRoleMemberCount(role); - assertEq(roleMemberCount, 4); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(drop.getRoleMember(role, i)); - } - console.log(""); - } - - /** - * note: Testing transfer of tokens when transfer-role is restricted - */ - function test_claim_transferRole() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - - // revoke transfer role from address(0) - vm.prank(deployer); - drop.revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.startPrank(receiver); - vm.expectRevert("!Transfer-Role"); - drop.transferFrom(receiver, address(123), 0); - } - - /** - * @dev Tests whether role member count is incremented correctly. - */ - function test_member_count_incremented_properly_when_role_granted() public { - bytes32 role = keccak256("ABC_ROLE"); - address receiver = getActor(0); - - vm.startPrank(deployer); - uint256 roleMemberCount = drop.getRoleMemberCount(role); - - assertEq(roleMemberCount, 0); - - drop.grantRole(role, receiver); - - assertEq(drop.getRoleMemberCount(role), 1); - - vm.stopPrank(); - } - - function test_claimCondition_with_startTimestamp() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].startTimestamp = 100; - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.warp(99); - vm.prank(getActor(5), getActor(5)); - vm.expectRevert(abi.encodeWithSelector(Drop.DropNoActiveCondition.selector)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - - vm.warp(100); - vm.prank(getActor(4), getActor(4)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - Lazy Mint Tests - //////////////////////////////////////////////////////////////*/ - - /* - * note: Testing state changes; lazy mint a batch of tokens with no encrypted base URI. - */ - function test_state_lazyMint_noEncryptedURI() public { - uint256 amountToLazyMint = 100; - string memory baseURI = "ipfs://"; - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, emptyEncodedBytes); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.tokenURI(i); - console.log(uri); - assertEq(uri, string(abi.encodePacked(baseURI, i.toString()))); - } - - vm.stopPrank(); - } - - /* - * note: Testing state changes; lazy mint a batch of tokens with encrypted base URI. - */ - function test_state_lazyMint_withEncryptedURI() public { - uint256 amountToLazyMint = 100; - string memory baseURI = "ipfs://"; - bytes memory encryptedBaseURI = "encryptedBaseURI://"; - bytes32 provenanceHash = bytes32("whatever"); - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, abi.encode(encryptedBaseURI, provenanceHash)); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.tokenURI(i); - console.log(uri); - assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - } - - vm.stopPrank(); - } - - /** - * note: Testing revert condition; an address without MINTER_ROLE calls lazyMint function. - */ - function test_revert_lazyMint_MINTER_ROLE() public { - vm.expectRevert(abi.encodeWithSelector(LazyMint.LazyMintUnauthorized.selector)); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - } - - /* - * note: Testing revert condition; calling tokenURI for invalid batch id. - */ - function test_revert_lazyMint_URIForNonLazyMintedToken() public { - vm.startPrank(deployer); - - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidTokenId.selector, 100)); - drop.tokenURI(100); - - vm.stopPrank(); - } - - /** - * note: Testing event emission; tokens lazy minted. - */ - function test_event_lazyMint_TokensLazyMinted() public { - vm.startPrank(deployer); - - vm.expectEmit(true, false, false, true); - emit TokensLazyMinted(0, 99, "ipfs://", emptyEncodedBytes); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - - vm.stopPrank(); - } - - /* - * note: Fuzz testing state changes; lazy mint a batch of tokens with no encrypted base URI. - */ - function test_fuzz_lazyMint_noEncryptedURI(uint256 x) public { - vm.assume(x > 0); - - uint256 amountToLazyMint = x; - string memory baseURI = "ipfs://"; - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, emptyEncodedBytes); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - string memory uri = drop.tokenURI(0); - assertEq(uri, string(abi.encodePacked(baseURI, uint256(0).toString()))); - - uri = drop.tokenURI(x - 1); - assertEq(uri, string(abi.encodePacked(baseURI, uint256(x - 1).toString()))); - - /** - * note: this loop takes too long to run with fuzz tests. - */ - // for(uint256 i = 0; i < amountToLazyMint; i += 1) { - // string memory uri = drop.tokenURI(i); - // console.log(uri); - // assertEq(uri, string(abi.encodePacked(baseURI, i.toString()))); - // } - - vm.stopPrank(); - } - - /* - * note: Fuzz testing state changes; lazy mint a batch of tokens with encrypted base URI. - */ - function test_fuzz_lazyMint_withEncryptedURI(uint256 x) public { - vm.assume(x > 0); - - uint256 amountToLazyMint = x; - string memory baseURI = "ipfs://"; - bytes memory encryptedBaseURI = "encryptedBaseURI://"; - bytes32 provenanceHash = bytes32("whatever"); - - uint256 nextTokenIdToMintBefore = drop.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = drop.lazyMint(amountToLazyMint, baseURI, abi.encode(encryptedBaseURI, provenanceHash)); - - assertEq(nextTokenIdToMintBefore + amountToLazyMint, drop.nextTokenIdToMint()); - assertEq(nextTokenIdToMintBefore + amountToLazyMint, batchId); - - string memory uri = drop.tokenURI(0); - assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - - uri = drop.tokenURI(x - 1); - assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - - /** - * note: this loop takes too long to run with fuzz tests. - */ - // for(uint256 i = 0; i < amountToLazyMint; i += 1) { - // string memory uri = drop.tokenURI(1); - // assertEq(uri, string(abi.encodePacked(baseURI, "0"))); - // } - - vm.stopPrank(); - } - - /* - * note: Fuzz testing; a batch of tokens, and nextTokenIdToMint - */ - function test_fuzz_lazyMint_batchMintAndNextTokenIdToMint(uint256 x) public { - vm.assume(x > 0); - vm.startPrank(deployer); - - if (x == 0) { - vm.expectRevert("Zero amount"); - } - drop.lazyMint(x, "ipfs://", emptyEncodedBytes); - - uint256 slot = stdstore.target(address(drop)).sig("nextTokenIdToMint()").find(); - bytes32 loc = bytes32(slot); - uint256 nextTokenIdToMint = uint256(vm.load(address(drop), loc)); - - assertEq(nextTokenIdToMint, x); - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Delayed Reveal Tests - //////////////////////////////////////////////////////////////*/ - - /* - * note: Testing state changes; URI revealed for a batch of tokens. - */ - function test_state_reveal() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - uint256 amountToLazyMint = 100; - bytes memory secretURI = "ipfs://"; - string memory placeholderURI = "abcd://"; - bytes memory encryptedURI = drop.encryptDecrypt(secretURI, key); - bytes32 provenanceHash = keccak256(abi.encodePacked(secretURI, key, block.chainid)); - - drop.lazyMint(amountToLazyMint, placeholderURI, abi.encode(encryptedURI, provenanceHash)); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.tokenURI(i); - assertEq(uri, string(abi.encodePacked(placeholderURI, "0"))); - } - - string memory revealedURI = drop.reveal(0, key); - assertEq(revealedURI, string(secretURI)); - - for (uint256 i = 0; i < amountToLazyMint; i += 1) { - string memory uri = drop.tokenURI(i); - assertEq(uri, string(abi.encodePacked(secretURI, i.toString()))); - } - - vm.stopPrank(); - } - - /** - * note: Testing revert condition; an address without METADATA_ROLE calls reveal function. - */ - function test_revert_reveal_METADATA_ROLE() public { - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - vm.prank(deployer); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.prank(deployer); - drop.reveal(0, "key"); - - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - address(this), - keccak256("METADATA_ROLE") - ) - ); - drop.reveal(0, "key"); - } - - /* - * note: Testing revert condition; trying to reveal URI for non-existent batch. - */ - function test_revert_reveal_revealingNonExistentBatch() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - drop.reveal(0, "key"); - - console.log(drop.getBaseURICount()); - - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidBatchId.selector, 2)); - drop.reveal(2, "key"); - - vm.stopPrank(); - } - - /* - * note: Testing revert condition; already revealed URI. - */ - function test_revert_delayedReveal_alreadyRevealed() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - drop.reveal(0, "key"); - - vm.expectRevert(abi.encodeWithSelector(DelayedReveal.DelayedRevealNothingToReveal.selector)); - drop.reveal(0, "key"); - - vm.stopPrank(); - } - - /* - * note: Testing state changes; revealing URI with an incorrect key. - */ - function test_revert_reveal_incorrectKey() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.expectRevert(); - string memory revealedURI = drop.reveal(0, "keyy"); - - vm.stopPrank(); - } - - /** - * note: Testing event emission; TokenURIRevealed. - */ - function test_event_reveal_TokenURIRevealed() public { - vm.startPrank(deployer); - - bytes memory key = "key"; - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", key); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.expectEmit(true, false, false, true); - emit TokenURIRevealed(0, "ipfs://"); - drop.reveal(0, "key"); - - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Unit Test: updateBatchBaseURI - //////////////////////////////////////////////////////////////*/ - - function test_state_updateBatchBaseURI() public { - string memory initURI = "ipfs://init"; - string memory newURI = "ipfs://new"; - - vm.startPrank(deployer); - drop.lazyMint(100, initURI, ""); - - string memory initTokenURI = drop.tokenURI(0); - - assertEq(initTokenURI, string(abi.encodePacked(initURI, "0"))); - - drop.updateBatchBaseURI(0, newURI); - - string memory newTokenURI = drop.tokenURI(0); - - assertEq(newTokenURI, string(abi.encodePacked(newURI, "0"))); - } - - function test_updateBatchBaseURI_revert_encrypted() public { - bytes memory uri = "ipfs://init"; - bytes memory key = "key"; - - vm.startPrank(deployer); - bytes memory encryptedURI = drop.encryptDecrypt(uri, key); - bytes32 provenanceHash = keccak256(abi.encodePacked(uri, key, block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - - vm.expectRevert("Encrypted batch"); - drop.updateBatchBaseURI(0, "uri"); - } - - /*/////////////////////////////////////////////////////////////// - Unit Test: freezeBatchBaseURI - //////////////////////////////////////////////////////////////*/ - - function test_state_freezeBatchBaseURI() public { - string memory initURI = "ipfs://init"; - - vm.startPrank(deployer); - drop.lazyMint(100, initURI, ""); - - string memory initTokenURI = drop.tokenURI(0); - - assertEq(initTokenURI, string(abi.encodePacked(initURI, "0"))); - - drop.freezeBatchBaseURI(0); - - assertEq(drop.batchFrozen(100), true); - } - - /*/////////////////////////////////////////////////////////////// - Unit Test: setMaxTotalSupply - //////////////////////////////////////////////////////////////*/ - - function test_state_setMaxTotalSupply() public { - vm.startPrank(deployer); - drop.setMaxTotalSupply(100); - - assertEq(drop.maxTotalSupply(), 100); - } - - function test_event_setMaxTotalSupply_MaxTotalSupplyUpdated() public { - vm.startPrank(deployer); - - vm.expectEmit(true, false, false, true); - emit MaxTotalSupplyUpdated(100); - drop.setMaxTotalSupply(100); - } - - /*/////////////////////////////////////////////////////////////// - Claim Tests - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing revert condition; not enough minted tokens. - */ - function test_revert_claimCondition_notEnoughMintedTokens() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 200; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.expectRevert("!Tokens"); - vm.prank(getActor(6), getActor(6)); - drop.claim(receiver, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing revert condition; exceed max claimable supply. - */ - function test_revert_claimCondition_exceedMaxClaimableSupply() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 200; - - vm.prank(deployer); - drop.lazyMint(200, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 100, address(0), 0, alp, ""); - - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedMaxSupply.selector, conditions[0].maxClaimableSupply, 101) - ); - vm.prank(getActor(6), getActor(6)); - drop.claim(receiver, 1, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction when no allowlist present. - */ - function test_fuzz_claim_noAllowlist(uint256 x) public { - vm.assume(x != 0); - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 0) - ); - drop.claim(receiver, 0, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 101) - ); - drop.claim(receiver, 101, address(0), 0, alp, ""); - - vm.prank(deployer); - drop.setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 101) - ); - drop.claim(receiver, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to 0 - */ - function test_state_claim_allowlisted_SetQuantityZeroPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 100, address(erc20), 0, alp, ""); // claims for free, because allowlist price is 0 - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 100); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price set to non-zero value - */ - function test_state_claim_allowlisted_SetQuantityPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "5"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 5; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(receiver, receiver); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimInvalidTokenPrice.selector, address(erc20), 0, address(erc20), 5) - ); - drop.claim(receiver, 100, address(erc20), 0, alp, ""); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 100, address(erc20), 5, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 100); - assertEq(erc20.balanceOf(receiver), 10000 - 500); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to some value different than general limit - * - allowlist price not set; should default to general price and currency - */ - function test_state_claim_allowlisted_SetQuantityDefaultPrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = Strings.toString(type(uint256).max); // this implies that general price is applicable - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = type(uint256).max; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 100, address(erc20), 10, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 100); - assertEq(erc20.balanceOf(receiver), 10000 - 1000); - } - - /** - * note: Testing quantity limit restriction - * - allowlist quantity set to 0 => should default to general limit - * - allowlist price set to some value different than general price - */ - function test_state_claim_allowlisted_DefaultQuantitySomePrice() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "0"; // this implies that general limit is applicable - inputs[3] = "5"; - inputs[4] = "0x0000000000000000000000000000000000000000"; // general currency will be applicable - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 0; - alp.pricePerToken = 5; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - erc20.mint(receiver, 10000); - vm.prank(receiver); - erc20.approve(address(drop), 10000); - - vm.prank(receiver, receiver); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 100) - ); - drop.claim(receiver, 100, address(erc20), 5, alp, ""); // trying to claim more than general limit - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 10, address(erc20), 5, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), 10); - assertEq(erc20.balanceOf(receiver), 10000 - 50); - } - - function test_fuzz_claim_merkleProof(uint256 x) public { - vm.assume(x > 10 && x < 500); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(x); - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - alp.pricePerToken = 0; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - // bytes32[] memory proofs = new bytes32[](0); - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = x; - conditions[0].quantityLimitPerWallet = 1; - conditions[0].merkleRoot = root; - - vm.prank(deployer); - drop.lazyMint(2 * x, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, x - 5, address(0), 0, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), x - 5); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, x, x + 1)); - drop.claim(receiver, 6, address(0), 0, alp, ""); - - vm.prank(receiver, receiver); - drop.claim(receiver, 5, address(0), 0, alp, ""); - assertEq(drop.getSupplyClaimedByWallet(drop.getActiveClaimConditionId(), receiver), x); - - vm.prank(receiver, receiver); - vm.expectRevert(abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, x, x + 5)); - drop.claim(receiver, 5, address(0), 0, alp, ""); // quantity limit already claimed - } - - /** - * note: Testing state changes; reset eligibility of claim conditions and claiming again for same condition id. - */ - function test_state_claimCondition_resetEligibility() public { - vm.warp(1); - - address receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(500, "ipfs://", emptyEncodedBytes); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 100, address(0), 0, alp, ""); - - vm.prank(getActor(5), getActor(5)); - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, conditions[0].quantityLimitPerWallet, 200) - ); - drop.claim(receiver, 100, address(0), 0, alp, ""); - - vm.prank(deployer); - drop.setClaimConditions(conditions, true); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, 100, address(0), 0, alp, ""); - } - - /*/////////////////////////////////////////////////////////////// - setClaimConditions - //////////////////////////////////////////////////////////////*/ - - function test_claimCondition_startIdAndCount() public { - vm.startPrank(deployer); - - uint256 currentStartId = 0; - uint256 count = 0; - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](2); - conditions[0].startTimestamp = 0; - conditions[0].maxClaimableSupply = 10; - conditions[1].startTimestamp = 1; - conditions[1].maxClaimableSupply = 10; - - drop.setClaimConditions(conditions, false); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - drop.setClaimConditions(conditions, false); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 0); - assertEq(count, 2); - - drop.setClaimConditions(conditions, true); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 2); - assertEq(count, 2); - - drop.setClaimConditions(conditions, true); - (currentStartId, count) = drop.claimCondition(); - assertEq(currentStartId, 4); - assertEq(count, 2); - } - - function test_claimCondition_startPhase() public { - vm.startPrank(deployer); - - uint256 activeConditionId = 0; - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](3); - conditions[0].startTimestamp = 10; - conditions[0].maxClaimableSupply = 11; - conditions[0].quantityLimitPerWallet = 12; - conditions[1].startTimestamp = 20; - conditions[1].maxClaimableSupply = 21; - conditions[1].quantityLimitPerWallet = 22; - conditions[2].startTimestamp = 30; - conditions[2].maxClaimableSupply = 31; - conditions[2].quantityLimitPerWallet = 32; - drop.setClaimConditions(conditions, false); - - vm.expectRevert(abi.encodeWithSelector(Drop.DropNoActiveCondition.selector)); - drop.getActiveClaimConditionId(); - - vm.warp(10); - activeConditionId = drop.getActiveClaimConditionId(); - assertEq(activeConditionId, 0); - assertEq(drop.getClaimConditionById(activeConditionId).startTimestamp, 10); - assertEq(drop.getClaimConditionById(activeConditionId).maxClaimableSupply, 11); - assertEq(drop.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 12); - - vm.warp(20); - activeConditionId = drop.getActiveClaimConditionId(); - assertEq(activeConditionId, 1); - assertEq(drop.getClaimConditionById(activeConditionId).startTimestamp, 20); - assertEq(drop.getClaimConditionById(activeConditionId).maxClaimableSupply, 21); - assertEq(drop.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 22); - - vm.warp(30); - activeConditionId = drop.getActiveClaimConditionId(); - assertEq(activeConditionId, 2); - assertEq(drop.getClaimConditionById(activeConditionId).startTimestamp, 30); - assertEq(drop.getClaimConditionById(activeConditionId).maxClaimableSupply, 31); - assertEq(drop.getClaimConditionById(activeConditionId).quantityLimitPerWallet, 32); - - vm.warp(40); - assertEq(drop.getActiveClaimConditionId(), 2); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_delayedReveal_withNewLazyMintedEmptyBatch() public { - vm.startPrank(deployer); - - bytes memory encryptedURI = drop.encryptDecrypt("ipfs://", "key"); - bytes32 provenanceHash = keccak256(abi.encodePacked("ipfs://", "key", block.chainid)); - drop.lazyMint(100, "", abi.encode(encryptedURI, provenanceHash)); - drop.reveal(0, "key"); - - string memory uri = drop.tokenURI(1); - assertEq(uri, string(abi.encodePacked("ipfs://", "1"))); - - bytes memory newEncryptedURI = drop.encryptDecrypt("ipfs://secret", "key"); - vm.expectRevert(abi.encodeWithSelector(LazyMint.LazyMintInvalidAmount.selector)); - drop.lazyMint(0, "", abi.encode(newEncryptedURI, provenanceHash)); - - vm.stopPrank(); - } -} diff --git a/src/test/drop/drop-erc1155/_beforeClaim/_beforeClaim.t.sol b/src/test/drop/drop-erc1155/_beforeClaim/_beforeClaim.t.sol deleted file mode 100644 index 8397c3195..000000000 --- a/src/test/drop/drop-erc1155/_beforeClaim/_beforeClaim.t.sol +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155 } from "contracts/prebuilts/drop/DropERC1155.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract HarnessDropERC1155 is DropERC1155 { - function beforeClaim( - uint256 _tokenId, - address, - uint256 _quantity, - address, - uint256, - AllowlistProof calldata alp, - bytes memory - ) external view { - _beforeClaim(_tokenId, address(0), _quantity, address(0), 0, alp, bytes("")); - } -} - -contract DropERC1155Test_beforeClaim is BaseTest { - address public dropImp; - HarnessDropERC1155 public proxy; - - function setUp() public override { - super.setUp(); - - bytes memory initializeData = abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ); - - dropImp = address(new HarnessDropERC1155()); - proxy = HarnessDropERC1155(address(new TWProxy(dropImp, initializeData))); - - vm.prank(deployer); - proxy.setMaxTotalSupply(0, 1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc. - //////////////////////////////////////////////////////////////*/ - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_revert_ExceedMaxSupply() public { - DropERC1155.AllowlistProof memory alp; - vm.expectRevert("exceed max total supply"); - proxy.beforeClaim(0, address(0), 2, address(0), 0, alp, bytes("")); - } - - function test_NoRevert() public view { - DropERC1155.AllowlistProof memory alp; - proxy.beforeClaim(0, address(0), 1, address(0), 0, alp, bytes("")); - } -} diff --git a/src/test/drop/drop-erc1155/_beforeClaim/_beforeClaim.tree b/src/test/drop/drop-erc1155/_beforeClaim/_beforeClaim.tree deleted file mode 100644 index a5a114d07..000000000 --- a/src/test/drop/drop-erc1155/_beforeClaim/_beforeClaim.tree +++ /dev/null @@ -1,12 +0,0 @@ -function _beforeClaim( - uint256 _tokenId, - address, - uint256 _quantity, - address, - uint256, - AllowlistProof calldata, - bytes memory -) -└── when maxTotalSupply for _tokenId is not zero - └── when totalSupply of _tokenId + _quantity is greater than or equal to maxTotalSupply for _tokenId - └── it should revert ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc1155/_beforeTokenTransfer/_beforeTokenTransfer.sol b/src/test/drop/drop-erc1155/_beforeTokenTransfer/_beforeTokenTransfer.sol deleted file mode 100644 index b4c32607c..000000000 --- a/src/test/drop/drop-erc1155/_beforeTokenTransfer/_beforeTokenTransfer.sol +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155 } from "contracts/prebuilts/drop/DropERC1155.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract HarnessDropERC1155 is DropERC1155 { - function beforeTokenTransfer( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) external { - _beforeTokenTransfer(operator, from, to, ids, amounts, data); - } -} - -contract DropERC1155Test_beforeTokenTransfer is BaseTest { - address private beforeTransfer_from = address(0x01); - address private beforeTransfer_to = address(0x01); - uint256[] private beforeTransfer_ids; - uint256[] private beforeTransfer_amounts; - bytes private beforeTransfer_data; - - address public dropImp; - HarnessDropERC1155 public proxy; - - function setUp() public override { - super.setUp(); - - bytes memory initializeData = abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ); - - dropImp = address(new HarnessDropERC1155()); - proxy = HarnessDropERC1155(address(new TWProxy(dropImp, initializeData))); - - beforeTransfer_ids = new uint256[](1); - beforeTransfer_ids[0] = 0; - beforeTransfer_amounts = new uint256[](1); - beforeTransfer_amounts[0] = 1; - beforeTransfer_data = abi.encode("", ""); - } - - modifier fromAddressZero() { - beforeTransfer_from = address(0); - _; - } - - modifier toAddressZero() { - beforeTransfer_to = address(0); - _; - } - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_state_transferFromZero() public fromAddressZero { - uint256 beforeTokenTotalSupply = proxy.totalSupply(0); - proxy.beforeTokenTransfer( - deployer, - beforeTransfer_from, - beforeTransfer_to, - beforeTransfer_ids, - beforeTransfer_amounts, - beforeTransfer_data - ); - uint256 afterTokenTotalSupply = proxy.totalSupply(0); - assertEq(beforeTokenTotalSupply + beforeTransfer_amounts[0], afterTokenTotalSupply); - } - - function test_state_tranferToZero() public toAddressZero { - proxy.beforeTokenTransfer( - deployer, - beforeTransfer_to, - beforeTransfer_from, - beforeTransfer_ids, - beforeTransfer_amounts, - beforeTransfer_data - ); - uint256 beforeTokenTotalSupply = proxy.totalSupply(0); - proxy.beforeTokenTransfer( - deployer, - beforeTransfer_from, - beforeTransfer_to, - beforeTransfer_ids, - beforeTransfer_amounts, - beforeTransfer_data - ); - uint256 afterTokenTotalSupply = proxy.totalSupply(0); - assertEq(beforeTokenTotalSupply - beforeTransfer_amounts[0], afterTokenTotalSupply); - } -} diff --git a/src/test/drop/drop-erc1155/_beforeTokenTransfer/_beforeTokenTransfer.tree b/src/test/drop/drop-erc1155/_beforeTokenTransfer/_beforeTokenTransfer.tree deleted file mode 100644 index b5e147b76..000000000 --- a/src/test/drop/drop-erc1155/_beforeTokenTransfer/_beforeTokenTransfer.tree +++ /dev/null @@ -1,12 +0,0 @@ -function _beforeTokenTransfer( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data -) -├── when from equals to address(0) -│ └── totalSupply for each id is incremented by the corresponding amounts ✅ -└── when to equals address(0) - └── totalSupply for each id is decremented by the corresponding amounts ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc1155/_canSetFunctions/_canSetFunctions.sol b/src/test/drop/drop-erc1155/_canSetFunctions/_canSetFunctions.sol deleted file mode 100644 index 2be22888c..000000000 --- a/src/test/drop/drop-erc1155/_canSetFunctions/_canSetFunctions.sol +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155 } from "contracts/prebuilts/drop/DropERC1155.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract HarnessDropERC1155 is DropERC1155 { - function canSetPlatformFeeInfo() external view returns (bool) { - return _canSetPlatformFeeInfo(); - } - - /// @dev Checks whether primary sale recipient can be set in the given execution context. - function canSetPrimarySaleRecipient() external view returns (bool) { - return _canSetPrimarySaleRecipient(); - } - - /// @dev Checks whether owner can be set in the given execution context. - function canSetOwner() external view returns (bool) { - return _canSetOwner(); - } - - /// @dev Checks whether royalty info can be set in the given execution context. - function canSetRoyaltyInfo() external view returns (bool) { - return _canSetRoyaltyInfo(); - } - - /// @dev Checks whether contract metadata can be set in the given execution context. - function canSetContractURI() external view returns (bool) { - return _canSetContractURI(); - } - - /// @dev Checks whether platform fee info can be set in the given execution context. - function canSetClaimConditions() external view returns (bool) { - return _canSetClaimConditions(); - } - - /// @dev Returns whether lazy minting can be done in the given execution context. - function canLazyMint() external view virtual returns (bool) { - return _canLazyMint(); - } -} - -contract DropERC1155Test_canSetFunctions is BaseTest { - address public dropImp; - HarnessDropERC1155 public proxy; - - function setUp() public override { - super.setUp(); - - bytes memory initializeData = abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ); - - dropImp = address(new HarnessDropERC1155()); - proxy = HarnessDropERC1155(address(new TWProxy(dropImp, initializeData))); - } - - modifier HasDefaultAdminRole() { - vm.startPrank(deployer); - _; - } - - modifier DoesNotHaveDefaultAdminRole() { - vm.startPrank(address(0x123)); - _; - } - - modifier HasMinterRole() { - vm.startPrank(deployer); - _; - } - - modifier DoesNotHaveMinterRole() { - vm.startPrank(address(0x123)); - _; - } - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_canSetPlatformFeeInfo_true() public HasDefaultAdminRole { - assertTrue(proxy.canSetPlatformFeeInfo()); - } - - function test_canSetPlatformFeeInfo_false() public DoesNotHaveDefaultAdminRole { - assertFalse(proxy.canSetPlatformFeeInfo()); - } - - function test_canSetPrimarySaleRecipient_true() public HasDefaultAdminRole { - assertTrue(proxy.canSetPrimarySaleRecipient()); - } - - function test_canSetPrimarySaleRecipient_false() public DoesNotHaveDefaultAdminRole { - assertFalse(proxy.canSetPrimarySaleRecipient()); - } - - function test_canSetOwner_true() public HasDefaultAdminRole { - assertTrue(proxy.canSetOwner()); - } - - function test_canSetOwner_false() public DoesNotHaveDefaultAdminRole { - assertFalse(proxy.canSetOwner()); - } - - function test_canSetRoyaltyInfo_true() public HasDefaultAdminRole { - assertTrue(proxy.canSetRoyaltyInfo()); - } - - function test_canSetRoyaltyInfo_false() public DoesNotHaveDefaultAdminRole { - assertFalse(proxy.canSetRoyaltyInfo()); - } - - function test_canSetContractURI_true() public HasDefaultAdminRole { - assertTrue(proxy.canSetContractURI()); - } - - function test_canSetContractURI_false() public DoesNotHaveDefaultAdminRole { - assertFalse(proxy.canSetContractURI()); - } - - function test_canSetClaimConditions_true() public HasDefaultAdminRole { - assertTrue(proxy.canSetClaimConditions()); - } - - function test_canSetClaimConditions_false() public DoesNotHaveDefaultAdminRole { - assertFalse(proxy.canSetClaimConditions()); - } - - function test_canLazyMint_true() public HasMinterRole { - assertTrue(proxy.canLazyMint()); - } - - function test_canLazyMint_false() public DoesNotHaveMinterRole { - assertFalse(proxy.canLazyMint()); - } -} diff --git a/src/test/drop/drop-erc1155/_canSetFunctions/_canSetFunctions.tree b/src/test/drop/drop-erc1155/_canSetFunctions/_canSetFunctions.tree deleted file mode 100644 index 4b214c4e9..000000000 --- a/src/test/drop/drop-erc1155/_canSetFunctions/_canSetFunctions.tree +++ /dev/null @@ -1,41 +0,0 @@ -function _canSetPlatformFeeInfo() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetPrimarySaleRecipient() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetOwner() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetRoyaltyInfo() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetContractURI() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetClaimConditions() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canLazyMint() -├── when caller has minterRole -│ └── it should return true ✅ -└── when caller does not have minterRole - └── it should return false ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc1155/burnBatch/burnBatch.t.sol b/src/test/drop/drop-erc1155/burnBatch/burnBatch.t.sol deleted file mode 100644 index 86e365f33..000000000 --- a/src/test/drop/drop-erc1155/burnBatch/burnBatch.t.sol +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155 } from "contracts/prebuilts/drop/DropERC1155.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; -import "../../../../../lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC2981Upgradeable.sol"; - -contract DropERC1155Test_burnBatch is BaseTest { - DropERC1155 public drop; - - address private unauthorized = address(0x999); - address private account; - uint256[] private ids; - uint256[] private values; - - address private receiver; - bytes private emptyEncodedBytes = abi.encode("", ""); - - event TransferBatch( - address indexed operator, - address indexed from, - address indexed to, - uint256[] ids, - uint256[] values - ); - - function setUp() public override { - super.setUp(); - drop = DropERC1155(getContract("DropERC1155")); - - ids = new uint256[](1); - values = new uint256[](1); - ids[0] = 0; - values[0] = 1; - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier callerNotApproved() { - vm.startPrank(unauthorized); - _; - } - - modifier callerOwner() { - receiver = getActor(0); - vm.startPrank(receiver); - _; - } - - modifier callerApproved() { - receiver = getActor(0); - vm.prank(receiver); - drop.setApprovalForAll(deployer, true); - vm.startPrank(deployer); - _; - } - - modifier IdValueMismatch() { - values = new uint256[](2); - values[0] = 1; - values[1] = 1; - _; - } - - modifier tokenClaimed() { - vm.warp(1); - - uint256 _tokenId = 0; - receiver = getActor(0); - bytes32[] memory proofs = new bytes32[](0); - - DropERC1155.AllowlistProof memory alp; - alp.proof = proofs; - - DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - vm.prank(deployer); - drop.setClaimConditions(_tokenId, conditions, false); - - vm.prank(getActor(5), getActor(5)); - drop.claim(receiver, _tokenId, 1, address(0), 0, alp, ""); - - _; - } - - function test_revert_callerNotApproved() public tokenClaimed callerNotApproved { - vm.expectRevert("ERC1155: caller is not owner nor approved."); - drop.burnBatch(receiver, ids, values); - } - - function test_state_callerApproved() public tokenClaimed callerApproved { - uint256 beforeBalance = drop.balanceOf(receiver, ids[0]); - drop.burnBatch(receiver, ids, values); - uint256 afterBalance = drop.balanceOf(receiver, ids[0]); - assertEq(beforeBalance - values[0], afterBalance); - } - - function test_state_callerOwner() public tokenClaimed callerOwner { - uint256 beforeBalance = drop.balanceOf(receiver, ids[0]); - drop.burnBatch(receiver, ids, values); - uint256 afterBalance = drop.balanceOf(receiver, ids[0]); - assertEq(beforeBalance - values[0], afterBalance); - } - - function test_revert_IdValueMismatch() public tokenClaimed IdValueMismatch callerOwner { - vm.expectRevert("ERC1155: ids and amounts length mismatch"); - drop.burnBatch(receiver, ids, values); - } - - function test_revert_balanceUnderflow() public tokenClaimed callerOwner { - values[0] = 2; - vm.expectRevert(); - drop.burnBatch(receiver, ids, values); - } - - function test_event() public tokenClaimed callerOwner { - vm.expectEmit(true, true, true, true); - emit TransferBatch(receiver, receiver, address(0), ids, values); - drop.burnBatch(receiver, ids, values); - } -} diff --git a/src/test/drop/drop-erc1155/burnBatch/burnBatch.tree b/src/test/drop/drop-erc1155/burnBatch/burnBatch.tree deleted file mode 100644 index e8685085e..000000000 --- a/src/test/drop/drop-erc1155/burnBatch/burnBatch.tree +++ /dev/null @@ -1,17 +0,0 @@ -function burnBatch( - address account, - uint256[] memory ids, - uint256[] memory values -) -├── when account does not equal _msgSender() and _msgSender() is not an approved operator for account -│ └── it should revert ✅ -└── when account is equal to _msgSender() or _msgSender() is an approved operator for account - ├── when ids and values are not the same length - │ └── it should revert ✅ - └── when ids and values are the same length - ├── when the balance of account for each id is not greater than or equal to the corresponding value - │ └── it should revert ✅ - └── when the balance of account for each id is greater than or equal to the corresponding value - ├── it should reduce the balance of each id for account by the corresponding value ✅ - ├── it should reduce the total supply of each id by the corresponding value ✅ - └── it should emit TransferBatch with the following parameters: _msgSender(), account, address(0), ids, amounts ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc1155/collectPriceOnClaim/collectPriceOnClaim.t.sol b/src/test/drop/drop-erc1155/collectPriceOnClaim/collectPriceOnClaim.t.sol deleted file mode 100644 index fd9095fe3..000000000 --- a/src/test/drop/drop-erc1155/collectPriceOnClaim/collectPriceOnClaim.t.sol +++ /dev/null @@ -1,304 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155 } from "contracts/prebuilts/drop/DropERC1155.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract HarnessDropERC1155 is DropERC1155 { - function collectPriceOnClaimHarness( - uint256 _tokenId, - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) public payable { - collectPriceOnClaim(_tokenId, _primarySaleRecipient, _quantityToClaim, _currency, _pricePerToken); - } -} - -contract DropERC1155Test_collectPrice is BaseTest { - address private collectPrice_saleRecipient = address(0x010); - address private collectPrice_royaltyRecipient = address(0x011); - uint128 private collectPrice_royaltyBps = 1000; - uint128 private collectPrice_platformFeeBps = 1000; - address private collectPrice_platformFeeRecipient = address(0x012); - uint256 private collectPrice_quantityToClaim = 1; - uint256 private collectPrice_pricePerToken; - address private collectPrice_currency; - uint256 private collectPrice_msgValue; - address private collectPrice_tokenSaleRecipient = address(0x111); - address private defaultFeeRecipient; - - address public dropImp; - HarnessDropERC1155 public proxy; - - function setUp() public override { - super.setUp(); - - bytes memory initializeData = abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ); - - dropImp = address(new HarnessDropERC1155()); - proxy = HarnessDropERC1155(address(new TWProxy(dropImp, initializeData))); - defaultFeeRecipient = proxy.DEFAULT_FEE_RECIPIENT(); - } - - modifier pricePerTokenZero() { - collectPrice_pricePerToken = 0; - _; - } - - modifier pricePerTokenNotZero() { - collectPrice_pricePerToken = 1 ether; - _; - } - - modifier msgValueNotZero() { - collectPrice_msgValue = 1 ether; - _; - } - - modifier nativeCurrency() { - collectPrice_currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - _; - } - - modifier erc20Currency() { - collectPrice_currency = address(erc20); - erc20.mint(address(this), 1_000 ether); - _; - } - - modifier primarySaleRecipientZeroAddress() { - saleRecipient = address(0); - _; - } - - modifier primarySaleRecipientNotZeroAddress() { - saleRecipient = address(0x112); - _; - } - - modifier saleRecipientSet() { - vm.prank(deployer); - proxy.setSaleRecipientForToken(0, address(0x111)); - _; - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - function test_revert_msgValueNotZero() public nativeCurrency msgValueNotZero pricePerTokenZero { - vm.expectRevert(); - proxy.collectPriceOnClaimHarness{ value: collectPrice_msgValue }( - 0, - saleRecipient, - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - } - - function test_msgValueZero_return() public nativeCurrency pricePerTokenZero { - proxy.collectPriceOnClaimHarness{ value: collectPrice_msgValue }( - 0, - saleRecipient, - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - } - - function test_revert_priceValueMismatchNativeCurrency() public nativeCurrency pricePerTokenNotZero { - vm.expectRevert(); - proxy.collectPriceOnClaimHarness{ value: collectPrice_msgValue }( - 0, - saleRecipient, - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - } - - function test_transferNativeCurrencyToSaleRecipient() public nativeCurrency pricePerTokenNotZero msgValueNotZero { - uint256 balanceSaleRecipientBefore = address(saleRecipient).balance; - uint256 defaultFeeRecipientBefore = address(defaultFeeRecipient).balance; - uint256 platformFeeRecipientBefore = address(platformFeeRecipient).balance; - proxy.collectPriceOnClaimHarness{ value: collectPrice_msgValue }( - 0, - saleRecipient, - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - - uint256 balanceSaleRecipientAfter = address(saleRecipient).balance; - uint256 defaultFeeRecipientAfter = address(defaultFeeRecipient).balance; - uint256 platformFeeRecipientAfter = address(platformFeeRecipient).balance; - uint256 expectedDefaultPlatformFee = (collectPrice_pricePerToken * 100) / MAX_BPS; - uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; - uint256 expectedSaleRecipientProceed = collectPrice_msgValue - expectedPlatformFee - expectedDefaultPlatformFee; - - assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); - assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, expectedDefaultPlatformFee); - } - - function test_transferERC20ToSaleRecipient() public erc20Currency pricePerTokenNotZero { - uint256 balanceSaleRecipientBefore = erc20.balanceOf(saleRecipient); - uint256 defaultFeeRecipientBefore = erc20.balanceOf(defaultFeeRecipient); - uint256 platformFeeRecipientBefore = erc20.balanceOf(platformFeeRecipient); - erc20.approve(address(proxy), collectPrice_pricePerToken); - proxy.collectPriceOnClaimHarness( - 0, - saleRecipient, - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - - uint256 balanceSaleRecipientAfter = erc20.balanceOf(saleRecipient); - uint256 defaultFeeRecipientAfter = erc20.balanceOf(defaultFeeRecipient); - uint256 platformFeeRecipientAfter = erc20.balanceOf(platformFeeRecipient); - uint256 expectedDefaultPlatformFee = (collectPrice_pricePerToken * 100) / MAX_BPS; - uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; - uint256 expectedSaleRecipientProceed = collectPrice_pricePerToken - - expectedPlatformFee - - expectedDefaultPlatformFee; - - assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); - assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, expectedDefaultPlatformFee); - } - - function test_transferNativeCurrencyToTokenIdSaleRecipient() - public - nativeCurrency - pricePerTokenNotZero - msgValueNotZero - saleRecipientSet - primarySaleRecipientZeroAddress - { - uint256 balanceSaleRecipientBefore = address(collectPrice_tokenSaleRecipient).balance; - uint256 defaultFeeRecipientBefore = address(defaultFeeRecipient).balance; - uint256 platformFeeRecipientBefore = address(platformFeeRecipient).balance; - proxy.collectPriceOnClaimHarness{ value: collectPrice_msgValue }( - 0, - address(0), - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - - uint256 balanceSaleRecipientAfter = address(collectPrice_tokenSaleRecipient).balance; - uint256 defaultFeeRecipientAfter = address(defaultFeeRecipient).balance; - uint256 platformFeeRecipientAfter = address(platformFeeRecipient).balance; - uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; - uint256 expectedDefaultPlatformFee = (collectPrice_pricePerToken * 100) / MAX_BPS; - uint256 expectedSaleRecipientProceed = collectPrice_msgValue - expectedPlatformFee - expectedDefaultPlatformFee; - - assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); - assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, expectedDefaultPlatformFee); - } - - function test_transferERC20ToTokenIdSaleRecipient() public erc20Currency pricePerTokenNotZero saleRecipientSet { - uint256 balanceSaleRecipientBefore = erc20.balanceOf(collectPrice_tokenSaleRecipient); - uint256 defaultFeeRecipientBefore = erc20.balanceOf(defaultFeeRecipient); - uint256 platformFeeRecipientBefore = erc20.balanceOf(platformFeeRecipient); - erc20.approve(address(proxy), collectPrice_pricePerToken); - proxy.collectPriceOnClaimHarness( - 0, - address(0), - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - - uint256 balanceSaleRecipientAfter = erc20.balanceOf(collectPrice_tokenSaleRecipient); - uint256 defaultFeeRecipientAfter = erc20.balanceOf(defaultFeeRecipient); - uint256 platformFeeRecipientAfter = erc20.balanceOf(platformFeeRecipient); - uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; - uint256 expectedDefaultPlatformFee = (collectPrice_pricePerToken * 100) / MAX_BPS; - uint256 expectedSaleRecipientProceed = collectPrice_pricePerToken - - expectedPlatformFee - - expectedDefaultPlatformFee; - - assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); - assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, expectedDefaultPlatformFee); - } - - function test_transferNativeCurrencyToPrimarySaleRecipient() - public - nativeCurrency - pricePerTokenNotZero - msgValueNotZero - { - uint256 balanceSaleRecipientBefore = address(saleRecipient).balance; - uint256 balanceDefaultFeeRecipientBefore = address(defaultFeeRecipient).balance; - uint256 platformFeeRecipientBefore = address(platformFeeRecipient).balance; - proxy.collectPriceOnClaimHarness{ value: collectPrice_msgValue }( - 0, - address(0), - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - - uint256 balanceSaleRecipientAfter = address(saleRecipient).balance; - uint256 balanceDefaultFeeRecipientAfter = address(defaultFeeRecipient).balance; - uint256 platformFeeRecipientAfter = address(platformFeeRecipient).balance; - uint256 expectedDefaultPlatformFee = (collectPrice_pricePerToken * 100) / MAX_BPS; - uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; - uint256 expectedSaleRecipientProceed = collectPrice_msgValue - expectedPlatformFee - expectedDefaultPlatformFee; - - assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); - assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); - assertEq(balanceDefaultFeeRecipientAfter - balanceDefaultFeeRecipientBefore, expectedDefaultPlatformFee); - } - - function test_transferERC20ToPrimarySaleRecipient() public erc20Currency pricePerTokenNotZero { - uint256 balanceSaleRecipientBefore = erc20.balanceOf(saleRecipient); - uint256 defaultFeeRecipientBefore = erc20.balanceOf(defaultFeeRecipient); - uint256 platformFeeRecipientBefore = erc20.balanceOf(platformFeeRecipient); - erc20.approve(address(proxy), collectPrice_pricePerToken); - proxy.collectPriceOnClaimHarness( - 0, - address(0), - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - - uint256 balanceSaleRecipientAfter = erc20.balanceOf(saleRecipient); - uint256 defaultFeeRecipientAfter = erc20.balanceOf(defaultFeeRecipient); - uint256 platformFeeRecipientAfter = erc20.balanceOf(platformFeeRecipient); - uint256 expectedDefaultPlatformFee = (collectPrice_pricePerToken * 100) / MAX_BPS; - uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; - uint256 expectedSaleRecipientProceed = collectPrice_pricePerToken - - expectedPlatformFee - - expectedDefaultPlatformFee; - - assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, expectedDefaultPlatformFee); - assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); - } -} diff --git a/src/test/drop/drop-erc1155/collectPriceOnClaim/collectPriceOnClaim.tree b/src/test/drop/drop-erc1155/collectPriceOnClaim/collectPriceOnClaim.tree deleted file mode 100644 index 6cb5cd180..000000000 --- a/src/test/drop/drop-erc1155/collectPriceOnClaim/collectPriceOnClaim.tree +++ /dev/null @@ -1,44 +0,0 @@ -function collectPriceOnClaim( - uint256 _tokenId, - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) -├── when _pricePerToken is equal to zero -│ ├── when msg.value does not equal to zero -│ │ └── it should revert ✅ -│ └── when msg.value is equal to zero -│ └── it should return ✅ -└── when _pricePerToken is not equal to zero - ├── when _primarySaleRecipient is equal to address(0) - │ ├── when saleRecipient for _tokenId is equal to address(0) - │ │ ├── when currency is native token - │ │ │ ├── when msg.value does not equal totalPrice - │ │ │ │ └── it should revert ✅ - │ │ │ └── when msg.value does equal totalPrice - │ │ │ ├── it should transfer platformFees to platformFeeRecipient in native token ✅ - │ │ │ └── it should transfer totalPrice - platformFees to primarySaleRecipient in native token ✅ - │ │ └── when currency is not native token - │ │ ├── it should transfer platformFees to platformFeeRecipient in _currency token ✅ - │ │ └── it should transfer totalPrice - platformFees to primarySaleRecipient in _currency token ✅ - │ └── when salerecipient for _tokenId is not equal to address(0) - │ ├── when currency is native token - │ │ ├── when msg.value does not equal totalPrice - │ │ │ └── it should revert ✅ - │ │ └── when msg.value does equal totalPrice - │ │ ├── it should transfer platformFees to platformFeeRecipient in native token ✅ - │ │ └── it should transfer totalPrice - platformFees to saleRecipient for _tokenId in native token ✅ - │ └── when currency is not native token - │ ├── it should transfer platformFees to platformFeeRecipient in _currency token ✅ - │ └── it should transfer totalPrice - platformFees to saleRecipient for _tokenId in _currency token ✅ - └── when _primarySaleRecipient is not equal to address(0) - ├── when currency is native token - │ ├── when msg.value does not equal totalPrice - │ │ └── it should revert ✅ - │ └── when msg.value does equal totalPrice - │ ├── it should transfer platformFees to platformFeeRecipient in native token ✅ - │ └── it should transfer totalPrice - platformFees to _primarySaleRecipient in native token ✅ - └── when currency is not native token - ├── it should transfer platformFees to platformFeeRecipient in _currency token ✅ - └── it should transfer totalPrice - platformFees to _primarySaleRecipient in _currency token ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc1155/freezeBatchBaseURI/freezeBatchBaseURI.t.sol b/src/test/drop/drop-erc1155/freezeBatchBaseURI/freezeBatchBaseURI.t.sol deleted file mode 100644 index 774669b51..000000000 --- a/src/test/drop/drop-erc1155/freezeBatchBaseURI/freezeBatchBaseURI.t.sol +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155, BatchMintMetadata } from "contracts/prebuilts/drop/DropERC1155.sol"; - -// Test imports - -import "../../../utils/BaseTest.sol"; -import "../../../../../lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC2981Upgradeable.sol"; - -contract DropERC1155Test_freezeBatchBaseURI is BaseTest { - event MetadataFrozen(); - - DropERC1155 public drop; - - address private unauthorized = address(0x123); - - bytes private emptyEncodedBytes = abi.encode("", ""); - - function setUp() public override { - super.setUp(); - drop = DropERC1155(getContract("DropERC1155")); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier callerWithoutMetadataRole() { - vm.startPrank(unauthorized); - _; - } - - modifier callerWithMetadataRole() { - vm.startPrank(deployer); - _; - } - - modifier lazyMint() { - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - _; - } - - modifier lazyMintEmptyUri() { - vm.prank(deployer); - drop.lazyMint(100, "", emptyEncodedBytes); - _; - } - - function test_revert_NoMetadataRole() public lazyMint callerWithoutMetadataRole { - bytes32 role = keccak256("METADATA_ROLE"); - vm.expectRevert( - abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, unauthorized, role) - ); - drop.freezeBatchBaseURI(0); - } - - function test_revert_IndexTooHigh() public lazyMint callerWithMetadataRole { - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidBatchId.selector, 1)); - drop.freezeBatchBaseURI(1); - } - - function test_revert_EmptyBaseURI() public lazyMintEmptyUri callerWithMetadataRole { - vm.expectRevert( - abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidBatchId.selector, drop.getBatchIdAtIndex(0)) - ); - drop.freezeBatchBaseURI(0); - } - - function test_state() public lazyMint callerWithMetadataRole { - uint256 batchId = drop.getBatchIdAtIndex(0); - drop.freezeBatchBaseURI(0); - assertEq(drop.batchFrozen(batchId), true); - } - - function test_event() public lazyMint callerWithMetadataRole { - vm.expectEmit(false, false, false, false); - emit MetadataFrozen(); - drop.freezeBatchBaseURI(0); - } -} diff --git a/src/test/drop/drop-erc1155/freezeBatchBaseURI/freezeBatchBaseURI.tree b/src/test/drop/drop-erc1155/freezeBatchBaseURI/freezeBatchBaseURI.tree deleted file mode 100644 index 7a9a00b7f..000000000 --- a/src/test/drop/drop-erc1155/freezeBatchBaseURI/freezeBatchBaseURI.tree +++ /dev/null @@ -1,12 +0,0 @@ -function freezeBatchBaseURI(uint256 _index) -├── when the caller does not have metadataRole -│ └── it should revert ✅ -└── when the caller has metadataRole - ├── when _index is greater than the number of current batches - │ └── it should revert ✅ - └── when _index is equal to or less than the number of current batches - ├── when the baseURI for the batch at _index is not set - │ └── it should revert ✅ - └── when the baseURI for the batch at _index is set - ├── it should set batchFrozen[(batchId for _index)] to true ✅ - └── it should emit MetadataFrozen ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc1155/initialize/initialize.t.sol b/src/test/drop/drop-erc1155/initialize/initialize.t.sol deleted file mode 100644 index 4f242b9e9..000000000 --- a/src/test/drop/drop-erc1155/initialize/initialize.t.sol +++ /dev/null @@ -1,407 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155, Royalty, PlatformFee } from "contracts/prebuilts/drop/DropERC1155.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract DropERC1155Test_initializer is BaseTest { - DropERC1155 public newDropContract; - - event ContractURIUpdated(string prevURI, string newURI); - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); - event PrimarySaleRecipientUpdated(address indexed recipient); - - function setUp() public override { - super.setUp(); - } - - modifier royaltyBPSTooHigh() { - uint128 royaltyBps = 10001; - _; - } - - modifier platformFeeBPSTooHigh() { - uint128 platformFeeBps = 10001; - _; - } - - function test_state() public { - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - - newDropContract = DropERC1155(getContract("DropERC1155")); - (address _platformFeeRecipient, uint128 _platformFeeBps) = newDropContract.getPlatformFeeInfo(); - (address _royaltyRecipient, uint128 _royaltyBps) = newDropContract.getDefaultRoyaltyInfo(); - address _saleRecipient = newDropContract.primarySaleRecipient(); - - for (uint256 i = 0; i < forwarders().length; i++) { - assertEq(newDropContract.isTrustedForwarder(forwarders()[i]), true); - } - - assertEq(newDropContract.name(), NAME); - assertEq(newDropContract.symbol(), SYMBOL); - assertEq(newDropContract.contractURI(), CONTRACT_URI); - assertEq(newDropContract.owner(), deployer); - assertEq(_platformFeeRecipient, platformFeeRecipient); - assertEq(_platformFeeBps, platformFeeBps); - assertEq(_royaltyRecipient, royaltyRecipient); - assertEq(_royaltyBps, royaltyBps); - assertEq(_saleRecipient, saleRecipient); - } - - function test_revert_RoyaltyBPSTooHigh() public royaltyBPSTooHigh { - vm.expectRevert(abi.encodeWithSelector(Royalty.RoyaltyExceededMaxFeeBps.selector, 10_000, 10_001)); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - 10001, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_revert_PlatformFeeBPSTooHigh() public platformFeeBPSTooHigh { - vm.expectRevert(abi.encodeWithSelector(PlatformFee.PlatformFeeExceededMaxFeeBps.selector, 10_000, 10_001)); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - 10001, - platformFeeRecipient - ) - ) - ); - } - - function test_event_ContractURIUpdated() public { - vm.expectEmit(false, false, false, true); - emit ContractURIUpdated("", CONTRACT_URI); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_OwnerUpdated() public { - vm.expectEmit(true, true, false, false); - emit OwnerUpdated(address(0), deployer); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_RoleGrantedDefaultAdminRole() public { - bytes32 role = bytes32(0x00); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, deployer, factory); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_RoleGrantedMinterRole() public { - bytes32 role = keccak256("MINTER_ROLE"); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, deployer, factory); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_RoleGrantedTransferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, deployer, factory); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_RoleGrantedTransferRoleZeroAddress() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, address(0), factory); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_RoleGrantedMetadataRole() public { - bytes32 role = keccak256("METADATA_ROLE"); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, deployer, factory); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_RoleAdminChangedMetadataRole() public { - bytes32 role = keccak256("METADATA_ROLE"); - vm.expectEmit(true, true, true, false); - emit RoleAdminChanged(role, bytes32(0x00), role); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_PlatformFeeInfoUpdated() public { - vm.expectEmit(true, false, false, true); - emit PlatformFeeInfoUpdated(platformFeeRecipient, platformFeeBps); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_DefaultRoyalty() public { - vm.expectEmit(true, false, false, true); - emit DefaultRoyalty(royaltyRecipient, royaltyBps); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_PrimarySaleRecipientUpdated() public { - vm.expectEmit(true, false, false, false); - emit PrimarySaleRecipientUpdated(saleRecipient); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_roleCheck() public { - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - - newDropContract = DropERC1155(getContract("DropERC1155")); - - assertEq(newDropContract.hasRole(bytes32(0x00), deployer), true); - assertEq(newDropContract.hasRole(keccak256("MINTER_ROLE"), deployer), true); - assertEq(newDropContract.hasRole(keccak256("TRANSFER_ROLE"), deployer), true); - assertEq(newDropContract.hasRole(keccak256("TRANSFER_ROLE"), address(0)), true); - assertEq(newDropContract.hasRole(keccak256("METADATA_ROLE"), deployer), true); - - assertEq(newDropContract.getRoleAdmin(keccak256("METADATA_ROLE")), keccak256("METADATA_ROLE")); - } -} diff --git a/src/test/drop/drop-erc1155/initialize/initialize.tree b/src/test/drop/drop-erc1155/initialize/initialize.tree deleted file mode 100644 index 76c8d15d0..000000000 --- a/src/test/drop/drop-erc1155/initialize/initialize.tree +++ /dev/null @@ -1,50 +0,0 @@ -function initialize( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _saleRecipient, - address _royaltyRecipient, - uint128 _royaltyBps, - uint128 _platformFeeBps, - address _platformFeeRecipient -) -├── when _trustedForwarders.length > 0 -│ └── it should set _trustedForwarder[_trustedForwarders[i]] as true for each address in _trustedForwarders ✅ -├── it should set _uri to an empty string ✅ -├── it should set contractURI as _contractURI ✅ -├── it should emit ContractURIUpdated with the parameters: prevURI, _uri ✅ -├── it should set _defaultAdmin as the owner of the contract ✅ -├── it should emit OwnerUpdated with the parameters: _prevOwner, _defaultAdmin ✅ -├── it should assign the role DEFAULT_ADMIN_ROLE to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: DEFAULT_ADMIN_ROLE, _defaultAdmin, msg.sender ✅ -├── it should assign the role _minterRole to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: _minterRole, _defaultAdmin, msg.sender ✅ -├── it should assign the role _transferRole to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: _transferRole, _defaultAdmin, msg.sender ✅ -├── it should assign the role _transferRole to address(0) ✅ -├── it should emit RoleGranted with the parameters: _transferRole, address(0), msg.sender ✅ -├── it should assign the role _metadataRole to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: _metadataRole, _defaultAdmin, msg.sender ✅ -├── it should set _getAdminRole[_metadataRole] to equal _metadataRole ✅ -├── it should emit RoleAdminChanged with the parameters _metadataRole, previousAdminRole, _metadataRole ✅ -├── when _platformFeeBps is greater than 10_000 -│ └── it should revert ✅ -├── when _platformFeeBps is less than or equal to 10_000 -│ ├── it should set platformFeeBps to uint16(_platformFeeBps); ✅ -│ ├── it should set platformFeeRecipient to _platformFeeRecipient ✅ -│ └── it should emit PlatformFeeInfoUpdated with the following parameters: _platformFeeRecipient, _platformFeeBps ✅ -├── when _royaltyBps is greater than 10_000 -│ └── it should revert ✅ -├── when _royaltyBps is less than or equal to 10_000 -│ ├── it should set royaltyRecipient as _royaltyRecipient ✅ -│ ├── it should set royaltyBps as uint16(_royaltyBps) ✅ -│ └── it should emit DefaultRoyalty with the parameters _royaltyRecipient, _royaltyBps ✅ -├── it should set recipient as _primarySaleRecipient ✅ -├── it should emit PrimarySaleRecipientUpdated with the parameters _primarySaleRecipient ✅ -├── it should set transferRole as keccak256("TRANSFER_ROLE") ✅ -├── it should set minterRole as keccak256("MINTER_ROLE") ✅ -├── it should set metadataRole as keccak256("METADATA_ROLE") ✅ -├── it should set name as _name ✅ -└── it should set symbol as _symbol ✅ diff --git a/src/test/drop/drop-erc1155/miscellaneous/miscellaneous.t.sol b/src/test/drop/drop-erc1155/miscellaneous/miscellaneous.t.sol deleted file mode 100644 index 661c0e3db..000000000 --- a/src/test/drop/drop-erc1155/miscellaneous/miscellaneous.t.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155 } from "contracts/prebuilts/drop/DropERC1155.sol"; - -// Test imports - -import "../../../utils/BaseTest.sol"; -import "../../../../../lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC2981Upgradeable.sol"; -import "../../../../../lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC1155Upgradeable.sol"; -import "../../../../../lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC1155MetadataURIUpgradeable.sol"; - -contract DropERC1155Test_misc is BaseTest { - DropERC1155 public drop; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - function setUp() public override { - super.setUp(); - drop = DropERC1155(getContract("DropERC1155")); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier lazyMint() { - vm.prank(deployer); - drop.lazyMint(10, "ipfs://", emptyEncodedBytes); - _; - } - - function test_nextTokenIdToMint_ZeroLazyMinted() public { - uint256 nextTokenIdToMint = drop.nextTokenIdToMint(); - assertEq(nextTokenIdToMint, 0); - } - - function test_nextTokenIdToMint_TenLazyMinted() public lazyMint { - uint256 nextTokenIdToMint = drop.nextTokenIdToMint(); - assertEq(nextTokenIdToMint, 10); - } - - function test_contractType() public { - assertEq(drop.contractType(), bytes32("DropERC1155")); - } - - function test_contractVersion() public { - assertEq(drop.contractVersion(), uint8(4)); - } - - function test_supportsInterface() public { - assertEq(drop.supportsInterface(type(IERC2981Upgradeable).interfaceId), true); - assertEq(drop.supportsInterface(type(IERC1155Upgradeable).interfaceId), true); - assertEq(drop.supportsInterface(type(IERC1155MetadataURIUpgradeable).interfaceId), true); - } - - function test__msgData() public { - HarnessDropERC1155MsgData msgDataDrop = new HarnessDropERC1155MsgData(); - bytes memory msgData = msgDataDrop.msgData(); - bytes4 expectedData = msgDataDrop.msgData.selector; - assertEq(bytes4(msgData), expectedData); - } -} - -contract HarnessDropERC1155MsgData is DropERC1155 { - function msgData() public view returns (bytes memory) { - return _msgData(); - } -} diff --git a/src/test/drop/drop-erc1155/miscellaneous/miscellaneous.tree b/src/test/drop/drop-erc1155/miscellaneous/miscellaneous.tree deleted file mode 100644 index e74189c71..000000000 --- a/src/test/drop/drop-erc1155/miscellaneous/miscellaneous.tree +++ /dev/null @@ -1,8 +0,0 @@ -function nextTokenIdToMint() -└── it should return the next tokenId that is to be lazy minted ✅ - -function contractType() -└── it should return "DropERC1155" in bytes32 format ✅ - -function contractVersion() -└── it should return 4 in uint8 format ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc1155/setMaxTotalSupply/setMaxTotalSupply.t.sol b/src/test/drop/drop-erc1155/setMaxTotalSupply/setMaxTotalSupply.t.sol deleted file mode 100644 index 965c4ce5e..000000000 --- a/src/test/drop/drop-erc1155/setMaxTotalSupply/setMaxTotalSupply.t.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155 } from "contracts/prebuilts/drop/DropERC1155.sol"; - -// Test imports - -import "../../../utils/BaseTest.sol"; -import "../../../../../lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC2981Upgradeable.sol"; - -contract DropERC1155Test_setMaxTotalSupply is BaseTest { - DropERC1155 public drop; - - address private unauthorized = address(0x123); - - uint256 private newMaxSupply = 100; - string private updatedBaseURI = "ipfs://"; - - event MaxTotalSupplyUpdated(uint256 tokenId, uint256 maxTotalSupply); - - function setUp() public override { - super.setUp(); - drop = DropERC1155(getContract("DropERC1155")); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier callerWithoutAdminRole() { - vm.startPrank(unauthorized); - _; - } - - modifier callerWithAdminRole() { - vm.startPrank(deployer); - _; - } - - function test_revert_NoAdminRole() public callerWithoutAdminRole { - bytes32 role = bytes32(0x00); - vm.expectRevert( - abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, unauthorized, role) - ); - drop.setMaxTotalSupply(0, newMaxSupply); - } - - function test_state() public callerWithAdminRole { - drop.setMaxTotalSupply(0, newMaxSupply); - uint256 newMaxTotalSupply = drop.maxTotalSupply(0); - assertEq(newMaxSupply, newMaxTotalSupply); - } - - function test_event() public callerWithAdminRole { - vm.expectEmit(false, false, false, true); - emit MaxTotalSupplyUpdated(0, newMaxSupply); - drop.setMaxTotalSupply(0, newMaxSupply); - } -} diff --git a/src/test/drop/drop-erc1155/setMaxTotalSupply/setMaxTotalSupply.tree b/src/test/drop/drop-erc1155/setMaxTotalSupply/setMaxTotalSupply.tree deleted file mode 100644 index 8aa6ad0ef..000000000 --- a/src/test/drop/drop-erc1155/setMaxTotalSupply/setMaxTotalSupply.tree +++ /dev/null @@ -1,6 +0,0 @@ -function setMaxTotalSupply(uint256 _tokenId, uint256 _maxTotalSupply) -├── when the caller does not have DEFAULT_ADMIN_ROLE -│ └── it should revert ✅ -└── when the caller does have DEFAULT_ADMIN_ROLE - ├── it should set maxTotalSupply for _tokenId as _maxTotalSupply ✅ - └── it should emit MaxTotalSupplyUpdated with the parameters _tokenId, _maxTotalSupply ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc1155/setSaleRecipientForToken/setSaleRecipientForToken.t.sol b/src/test/drop/drop-erc1155/setSaleRecipientForToken/setSaleRecipientForToken.t.sol deleted file mode 100644 index a3d194a53..000000000 --- a/src/test/drop/drop-erc1155/setSaleRecipientForToken/setSaleRecipientForToken.t.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155 } from "contracts/prebuilts/drop/DropERC1155.sol"; - -// Test imports - -import "../../../utils/BaseTest.sol"; -import "../../../../../lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC2981Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract DropERC1155Test_setSaleRecipientForToken is BaseTest { - using Strings for uint256; - - DropERC1155 public drop; - - address private unauthorized = address(0x123); - address private recipient = address(0x456); - - event SaleRecipientForTokenUpdated(uint256 indexed tokenId, address saleRecipient); - - function setUp() public override { - super.setUp(); - drop = DropERC1155(getContract("DropERC1155")); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier callerWithoutAdminRole() { - vm.startPrank(unauthorized); - _; - } - - modifier callerWithAdminRole() { - vm.startPrank(deployer); - _; - } - - function test_revert_NoAdminRole() public callerWithoutAdminRole { - bytes32 role = bytes32(0x00); - vm.expectRevert( - abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, unauthorized, role) - ); - drop.setSaleRecipientForToken(0, recipient); - } - - function test_state() public callerWithAdminRole { - drop.setSaleRecipientForToken(0, recipient); - address newSaleRecipient = drop.saleRecipient(0); - assertEq(newSaleRecipient, recipient); - } - - function test_event() public callerWithAdminRole { - vm.expectEmit(true, true, false, false); - emit SaleRecipientForTokenUpdated(0, recipient); - drop.setSaleRecipientForToken(0, recipient); - } -} diff --git a/src/test/drop/drop-erc1155/setSaleRecipientForToken/setSaleRecipientForToken.tree b/src/test/drop/drop-erc1155/setSaleRecipientForToken/setSaleRecipientForToken.tree deleted file mode 100644 index 72d7c2cad..000000000 --- a/src/test/drop/drop-erc1155/setSaleRecipientForToken/setSaleRecipientForToken.tree +++ /dev/null @@ -1,6 +0,0 @@ -function setSaleRecipientForToken(uint256 _tokenId, address _saleRecipient) -├── when called by a user without DEFAULT_ADMIN_ROLE -│ └── it should revert ✅ -└── when called by a user with DEFAULT_ADMIN_ROLE - ├── it should set saleRecipient for _tokenId as _saleRecipient ✅ - └── it should emit SaleRecipientForTokenUpdated with the parameters _tokenId, _saleRecipient ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc1155/transferTokensOnClaim/transferTokensOnClaim.t.sol b/src/test/drop/drop-erc1155/transferTokensOnClaim/transferTokensOnClaim.t.sol deleted file mode 100644 index 411206cc5..000000000 --- a/src/test/drop/drop-erc1155/transferTokensOnClaim/transferTokensOnClaim.t.sol +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155 } from "contracts/prebuilts/drop/DropERC1155.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports - -import "../../../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract HarnessDropERC1155 is DropERC1155 { - function transferTokensOnClaimHarness(address to, uint256 _tokenId, uint256 _quantityBeingClaimed) external { - transferTokensOnClaim(to, _tokenId, _quantityBeingClaimed); - } -} - -contract MockERC1155Receiver { - function onERC1155Received(address, address, uint256, uint256, bytes memory) external pure returns (bytes4) { - return this.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address, - address, - uint256[] memory, - uint256[] memory, - bytes memory - ) external pure returns (bytes4) { - return this.onERC1155BatchReceived.selector; - } -} - -contract MockERC11555NotReceiver {} - -contract DropERC1155Test_transferTokensOnClaim is BaseTest { - using Strings for uint256; - using Strings for address; - - address private to; - MockERC1155Receiver private receiver; - MockERC11555NotReceiver private notReceiver; - - address public dropImp; - HarnessDropERC1155 public proxy; - - function setUp() public override { - super.setUp(); - - bytes memory initializeData = abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ); - - dropImp = address(new HarnessDropERC1155()); - proxy = HarnessDropERC1155(address(new TWProxy(dropImp, initializeData))); - - receiver = new MockERC1155Receiver(); - notReceiver = new MockERC11555NotReceiver(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc. - //////////////////////////////////////////////////////////////*/ - - modifier toEOA() { - to = address(0x01); - _; - } - - modifier toReceiever() { - to = address(receiver); - _; - } - - modifier toNotReceiever() { - to = address(notReceiver); - _; - } - - /** - * note: Tests whether contract reverts when a non-holder renounces a role. - */ - function test_revert_ContractNotERC155Receiver() public toNotReceiever { - vm.expectRevert("ERC1155: transfer to non-ERC1155Receiver implementer"); - proxy.transferTokensOnClaimHarness(to, 0, 1); - } - - function test_state_ContractERC1155Receiver() public toReceiever { - uint256 beforeBalance = proxy.balanceOf(to, 0); - proxy.transferTokensOnClaimHarness(to, 0, 1); - uint256 afterBalance = proxy.balanceOf(to, 0); - assertEq(beforeBalance + 1, afterBalance); - } - - function test_state_EOAReceiver() public toEOA { - uint256 beforeBalance = proxy.balanceOf(to, 0); - proxy.transferTokensOnClaimHarness(to, 0, 1); - uint256 afterBalance = proxy.balanceOf(to, 0); - assertEq(beforeBalance + 1, afterBalance); - } -} diff --git a/src/test/drop/drop-erc1155/transferTokensOnClaim/transferTokensOnClaim.tree b/src/test/drop/drop-erc1155/transferTokensOnClaim/transferTokensOnClaim.tree deleted file mode 100644 index eb600dbed..000000000 --- a/src/test/drop/drop-erc1155/transferTokensOnClaim/transferTokensOnClaim.tree +++ /dev/null @@ -1,12 +0,0 @@ -function transferTokensOnClaim( - address _to, - uint256 _tokenId, - uint256 _quantityBeingClaimed -) -├── when {to} is a smart contract -│ ├── when {to} does not implement onERC1155Received -│ │ └── it should revert ✅ -│ └── when {to} does implement onERC1155Received -│ └── it should mint {amount} number of {id} tokens to {to} ✅ -└── when {to} is an EOA - └── it should mint {amount} number of {id} tokens to {to} ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc1155/updateBatchBaseURI/updateBatchBaseURI.t.sol b/src/test/drop/drop-erc1155/updateBatchBaseURI/updateBatchBaseURI.t.sol deleted file mode 100644 index fa980975b..000000000 --- a/src/test/drop/drop-erc1155/updateBatchBaseURI/updateBatchBaseURI.t.sol +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC1155, BatchMintMetadata } from "contracts/prebuilts/drop/DropERC1155.sol"; - -// Test imports - -import "../../../utils/BaseTest.sol"; -import "../../../../../lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC2981Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract DropERC1155Test_updateBatchBaseURI is BaseTest { - using Strings for uint256; - - event MetadataFrozen(); - - DropERC1155 public drop; - - address private unauthorized = address(0x123); - - bytes private emptyEncodedBytes = abi.encode("", ""); - string private updatedBaseURI = "ipfs://"; - - event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); - - function setUp() public override { - super.setUp(); - drop = DropERC1155(getContract("DropERC1155")); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier callerWithoutMetadataRole() { - vm.startPrank(unauthorized); - _; - } - - modifier callerWithMetadataRole() { - vm.startPrank(deployer); - _; - } - - modifier lazyMint() { - vm.prank(deployer); - drop.lazyMint(100, "ipfs://", emptyEncodedBytes); - _; - } - - modifier lazyMintEmptyUri() { - vm.prank(deployer); - drop.lazyMint(100, "", emptyEncodedBytes); - _; - } - - modifier batchFrozen() { - vm.prank(deployer); - drop.freezeBatchBaseURI(0); - _; - } - - function test_revert_NoMetadataRole() public lazyMint callerWithoutMetadataRole { - bytes32 role = keccak256("METADATA_ROLE"); - vm.expectRevert( - abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, unauthorized, role) - ); - drop.updateBatchBaseURI(0, updatedBaseURI); - } - - function test_revert_IndexTooHigh() public lazyMint callerWithMetadataRole { - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidBatchId.selector, 1)); - drop.updateBatchBaseURI(1, updatedBaseURI); - } - - function test_revert_BatchFrozen() public lazyMint batchFrozen callerWithMetadataRole { - vm.expectRevert( - abi.encodeWithSelector(BatchMintMetadata.BatchMintMetadataFrozen.selector, drop.getBatchIdAtIndex(0)) - ); - drop.updateBatchBaseURI(0, updatedBaseURI); - } - - function test_state() public lazyMint callerWithMetadataRole { - drop.updateBatchBaseURI(0, updatedBaseURI); - string memory newBaseURI = drop.uri(0); - console.log("newBaseURI: %s", newBaseURI); - assertEq(newBaseURI, string(abi.encodePacked(updatedBaseURI, "0"))); - } - - function test_event() public lazyMint callerWithMetadataRole { - vm.expectEmit(false, false, false, false); - emit BatchMetadataUpdate(0, 100); - drop.updateBatchBaseURI(0, updatedBaseURI); - } -} diff --git a/src/test/drop/drop-erc1155/updateBatchBaseURI/updateBatchBaseURI.tree b/src/test/drop/drop-erc1155/updateBatchBaseURI/updateBatchBaseURI.tree deleted file mode 100644 index 75ffaadcf..000000000 --- a/src/test/drop/drop-erc1155/updateBatchBaseURI/updateBatchBaseURI.tree +++ /dev/null @@ -1,9 +0,0 @@ -function updateBatchBaseURI(uint256 _index, string calldata _uri) -├── when the caller does not have metadataRole -│ └── it should revert ✅ -└── when the caller has metadataRole - ├── when batchFrozen[_batchId for _index] is equal to true - │ └── it should revert ✅ - └── when batchFrozen[_batchId for _index] is equal to false - ├── it should set baseURI[_batchId for _index] to _uri ✅ - └── it should emit BatchMetadataUpdate with the parameters startingTokenId, _batchId ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc20/_beforeClaim/_beforeClaim.t.sol b/src/test/drop/drop-erc20/_beforeClaim/_beforeClaim.t.sol deleted file mode 100644 index c6c3e681b..000000000 --- a/src/test/drop/drop-erc20/_beforeClaim/_beforeClaim.t.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC20 } from "contracts/prebuilts/drop/DropERC20.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract HarnessDropERC20BeforeClaim is DropERC20 { - bytes private emptyBytes = bytes(""); - - function harness_beforeClaim(uint256 quantity, AllowlistProof calldata _proof) public view { - _beforeClaim(address(0), quantity, address(0), 0, _proof, emptyBytes); - } -} - -contract DropERC20Test_beforeClaim is BaseTest { - address public dropImp; - HarnessDropERC20BeforeClaim public proxy; - - uint256 private mintQty; - - function setUp() public override { - super.setUp(); - - bytes memory initializeData = abi.encodeCall( - DropERC20.initialize, - (deployer, NAME, SYMBOL, CONTRACT_URI, forwarders(), saleRecipient, platformFeeRecipient, platformFeeBps) - ); - - dropImp = address(new HarnessDropERC20BeforeClaim()); - proxy = HarnessDropERC20BeforeClaim(address(new TWProxy(dropImp, initializeData))); - } - - modifier setMaxTotalSupply() { - vm.prank(deployer); - proxy.setMaxTotalSupply(100); - _; - } - - modifier qtyExceedMaxTotalSupply() { - mintQty = 101; - _; - } - - function test_revert_MaxSupplyExceeded() public setMaxTotalSupply qtyExceedMaxTotalSupply { - DropERC20.AllowlistProof memory proof; - vm.expectRevert("exceed max total supply."); - proxy.harness_beforeClaim(mintQty, proof); - } -} diff --git a/src/test/drop/drop-erc20/_beforeClaim/_beforeClaim.tree b/src/test/drop/drop-erc20/_beforeClaim/_beforeClaim.tree deleted file mode 100644 index f1cf867a4..000000000 --- a/src/test/drop/drop-erc20/_beforeClaim/_beforeClaim.tree +++ /dev/null @@ -1,10 +0,0 @@ -function _beforeClaim( - address, - uint256 _quantity, - address, - uint256, - AllowlistProof calldata, - bytes memory -) -└── when maxTotalSupply does not equal to 0 and totalSupply() + _quantity is greater than _maxTotalSupply - └── it should revert ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc20/_canSetFunctions/_canSetFunctions.t.sol b/src/test/drop/drop-erc20/_canSetFunctions/_canSetFunctions.t.sol deleted file mode 100644 index 2b17cdd08..000000000 --- a/src/test/drop/drop-erc20/_canSetFunctions/_canSetFunctions.t.sol +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC20 } from "contracts/prebuilts/drop/DropERC20.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract HarnessDropERC20CanSet is DropERC20 { - function canSetPlatformFeeInfo() external view returns (bool) { - return _canSetPlatformFeeInfo(); - } - - function canSetPrimarySaleRecipient() external view returns (bool) { - return _canSetPrimarySaleRecipient(); - } - - function canSetContractURI() external view returns (bool) { - return _canSetContractURI(); - } - - function canSetClaimConditions() external view returns (bool) { - return _canSetClaimConditions(); - } -} - -contract DropERC20Test_canSet is BaseTest { - address public dropImp; - - HarnessDropERC20CanSet public proxy; - - function setUp() public override { - super.setUp(); - - bytes memory initializeData = abi.encodeCall( - DropERC20.initialize, - (deployer, NAME, SYMBOL, CONTRACT_URI, forwarders(), saleRecipient, platformFeeRecipient, platformFeeBps) - ); - - dropImp = address(new HarnessDropERC20CanSet()); - proxy = HarnessDropERC20CanSet(address(new TWProxy(dropImp, initializeData))); - } - - modifier callerHasDefaultAdminRole() { - vm.startPrank(deployer); - _; - } - - modifier callerDoesNotHaveDefaultAdminRole() { - _; - } - - function test_canSetPlatformFee_returnTrue() public callerHasDefaultAdminRole { - bool status = proxy.canSetPlatformFeeInfo(); - assertEq(status, true); - } - - function test_canSetPlatformFee_returnFalse() public callerDoesNotHaveDefaultAdminRole { - bool status = proxy.canSetPlatformFeeInfo(); - assertEq(status, false); - } - - function test_canSetPrimarySaleRecipient_returnTrue() public callerHasDefaultAdminRole { - bool status = proxy.canSetPrimarySaleRecipient(); - assertEq(status, true); - } - - function test_canSetPrimarySaleRecipient_returnFalse() public callerDoesNotHaveDefaultAdminRole { - bool status = proxy.canSetPrimarySaleRecipient(); - assertEq(status, false); - } - - function test_canSetContractURI_returnTrue() public callerHasDefaultAdminRole { - bool status = proxy.canSetContractURI(); - assertEq(status, true); - } - - function test_canSetContractURI_returnFalse() public callerDoesNotHaveDefaultAdminRole { - bool status = proxy.canSetContractURI(); - assertEq(status, false); - } - - function test_canSetClaimConditions_returnTrue() public callerHasDefaultAdminRole { - bool status = proxy.canSetClaimConditions(); - assertEq(status, true); - } - - function test_canSetClaimConditions_returnFalse() public callerDoesNotHaveDefaultAdminRole { - bool status = proxy.canSetClaimConditions(); - assertEq(status, false); - } -} diff --git a/src/test/drop/drop-erc20/_canSetFunctions/_canSetFunctions.tree b/src/test/drop/drop-erc20/_canSetFunctions/_canSetFunctions.tree deleted file mode 100644 index 2f2da72e4..000000000 --- a/src/test/drop/drop-erc20/_canSetFunctions/_canSetFunctions.tree +++ /dev/null @@ -1,23 +0,0 @@ -function _canSetPlatformFeeInfo() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetPrimarySaleRecipient() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetContractURI() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetClaimConditions() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ diff --git a/src/test/drop/drop-erc20/_collectPriceOnClaim/_collectPriceOnClaim.t.sol b/src/test/drop/drop-erc20/_collectPriceOnClaim/_collectPriceOnClaim.t.sol deleted file mode 100644 index 35eb4ab78..000000000 --- a/src/test/drop/drop-erc20/_collectPriceOnClaim/_collectPriceOnClaim.t.sol +++ /dev/null @@ -1,227 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC20 } from "contracts/prebuilts/drop/DropERC20.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract HarnessDropERC20CollectPriceOnClaim is DropERC20 { - function harness_collectPrice( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) public payable { - _collectPriceOnClaim(_primarySaleRecipient, _quantityToClaim, _currency, _pricePerToken); - } -} - -contract DropERC20Test_collectPrice is BaseTest { - address public dropImp; - HarnessDropERC20CollectPriceOnClaim public proxy; - - address private currency; - address private primarySaleRecipient; - uint256 private msgValue; - uint256 private pricePerToken; - address private defaultFeeRecipient; - - function setUp() public override { - super.setUp(); - - bytes memory initializeData = abi.encodeCall( - DropERC20.initialize, - (deployer, NAME, SYMBOL, CONTRACT_URI, forwarders(), saleRecipient, platformFeeRecipient, platformFeeBps) - ); - - dropImp = address(new HarnessDropERC20CollectPriceOnClaim()); - proxy = HarnessDropERC20CollectPriceOnClaim(address(new TWProxy(dropImp, initializeData))); - defaultFeeRecipient = proxy.DEFAULT_FEE_RECIPIENT(); - } - - modifier pricePerTokenZero() { - _; - } - - modifier pricePerTokenNotZero() { - pricePerToken = 1 ether; - _; - } - - modifier msgValueZero() { - _; - } - - modifier msgValueNotZero() { - msgValue = 1 ether; - _; - } - - modifier valuePriceMismatch() { - msgValue = 1 ether; - pricePerToken = 2 ether; - _; - } - - modifier primarySaleRecipientZeroAddress() { - primarySaleRecipient = address(0); - _; - } - - modifier primarySaleRecipientNotZeroAddress() { - primarySaleRecipient = address(0x0999); - _; - } - - modifier currencyNativeToken() { - currency = NATIVE_TOKEN; - _; - } - - modifier currencyNotNativeToken() { - currency = address(erc20); - _; - } - - function test_revert_pricePerTokenZeroMsgValueNotZero() public pricePerTokenZero msgValueNotZero { - vm.expectRevert("!Value"); - proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 1 ether, currency, pricePerToken); - } - - function test_revert_nativeCurrencyTotalPriceZero() public pricePerTokenNotZero msgValueZero currencyNativeToken { - vm.expectRevert("quantity too low"); - proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 0, currency, pricePerToken); - } - - function test_revert_nativeCurrencyValuePriceMismatch() public currencyNativeToken valuePriceMismatch { - vm.expectRevert("Invalid msg value"); - proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 1 ether, currency, pricePerToken); - } - - function test_revert_erc20ValuePriceMismatch() public currencyNotNativeToken valuePriceMismatch { - vm.expectRevert("Invalid msg value"); - proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 1 ether, currency, pricePerToken); - } - - function test_state_nativeCurrency() - public - currencyNativeToken - pricePerTokenNotZero - msgValueNotZero - primarySaleRecipientNotZeroAddress - { - (address platformFeeRecipient, uint16 platformFeeBps) = proxy.getPlatformFeeInfo(); - uint256 beforeBalancePrimarySaleRecipient = address(primarySaleRecipient).balance; - uint256 beforeBalancePlatformFeeRecipient = address(platformFeeRecipient).balance; - uint256 defaultFeeRecipientBefore = address(defaultFeeRecipient).balance; - - proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 1 ether, currency, pricePerToken); - - uint256 afterBalancePrimarySaleRecipient = address(primarySaleRecipient).balance; - uint256 afterBalancePlatformFeeRecipient = address(platformFeeRecipient).balance; - uint256 defaultFeeRecipientAfter = address(defaultFeeRecipient).balance; - - uint256 defaultPlatformFeeVal = (pricePerToken * 100) / MAX_BPS; - uint256 platformFeeVal = (msgValue * platformFeeBps) / MAX_BPS; - uint256 primarySaleRecipientVal = msgValue - platformFeeVal - defaultPlatformFeeVal; - - assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); - assertEq(beforeBalancePlatformFeeRecipient + platformFeeVal, afterBalancePlatformFeeRecipient); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, defaultPlatformFeeVal); - } - - function test_revert_erc20_msgValueNotZero() - public - currencyNotNativeToken - msgValueNotZero - primarySaleRecipientNotZeroAddress - { - vm.expectRevert("!Value"); - proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, msgValue, currency, pricePerToken); - } - - function test_state_erc20() public currencyNotNativeToken pricePerTokenNotZero primarySaleRecipientNotZeroAddress { - (address platformFeeRecipient, uint16 platformFeeBps) = proxy.getPlatformFeeInfo(); - - erc20.mint(address(this), pricePerToken); - ERC20(erc20).approve(address(proxy), pricePerToken); - uint256 beforeBalancePrimarySaleRecipient = erc20.balanceOf(primarySaleRecipient); - uint256 defaultFeeRecipientBefore = erc20.balanceOf(defaultFeeRecipient); - uint256 beforeBalancePlatformFeeRecipient = erc20.balanceOf(platformFeeRecipient); - - proxy.harness_collectPrice(primarySaleRecipient, pricePerToken, currency, pricePerToken); - - uint256 afterBalancePrimarySaleRecipient = erc20.balanceOf(primarySaleRecipient); - uint256 defaultFeeRecipientAfter = erc20.balanceOf(defaultFeeRecipient); - uint256 afterBalancePlatformFeeRecipient = erc20.balanceOf(platformFeeRecipient); - - uint256 defaultPlatformFeeVal = (pricePerToken * 100) / MAX_BPS; - uint256 platformFeeVal = (pricePerToken * platformFeeBps) / MAX_BPS; - uint256 primarySaleRecipientVal = 1 ether - platformFeeVal - defaultPlatformFeeVal; - - assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); - assertEq(beforeBalancePlatformFeeRecipient + platformFeeVal, afterBalancePlatformFeeRecipient); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, defaultPlatformFeeVal); - } - - function test_state_erc20StoredPrimarySaleRecipient() - public - currencyNotNativeToken - pricePerTokenNotZero - primarySaleRecipientZeroAddress - { - (address platformFeeRecipient, uint16 platformFeeBps) = proxy.getPlatformFeeInfo(); - address storedPrimarySaleRecipient = proxy.primarySaleRecipient(); - - erc20.mint(address(this), pricePerToken); - ERC20(erc20).approve(address(proxy), pricePerToken); - uint256 beforeBalancePrimarySaleRecipient = erc20.balanceOf(storedPrimarySaleRecipient); - uint256 defaultFeeRecipientBefore = erc20.balanceOf(defaultFeeRecipient); - uint256 beforeBalancePlatformFeeRecipient = erc20.balanceOf(platformFeeRecipient); - - proxy.harness_collectPrice(primarySaleRecipient, pricePerToken, currency, pricePerToken); - - uint256 afterBalancePrimarySaleRecipient = erc20.balanceOf(storedPrimarySaleRecipient); - uint256 defaultFeeRecipientAfter = erc20.balanceOf(defaultFeeRecipient); - uint256 afterBalancePlatformFeeRecipient = erc20.balanceOf(platformFeeRecipient); - - uint256 defaultPlatformFeeVal = (pricePerToken * 100) / MAX_BPS; - uint256 platformFeeVal = (pricePerToken * platformFeeBps) / MAX_BPS; - uint256 primarySaleRecipientVal = 1 ether - platformFeeVal - defaultPlatformFeeVal; - - assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); - assertEq(beforeBalancePlatformFeeRecipient + platformFeeVal, afterBalancePlatformFeeRecipient); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, defaultPlatformFeeVal); - } - - function test_state_nativeCurrencyStoredPrimarySaleRecipient() - public - currencyNativeToken - pricePerTokenNotZero - primarySaleRecipientZeroAddress - msgValueNotZero - { - (address platformFeeRecipient, uint16 platformFeeBps) = proxy.getPlatformFeeInfo(); - address storedPrimarySaleRecipient = proxy.primarySaleRecipient(); - - uint256 beforeBalancePrimarySaleRecipient = address(storedPrimarySaleRecipient).balance; - uint256 beforeBalancePlatformFeeRecipient = address(platformFeeRecipient).balance; - uint256 defaultFeeRecipientBefore = address(defaultFeeRecipient).balance; - - proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 1 ether, currency, pricePerToken); - - uint256 afterBalancePrimarySaleRecipient = address(storedPrimarySaleRecipient).balance; - uint256 afterBalancePlatformFeeRecipient = address(platformFeeRecipient).balance; - uint256 defaultFeeRecipientAfter = address(defaultFeeRecipient).balance; - - uint256 defaultPlatformFeeVal = (pricePerToken * 100) / MAX_BPS; - uint256 platformFeeVal = (msgValue * platformFeeBps) / MAX_BPS; - uint256 primarySaleRecipientVal = msgValue - platformFeeVal - defaultPlatformFeeVal; - - assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); - assertEq(beforeBalancePlatformFeeRecipient + platformFeeVal, afterBalancePlatformFeeRecipient); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, defaultPlatformFeeVal); - } -} diff --git a/src/test/drop/drop-erc20/_collectPriceOnClaim/_collectPriceOnClaim.tree b/src/test/drop/drop-erc20/_collectPriceOnClaim/_collectPriceOnClaim.tree deleted file mode 100644 index 933ae6877..000000000 --- a/src/test/drop/drop-erc20/_collectPriceOnClaim/_collectPriceOnClaim.tree +++ /dev/null @@ -1,44 +0,0 @@ -function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken -) -├── when _pricePerToken is equal to zero -│ ├── when msg.value does not equal to zero -│ │ └── it should revert ✅ -│ └── when msg.value is equal to zero -│ └── it should return ✅ -└── when _pricePerToken is not equal to zero - ├── when _primarySaleRecipient is equal to address(0) - │ ├── when totalPrice is equal to zero - │ │ └── it should revert ✅ - │ └── when total price is not equal to zero - │ ├── when currency is native token - │ │ ├── when msg.value does not equal totalPrice - │ │ │ └── it should revert ✅ - │ │ └── when msg.value does equal totalPrice - │ │ ├── platformFees (totalPrice * platformFeeBps / MAX_BPS) should be transfered to platformFeeRecipient ✅ - │ │ └── totalPrice - platformFees should be transfered to primarySaleRecipient() ✅ - │ └── when currency is not native token - │ ├── when msg.value is not equal to zero - │ │ └── it should revert ✅ - │ └── when msg.value is equal to zero - │ ├── platformFees (totalPrice * platformFeeBps / MAX_BPS) should be transfered to platformFeeRecipient ✅ - │ └── totalPrice - platformFees should be transfered to primarySaleRecipient() ✅ - └── when _primarySaleRecipient is not equal to address(0) - ├── when totalPrice is equal to zero - │ └── it should revert ✅ - └── when total price is not equal to zero - ├── when currency is not native token - │ ├── when msg.value does not equal totalPrice - │ │ └── it should revert ✅ - │ └── when msg.value does equal totalPrice - │ ├── platformFees (totalPrice * platformFeeBps / MAX_BPS) should be transfered to platformFeeRecipient ✅ - │ └── totalPrice - platformFees should be transfered to _primarySaleRecipient ✅ - └── when currency is not native token - ├── when msg.value is not equal to zero - │ └── it should revert ✅ - └── when msg.value is equal to zero - ├── platformFees (totalPrice * platformFeeBps / MAX_BPS) should be transfered to platformFeeRecipient ✅ - └── totalPrice - platformFees should be transfered to _primarySaleRecipient ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc20/initialize/initialize.t.sol b/src/test/drop/drop-erc20/initialize/initialize.t.sol deleted file mode 100644 index 61debd129..000000000 --- a/src/test/drop/drop-erc20/initialize/initialize.t.sol +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC20, PlatformFee } from "contracts/prebuilts/drop/DropERC20.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract DropERC20Test_initializer is BaseTest { - DropERC20 public newDropContract; - - event ContractURIUpdated(string prevURI, string newURI); - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - event PrimarySaleRecipientUpdated(address indexed recipient); - - function setUp() public override { - super.setUp(); - } - - modifier platformFeeBPSTooHigh() { - platformFeeBps = 10001; - _; - } - - function test_state() public { - deployContractProxy( - "DropERC20", - abi.encodeCall( - DropERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ); - - newDropContract = DropERC20(getContract("DropERC20")); - (address _platformFeeRecipient, uint128 _platformFeeBps) = newDropContract.getPlatformFeeInfo(); - address _saleRecipient = newDropContract.primarySaleRecipient(); - - for (uint256 i = 0; i < forwarders().length; i++) { - assertEq(newDropContract.isTrustedForwarder(forwarders()[i]), true); - } - - assertEq(newDropContract.name(), NAME); - assertEq(newDropContract.symbol(), SYMBOL); - assertEq(newDropContract.contractURI(), CONTRACT_URI); - assertEq(_platformFeeRecipient, platformFeeRecipient); - assertEq(_platformFeeBps, platformFeeBps); - assertEq(_saleRecipient, saleRecipient); - } - - function test_revert_PlatformFeeBPSTooHigh() public platformFeeBPSTooHigh { - vm.expectRevert( - abi.encodeWithSelector(PlatformFee.PlatformFeeExceededMaxFeeBps.selector, 10_000, platformFeeBps) - ); - deployContractProxy( - "DropERC20", - abi.encodeCall( - DropERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ); - } - - function test_event_ContractURIUpdated() public { - vm.expectEmit(false, false, false, true); - emit ContractURIUpdated("", CONTRACT_URI); - deployContractProxy( - "DropERC20", - abi.encodeCall( - DropERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ); - } - - function test_event_RoleGrantedDefaultAdminRole() public { - bytes32 role = bytes32(0x00); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, deployer, factory); - deployContractProxy( - "DropERC20", - abi.encodeCall( - DropERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ); - } - - function test_event_RoleGrantedTransferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, deployer, factory); - deployContractProxy( - "DropERC20", - abi.encodeCall( - DropERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ); - } - - function test_event_RoleGrantedTransferRoleZeroAddress() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, address(0), factory); - deployContractProxy( - "DropERC20", - abi.encodeCall( - DropERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ); - } - - function test_event_PlatformFeeInfoUpdated() public { - vm.expectEmit(true, false, false, true); - emit PlatformFeeInfoUpdated(platformFeeRecipient, platformFeeBps); - deployContractProxy( - "DropERC20", - abi.encodeCall( - DropERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ); - } - - function test_event_PrimarySaleRecipientUpdated() public { - vm.expectEmit(true, false, false, false); - emit PrimarySaleRecipientUpdated(saleRecipient); - deployContractProxy( - "DropERC20", - abi.encodeCall( - DropERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ); - } - - function test_roleCheck() public { - deployContractProxy( - "DropERC20", - abi.encodeCall( - DropERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ); - - newDropContract = DropERC20(getContract("DropERC20")); - - assertEq(newDropContract.hasRole(bytes32(0x00), deployer), true); - assertEq(newDropContract.hasRole(keccak256("TRANSFER_ROLE"), deployer), true); - assertEq(newDropContract.hasRole(keccak256("TRANSFER_ROLE"), address(0)), true); - } -} diff --git a/src/test/drop/drop-erc20/initialize/intialize.tree b/src/test/drop/drop-erc20/initialize/intialize.tree deleted file mode 100644 index 6731bc81e..000000000 --- a/src/test/drop/drop-erc20/initialize/intialize.tree +++ /dev/null @@ -1,33 +0,0 @@ -function initialize( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _saleRecipient, - address _platformFeeRecipient, - uint128 _platformFeeBps -) -├── when _trustedForwarders.length > 0 -│ └── it should set _trustedForwarder[_trustedForwarders[i]] as true for each address in _trustedForwarders ✅ -├── it should set contractURI as _contractURI ✅ -├── it should emit ContractURIUpdated with the parameters: prevURI, _uri ✅ -├── it should set _defaultAdmin as the owner of the contract ✅ -├── it should emit OwnerUpdated with the parameters: _prevOwner, _defaultAdmin ✅ -├── it should assign the role DEFAULT_ADMIN_ROLE to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: DEFAULT_ADMIN_ROLE, _defaultAdmin, msg.sender ✅ -├── it should assign the role _transferRole to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: _transferRole, _defaultAdmin, msg.sender ✅ -├── it should assign the role _transferRole to address(0) ✅ -├── it should emit RoleGranted with the parameters: _transferRole, address(0), msg.sender ✅ -├── when _platformFeeBps is greater than 10_000 -│ └── it should revert ✅ -├── when _platformFeeBps is less than or equal to 10_000 -│ ├── it should set platformFeeBps to uint16(_platformFeeBps); ✅ -│ ├── it should set platformFeeRecipient to _platformFeeRecipient ✅ -│ └── it should emit PlatformFeeInfoUpdated with the following parameters: _platformFeeRecipient, _platformFeeBps ✅ -├── it should set recipient as _primarySaleRecipient ✅ -├── it should emit PrimarySaleRecipientUpdated with the parameters _primarySaleRecipient ✅ -├── it should set transferRole as keccak256("TRANSFER_ROLE")✅ -├── it should set _name as _name ✅ -└── it should set _symbol as _symbol ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc20/miscellaneous/miscellaneous.t.sol b/src/test/drop/drop-erc20/miscellaneous/miscellaneous.t.sol deleted file mode 100644 index 0845465ff..000000000 --- a/src/test/drop/drop-erc20/miscellaneous/miscellaneous.t.sol +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC20 } from "contracts/prebuilts/drop/DropERC20.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract HarnessDropERC20Misc is DropERC20 { - bytes32 private transferRole = keccak256("TRANSFER_ROLE"); - - function msgData() public view returns (bytes memory) { - return _msgData(); - } - - function transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) public returns (uint256) { - return _transferTokensOnClaim(_to, _quantityBeingClaimed); - } - - function beforeTokenTransfer(address from, address to, uint256 amount) public { - _beforeTokenTransfer(from, to, amount); - } - - function mint(address to, uint256 amount) public { - _mint(to, amount); - } - - function burn(address from, uint256 amount) public { - _burn(from, amount); - } - - function hasTransferRole(address _account) public view returns (bool) { - return hasRole(transferRole, _account); - } -} - -contract DropERC20Test_misc is BaseTest { - address public dropImp; - HarnessDropERC20Misc public proxy; - - function setUp() public override { - super.setUp(); - - bytes memory initializeData = abi.encodeCall( - DropERC20.initialize, - (deployer, NAME, SYMBOL, CONTRACT_URI, forwarders(), saleRecipient, platformFeeRecipient, platformFeeBps) - ); - - dropImp = address(new HarnessDropERC20Misc()); - proxy = HarnessDropERC20Misc(address(new TWProxy(dropImp, initializeData))); - } - - function test_contractType_returnValue() public { - assertEq(proxy.contractType(), "DropERC20"); - } - - function test_contractVersion_returnValue() public { - assertEq(proxy.contractVersion(), uint8(4)); - } - - function test_msgData_returnValue() public { - bytes memory msgData = proxy.msgData(); - bytes4 expectedData = proxy.msgData.selector; - assertEq(bytes4(msgData), expectedData); - } - - function test_state_transferTokensOnClaim() public { - uint256 initialBalance = proxy.balanceOf(deployer); - uint256 quantityBeingClaimed = 1; - proxy.transferTokensOnClaim(deployer, quantityBeingClaimed); - assertEq(proxy.balanceOf(deployer), initialBalance + quantityBeingClaimed); - } - - function test_returnValue_transferTokensOnClaim() public { - uint256 quantityBeingClaimed = 1; - uint256 returnValue = proxy.transferTokensOnClaim(deployer, quantityBeingClaimed); - assertEq(returnValue, 0); - } - - function test_beforeTokenTransfer_revert_addressZeroNoTransferRole() public { - vm.prank(deployer); - proxy.revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.expectRevert("transfers restricted."); - proxy.beforeTokenTransfer(address(0x01), address(0x02), 1); - } - - function test_beforeTokenTransfer_doesNotRevert_addressZeroNoTransferRole_burnMint() public { - vm.prank(deployer); - proxy.revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - proxy.beforeTokenTransfer(address(0), address(0x02), 1); - proxy.beforeTokenTransfer(address(0x01), address(0), 1); - } - - function test_state_mint() public { - uint256 initialBalance = proxy.balanceOf(deployer); - uint256 amount = 1; - proxy.mint(deployer, amount); - assertEq(proxy.balanceOf(deployer), initialBalance + amount); - } - - function test_state_burn() public { - proxy.mint(deployer, 1); - uint256 initialBalance = proxy.balanceOf(deployer); - uint256 amount = 1; - proxy.burn(deployer, amount); - assertEq(proxy.balanceOf(deployer), initialBalance - amount); - } - - function test_transfer_drop() public { - //deal erc20 drop to address(0x1) - deal(address(proxy), address(0x1), 1); - vm.prank(address(0x1)); - proxy.transfer(address(0x2), 1); - assertEq(proxy.balanceOf(address(0x2)), 1); - } -} diff --git a/src/test/drop/drop-erc20/miscellaneous/miscellaneous.tree b/src/test/drop/drop-erc20/miscellaneous/miscellaneous.tree deleted file mode 100644 index 61c35271e..000000000 --- a/src/test/drop/drop-erc20/miscellaneous/miscellaneous.tree +++ /dev/null @@ -1,30 +0,0 @@ -function contractType() -└── it should return bytes32("DropERC20") ✅ - -function contractVersion() -└── it should return uint8(4) ✅ - -function _mint(address account, uint256 amount) -└── it should mint amount tokens to account ✅ - -function _burn(address account, uint256 amount) -└── it should burn amount tokens from account ✅ - -function _afterTokenTransfer( - address from, - address to, - uint256 amount -) -└── it should call _afterTokenTransfer logic from ERC20VotesUpgradeable - -function _msgData() -└── it should return msg.data ✅ - -function _beforeTokenTransfer(address from, address to, uint256 amount) -└── when address(0) does not have transferRole and from does not equal address(0) and from does not equal address(0) - └── when from does not have transfer role and to does not have transferRole - └── it should revert ✅ - -function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) -├── it should mint _quantityBeingClaimed tokens to _to ✅ -└── it should return 0 ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc20/setMaxTotalSupply/setMaxTotalSupply.t.sol b/src/test/drop/drop-erc20/setMaxTotalSupply/setMaxTotalSupply.t.sol deleted file mode 100644 index 8349f808c..000000000 --- a/src/test/drop/drop-erc20/setMaxTotalSupply/setMaxTotalSupply.t.sol +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC20 } from "contracts/prebuilts/drop/DropERC20.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract DropERC20Test_setMaxTotalSupply is BaseTest { - event MaxTotalSupplyUpdated(uint256 maxTotalSupply); - - DropERC20 public drop; - - function setUp() public override { - super.setUp(); - - drop = DropERC20(getContract("DropERC20")); - } - - modifier callerHasDefaultAdminRole() { - vm.startPrank(deployer); - _; - } - - modifier callerDoesNotHaveDefaultAdminRole() { - _; - } - - function test_revert_doesNotHaveAdminRole() public callerDoesNotHaveDefaultAdminRole { - bytes32 role = bytes32(0x00); - - vm.expectRevert( - abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, address(this), role) - ); - drop.setMaxTotalSupply(0); - } - - function test_state_callerHasDefaultAdminRole() public callerHasDefaultAdminRole { - drop.setMaxTotalSupply(100); - assertEq(drop.maxTotalSupply(), 100); - } - - function test_event_callerHasDefaultAdminRole() public callerHasDefaultAdminRole { - vm.expectEmit(false, false, false, true); - emit MaxTotalSupplyUpdated(100); - drop.setMaxTotalSupply(100); - } -} diff --git a/src/test/drop/drop-erc20/setMaxTotalSupply/setMaxTotalSupply.tree b/src/test/drop/drop-erc20/setMaxTotalSupply/setMaxTotalSupply.tree deleted file mode 100644 index a8b23e9ca..000000000 --- a/src/test/drop/drop-erc20/setMaxTotalSupply/setMaxTotalSupply.tree +++ /dev/null @@ -1,6 +0,0 @@ -function setMaxTotalSupply(uint256 _maxTotalSupply) -├── when the caller does not have DEFAULT_ADMIN_ROLE -│ └── it should revert ✅ -└── when the caller does have DEFAULT_ADMIN_ROLE - ├── it should set maxTotalSupply as _maxTotalSupply ✅ - └── it should emit MaxTotalSupplyUpdated with the parameters _maxTotalSupply ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc721/_beforeClaim/_beforeClaim.t.sol b/src/test/drop/drop-erc721/_beforeClaim/_beforeClaim.t.sol deleted file mode 100644 index f30885a51..000000000 --- a/src/test/drop/drop-erc721/_beforeClaim/_beforeClaim.t.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721 } from "contracts/prebuilts/drop/DropERC721.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract DropERC721Test_beforeClaim is BaseTest { - event TokenURIRevealed(uint256 indexed index, string revealedURI); - - DropERC721 public drop; - - bytes private beforeClaim_data; - string private beforeClaim_baseURI; - uint256 private beforeClaim_amount; - address private receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - DropERC721.AllowlistProof private alp; - - function setUp() public override { - super.setUp(); - drop = DropERC721(getContract("DropERC721")); - - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier lazyMintUnEncrypted() { - beforeClaim_amount = 10; - beforeClaim_baseURI = "ipfs://"; - vm.prank(deployer); - drop.lazyMint(beforeClaim_amount, beforeClaim_baseURI, beforeClaim_data); - _; - } - - modifier setMaxSupply() { - vm.prank(deployer); - drop.setMaxTotalSupply(5); - _; - } - - function test_revert_greaterThanNextTokenIdToLazyMint() public lazyMintUnEncrypted { - vm.prank(receiver, receiver); - vm.expectRevert("!Tokens"); - drop.claim(receiver, 11, address(erc20), 0, alp, ""); - } - - function test_revert_greaterThanMaxTotalSupply() public lazyMintUnEncrypted setMaxSupply { - vm.prank(receiver, receiver); - vm.expectRevert("!Supply"); - drop.claim(receiver, 6, address(erc20), 0, alp, ""); - } -} diff --git a/src/test/drop/drop-erc721/_beforeClaim/_beforeClaim.tree b/src/test/drop/drop-erc721/_beforeClaim/_beforeClaim.tree deleted file mode 100644 index a225a8be4..000000000 --- a/src/test/drop/drop-erc721/_beforeClaim/_beforeClaim.tree +++ /dev/null @@ -1,12 +0,0 @@ -function _beforeClaim( - address, - uint256 _quantity, - address, - uint256, - AllowlistProof calldata, - bytes memory -) -├── when _current index + _quantity are greater than nextTokenIdToLazyMint -│ └── it should revert ✅ -└── when maxTotalSupply does not equal zero and _currentIndex + _quantity is greater than maxTotalSupply - └── it should revert ✅ diff --git a/src/test/drop/drop-erc721/_canSetFunctions/_canSetFunctions.t.sol b/src/test/drop/drop-erc721/_canSetFunctions/_canSetFunctions.t.sol deleted file mode 100644 index 61d67d687..000000000 --- a/src/test/drop/drop-erc721/_canSetFunctions/_canSetFunctions.t.sol +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721, PlatformFee, PrimarySale, ContractMetadata, Royalty, LazyMint, Drop, Ownable } from "contracts/prebuilts/drop/DropERC721.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract DropERC721Test_canSetFunctions is BaseTest { - DropERC721 public drop; - - bytes private canset_data; - string private canset_baseURI; - uint256 private canset_amount; - bytes private canset_encryptedURI; - bytes32 private canset_provenanceHash; - address private unauthorized = address(0x123); - - function setUp() public override { - super.setUp(); - drop = DropERC721(getContract("DropERC721")); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier callerNotAdmin() { - vm.startPrank(unauthorized); - _; - } - - modifier callerAdmin() { - vm.startPrank(deployer); - _; - } - - modifier callerNotMinter() { - vm.startPrank(unauthorized); - _; - } - - modifier callerMinter() { - vm.startPrank(deployer); - _; - } - - function test__canSetPlatformFeeInfo_revert_callerNotAdmin() public callerNotAdmin { - vm.expectRevert(abi.encodeWithSelector(PlatformFee.PlatformFeeUnauthorized.selector)); - drop.setPlatformFeeInfo(address(0x1), 1); - } - - function test__canSetPlatformFeeInfo_callerAdmin() public callerAdmin { - drop.setPlatformFeeInfo(address(0x1), 1); - (address recipient, uint16 bps) = drop.getPlatformFeeInfo(); - assertEq(recipient, address(0x1)); - assertEq(bps, 1); - } - - function test__canSetPrimarySaleRecipient_revert_callerNotAdmin() public callerNotAdmin { - vm.expectRevert(abi.encodeWithSelector(PrimarySale.PrimarySaleUnauthorized.selector)); - drop.setPrimarySaleRecipient(address(0x1)); - } - - function test__canSetPrimarySaleRecipient_callerAdmin() public callerAdmin { - drop.setPrimarySaleRecipient(address(0x1)); - assertEq(drop.primarySaleRecipient(), address(0x1)); - } - - function test__canSetOwner_revert_callerNotAdmin() public callerNotAdmin { - vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorized.selector)); - drop.setOwner(address(0x1)); - } - - function test__canSetOwner_callerAdmin() public callerAdmin { - drop.setOwner(address(0x1)); - assertEq(drop.owner(), address(0x1)); - } - - function test__canSetRoyaltyInfo_revert_callerNotAdmin() public callerNotAdmin { - vm.expectRevert(abi.encodeWithSelector(Royalty.RoyaltyUnauthorized.selector)); - drop.setDefaultRoyaltyInfo(address(0x1), 1); - } - - function test__canSetRoyaltyInfo_callerAdmin() public callerAdmin { - drop.setDefaultRoyaltyInfo(address(0x1), 1); - (address recipient, uint16 bps) = drop.getDefaultRoyaltyInfo(); - assertEq(recipient, address(0x1)); - assertEq(bps, 1); - } - - function test__canSetContractURI_revert_callerNotAdmin() public callerNotAdmin { - vm.expectRevert(abi.encodeWithSelector(ContractMetadata.ContractMetadataUnauthorized.selector)); - drop.setContractURI("ipfs://"); - } - - function test__canSetContractURI_callerAdmin() public callerAdmin { - drop.setContractURI("ipfs://"); - assertEq(drop.contractURI(), "ipfs://"); - } - - function test__canSetClaimConditions_revert_callerNotAdmin() public callerNotAdmin { - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = bytes32(0); - conditions[0].pricePerToken = 10; - conditions[0].currency = address(0x111); - vm.expectRevert(abi.encodeWithSelector(Drop.DropUnauthorized.selector)); - drop.setClaimConditions(conditions, true); - } - - function test__canSetClaimConditions_callerAdmin() public callerAdmin { - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = bytes32(0); - conditions[0].pricePerToken = 10; - conditions[0].currency = address(0x111); - drop.setClaimConditions(conditions, true); - } - - function test__canLazyMint_revert_callerNotMinter() public callerNotMinter { - canset_amount = 10; - canset_baseURI = "ipfs://"; - canset_data = abi.encode(canset_encryptedURI, canset_provenanceHash); - vm.expectRevert(abi.encodeWithSelector(LazyMint.LazyMintUnauthorized.selector)); - drop.lazyMint(canset_amount, canset_baseURI, canset_data); - } - - function test__canLazyMint_callerMinter() public callerMinter { - canset_amount = 10; - canset_baseURI = "ipfs://"; - canset_data = abi.encode(canset_encryptedURI, canset_provenanceHash); - drop.lazyMint(canset_amount, canset_baseURI, canset_data); - } -} diff --git a/src/test/drop/drop-erc721/_canSetFunctions/_canSetFunctions.tree b/src/test/drop/drop-erc721/_canSetFunctions/_canSetFunctions.tree deleted file mode 100644 index 72bfbe39a..000000000 --- a/src/test/drop/drop-erc721/_canSetFunctions/_canSetFunctions.tree +++ /dev/null @@ -1,41 +0,0 @@ -function _canSetPlatformFeeInfo() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetPrimarySaleRecipient() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetOwner() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetRoyaltyInfo() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetContractURI() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetClaimConditions() -├── when caller has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when caller does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canLazyMint() -├── when caller has minterRole -│ └── it should return true ✅ -└── when caller does not have minterRole - └── it should return false ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.t.sol b/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.t.sol deleted file mode 100644 index 6b345cf7a..000000000 --- a/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.t.sol +++ /dev/null @@ -1,152 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721 } from "contracts/prebuilts/drop/DropERC721.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract HarnessDropERC721 is DropERC721 { - function collectionPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) public payable { - _collectPriceOnClaim(_primarySaleRecipient, _quantityToClaim, _currency, _pricePerToken); - } -} - -contract DropERC721Test_collectPrice is BaseTest { - address public dropImp; - HarnessDropERC721 public proxy; - - address private collectPrice_saleRecipient = address(0x010); - uint256 private collectPrice_quantityToClaim = 1; - uint256 private collectPrice_pricePerToken; - address private collectPrice_currency; - uint256 private collectPrice_msgValue; - address private defaultFeeRecipient; - - function setUp() public override { - super.setUp(); - - bytes memory initializeData = abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ); - - dropImp = address(new HarnessDropERC721()); - proxy = HarnessDropERC721(address(new TWProxy(dropImp, initializeData))); - defaultFeeRecipient = proxy.DEFAULT_FEE_RECIPIENT(); - } - - modifier pricePerTokenZero() { - collectPrice_pricePerToken = 0; - _; - } - - modifier pricePerTokenNotZero() { - collectPrice_pricePerToken = 1 ether; - _; - } - - modifier msgValueNotZero() { - collectPrice_msgValue = 1 ether; - _; - } - - modifier nativeCurrency() { - collectPrice_currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - _; - } - - modifier erc20Currency() { - collectPrice_currency = address(erc20); - erc20.mint(address(this), 1_000 ether); - _; - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - function test_revert_msgValueNotZero() public nativeCurrency msgValueNotZero pricePerTokenZero { - vm.expectRevert(); - proxy.collectionPriceOnClaim{ value: collectPrice_msgValue }( - collectPrice_saleRecipient, - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - } - - function test_revert_priceValueMismatchNativeCurrency() public nativeCurrency pricePerTokenNotZero { - vm.expectRevert(); - proxy.collectionPriceOnClaim{ value: collectPrice_msgValue }( - collectPrice_saleRecipient, - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - } - - function test_transferNativeCurrency() public nativeCurrency pricePerTokenNotZero msgValueNotZero { - uint256 balanceSaleRecipientBefore = address(saleRecipient).balance; - uint256 platformFeeRecipientBefore = address(platformFeeRecipient).balance; - uint256 defaultFeeRecipientBefore = address(defaultFeeRecipient).balance; - proxy.collectionPriceOnClaim{ value: collectPrice_msgValue }( - saleRecipient, - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - - uint256 balanceSaleRecipientAfter = address(saleRecipient).balance; - uint256 platformFeeRecipientAfter = address(platformFeeRecipient).balance; - uint256 defaultFeeRecipientAfter = address(defaultFeeRecipient).balance; - uint256 defaultPlatformFeeVal = (collectPrice_pricePerToken * 100) / MAX_BPS; - uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; - uint256 expectedSaleRecipientProceed = collectPrice_msgValue - expectedPlatformFee - defaultPlatformFeeVal; - - assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); - assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, defaultPlatformFeeVal); - } - - function test_transferERC20() public erc20Currency pricePerTokenNotZero { - uint256 balanceSaleRecipientBefore = erc20.balanceOf(saleRecipient); - uint256 platformFeeRecipientBefore = erc20.balanceOf(platformFeeRecipient); - uint256 defaultFeeRecipientBefore = erc20.balanceOf(defaultFeeRecipient); - erc20.approve(address(proxy), collectPrice_pricePerToken); - proxy.collectionPriceOnClaim( - saleRecipient, - collectPrice_quantityToClaim, - collectPrice_currency, - collectPrice_pricePerToken - ); - - uint256 balanceSaleRecipientAfter = erc20.balanceOf(saleRecipient); - uint256 platformFeeRecipientAfter = erc20.balanceOf(platformFeeRecipient); - uint256 defaultFeeRecipientAfter = erc20.balanceOf(defaultFeeRecipient); - uint256 defaultPlatformFeeVal = (collectPrice_pricePerToken * 100) / MAX_BPS; - uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; - uint256 expectedSaleRecipientProceed = collectPrice_pricePerToken - expectedPlatformFee - defaultPlatformFeeVal; - - assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); - assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, defaultPlatformFeeVal); - } -} diff --git a/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.tree b/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.tree deleted file mode 100644 index b962e7d4e..000000000 --- a/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.tree +++ /dev/null @@ -1,20 +0,0 @@ -function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken -) -├── when _pricePerToken is equal to zero -│ └── when msg.value does not equal to zero -│ └── it should revert ✅ -└── when _pricePerToken is not equal to zero - └── when _primarySaleRecipient is equal to address(0) - ├── when _currency is native token - │ ├── when msg.value does not equal to totalPrice - │ │ └── it should revert ✅ - │ └── when msg.value does equal to totalPrice - │ ├── it should transfer platformFees to platformFeeRecipient in native token ✅ - │ └── it should transfer totalPrice - platformFees to saleRecipient in native token ✅ - └── when _currency is not native token - ├── it should transfer platformFees to platformFeeRecipient in _currency token ✅ - └── it should transfer totalPrice - platformFees to saleRecipient in _currency token ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc721/_transferTokensOnClaim/_tranferTokensOnClaim.tree b/src/test/drop/drop-erc721/_transferTokensOnClaim/_tranferTokensOnClaim.tree deleted file mode 100644 index 6bf501586..000000000 --- a/src/test/drop/drop-erc721/_transferTokensOnClaim/_tranferTokensOnClaim.tree +++ /dev/null @@ -1,2 +0,0 @@ -function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) -└── it should mint `_quantityBeingClaimed` number of tokens to `to` ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc721/_transferTokensOnClaim/_transferTokensOnClaim.t.sol b/src/test/drop/drop-erc721/_transferTokensOnClaim/_transferTokensOnClaim.t.sol deleted file mode 100644 index 87e98d606..000000000 --- a/src/test/drop/drop-erc721/_transferTokensOnClaim/_transferTokensOnClaim.t.sol +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721 } from "contracts/prebuilts/drop/DropERC721.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "../../../utils/BaseTest.sol"; - -contract HarnessDropERC721 is DropERC721 { - function transferTokensOnClaim(address _to, uint256 _quantityToClaim) public payable { - _transferTokensOnClaim(_to, _quantityToClaim); - } -} - -contract DropERC721Test_transferTokensOnClaim is BaseTest { - address public dropImp; - HarnessDropERC721 public proxy; - - address private transferTokens_receiver; - - ERC20 private nonReceiver; - - function setUp() public override { - super.setUp(); - - bytes memory initializeData = abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ); - - dropImp = address(new HarnessDropERC721()); - proxy = HarnessDropERC721(address(new TWProxy(dropImp, initializeData))); - - nonReceiver = new ERC20("", ""); - } - - modifier transferToEOA() { - transferTokens_receiver = address(0x111); - _; - } - - modifier transferToNonReceiver() { - transferTokens_receiver = address(nonReceiver); - _; - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - function test_revert_transferToNonReceiver() public transferToNonReceiver { - vm.expectRevert(IERC721AUpgradeable.TransferToNonERC721ReceiverImplementer.selector); - proxy.transferTokensOnClaim(transferTokens_receiver, 1); - } - - function test_transferToEOA() public transferToEOA { - uint256 eoaBalanceBefore = proxy.balanceOf(transferTokens_receiver); - uint256 supplyBefore = proxy.totalSupply(); - proxy.transferTokensOnClaim(transferTokens_receiver, 1); - assertEq(proxy.totalSupply(), supplyBefore + 1); - assertEq(proxy.balanceOf(transferTokens_receiver), eoaBalanceBefore + 1); - } -} diff --git a/src/test/drop/drop-erc721/freezeBatchBaseURI/freezeBatchBaseURI.t.sol b/src/test/drop/drop-erc721/freezeBatchBaseURI/freezeBatchBaseURI.t.sol deleted file mode 100644 index 9cb5c23c2..000000000 --- a/src/test/drop/drop-erc721/freezeBatchBaseURI/freezeBatchBaseURI.t.sol +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721, BatchMintMetadata } from "contracts/prebuilts/drop/DropERC721.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract DropERC721Test_freezeBatchBaseURI is BaseTest { - event MetadataFrozen(); - - DropERC721 public drop; - - bytes private freeze_data; - string private freeze_baseURI; - uint256 private freeze_amount; - bytes private freeze_encryptedURI; - bytes32 private freeze_provenanceHash; - string private freeze_revealedURI; - bytes private freeze_key; - address private unauthorized = address(0x123); - - function setUp() public override { - super.setUp(); - drop = DropERC721(getContract("DropERC721")); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier callerWithoutMetadataRole() { - vm.startPrank(unauthorized); - _; - } - - modifier callerWithMetadataRole() { - vm.startPrank(deployer); - _; - } - - modifier lazyMintEncrypted() { - freeze_amount = 10; - freeze_baseURI = "ipfs://"; - freeze_revealedURI = "ipfs://revealed"; - freeze_key = "key"; - freeze_encryptedURI = drop.encryptDecrypt(bytes(freeze_revealedURI), freeze_key); - freeze_provenanceHash = keccak256(abi.encodePacked(freeze_revealedURI, freeze_key, block.chainid)); - freeze_data = abi.encode(freeze_encryptedURI, freeze_provenanceHash); - vm.prank(deployer); - drop.lazyMint(freeze_amount, freeze_baseURI, freeze_data); - _; - } - - modifier lazyMintUnEncryptedEmptyBaseURI() { - freeze_amount = 10; - freeze_baseURI = ""; - vm.prank(deployer); - drop.lazyMint(freeze_amount, freeze_baseURI, freeze_data); - _; - } - - modifier lazyMintUnEncryptedRegularBaseURI() { - freeze_amount = 10; - freeze_baseURI = "ipfs://"; - vm.prank(deployer); - drop.lazyMint(freeze_amount, freeze_baseURI, freeze_data); - _; - } - - function test_revert_NoMetadataRole() public callerWithoutMetadataRole { - bytes32 role = keccak256("METADATA_ROLE"); - vm.expectRevert( - abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, unauthorized, role) - ); - drop.freezeBatchBaseURI(0); - } - - function test_revert_EncryptedBatch() public lazyMintEncrypted callerWithMetadataRole { - vm.expectRevert("Encrypted batch"); - drop.freezeBatchBaseURI(0); - } - - function test_revert_EmptyBaseURI() public lazyMintUnEncryptedEmptyBaseURI callerWithMetadataRole { - vm.expectRevert( - abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidBatchId.selector, drop.getBatchIdAtIndex(0)) - ); - drop.freezeBatchBaseURI(0); - } - - function test_state() public lazyMintUnEncryptedRegularBaseURI callerWithMetadataRole { - uint256 batchId = drop.getBatchIdAtIndex(0); - drop.freezeBatchBaseURI(0); - assertEq(drop.batchFrozen(batchId), true); - } - - function test_event() public lazyMintUnEncryptedRegularBaseURI callerWithMetadataRole { - vm.expectEmit(false, false, false, false); - emit MetadataFrozen(); - drop.freezeBatchBaseURI(0); - } -} diff --git a/src/test/drop/drop-erc721/freezeBatchBaseURI/freezeBatchBaseURI.tree b/src/test/drop/drop-erc721/freezeBatchBaseURI/freezeBatchBaseURI.tree deleted file mode 100644 index 9c29a9a83..000000000 --- a/src/test/drop/drop-erc721/freezeBatchBaseURI/freezeBatchBaseURI.tree +++ /dev/null @@ -1,12 +0,0 @@ -function freezeBatchBaseURI(uint256 _index) -├── when called by a user without the METADATA_ROLE -│ └── it should revert ✅ -└── when called by a user with the METADATA_ROLE - ├── when the batchId for the provided _index is an encrypted batch - │ └── it should revert ✅ - └── when the batchId for the provided _index is not an encrypted batch - ├── when the baseURI for the batchId is empty - │ └── it should revert ✅ - └── when the baseURI for the batchId is not empty - ├── it should set batchFrozen[batchId] as true ✅ - └── it should emit MetadataFrozen ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc721/initalizer/initializer.t.sol b/src/test/drop/drop-erc721/initalizer/initializer.t.sol deleted file mode 100644 index 5ef6ba8f1..000000000 --- a/src/test/drop/drop-erc721/initalizer/initializer.t.sol +++ /dev/null @@ -1,407 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721, PlatformFee, Royalty } from "contracts/prebuilts/drop/DropERC721.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract DropERC721Test_initializer is BaseTest { - DropERC721 public newDropContract; - - event ContractURIUpdated(string prevURI, string newURI); - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); - event PrimarySaleRecipientUpdated(address indexed recipient); - - function setUp() public override { - super.setUp(); - } - - modifier royaltyBPSTooHigh() { - uint128 royaltyBps = 10001; - _; - } - - modifier platformFeeBPSTooHigh() { - uint128 platformFeeBps = 10001; - _; - } - - function test_state() public { - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - - newDropContract = DropERC721(getContract("DropERC721")); - (address _platformFeeRecipient, uint128 _platformFeeBps) = newDropContract.getPlatformFeeInfo(); - (address _royaltyRecipient, uint128 _royaltyBps) = newDropContract.getDefaultRoyaltyInfo(); - address _saleRecipient = newDropContract.primarySaleRecipient(); - - for (uint256 i = 0; i < forwarders().length; i++) { - assertEq(newDropContract.isTrustedForwarder(forwarders()[i]), true); - } - - assertEq(newDropContract.name(), NAME); - assertEq(newDropContract.symbol(), SYMBOL); - assertEq(newDropContract.contractURI(), CONTRACT_URI); - assertEq(newDropContract.owner(), deployer); - assertEq(_platformFeeRecipient, platformFeeRecipient); - assertEq(_platformFeeBps, platformFeeBps); - assertEq(_royaltyRecipient, royaltyRecipient); - assertEq(_royaltyBps, royaltyBps); - assertEq(_saleRecipient, saleRecipient); - } - - function test_revert_RoyaltyBPSTooHigh() public royaltyBPSTooHigh { - vm.expectRevert(abi.encodeWithSelector(Royalty.RoyaltyExceededMaxFeeBps.selector, 10_000, 10_001)); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - 10001, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_revert_PlatformFeeBPSTooHigh() public platformFeeBPSTooHigh { - vm.expectRevert(abi.encodeWithSelector(PlatformFee.PlatformFeeExceededMaxFeeBps.selector, 10_000, 10_001)); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - 10001, - platformFeeRecipient - ) - ) - ); - } - - function test_event_ContractURIUpdated() public { - vm.expectEmit(false, false, false, true); - emit ContractURIUpdated("", CONTRACT_URI); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_OwnerUpdated() public { - vm.expectEmit(true, true, false, false); - emit OwnerUpdated(address(0), deployer); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_RoleGrantedDefaultAdminRole() public { - bytes32 role = bytes32(0x00); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, deployer, factory); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_RoleGrantedMinterRole() public { - bytes32 role = keccak256("MINTER_ROLE"); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, deployer, factory); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_RoleGrantedTransferRole() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, deployer, factory); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_RoleGrantedTransferRoleZeroAddress() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, address(0), factory); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_RoleGrantedMetadataRole() public { - bytes32 role = keccak256("METADATA_ROLE"); - vm.expectEmit(true, true, true, false); - emit RoleGranted(role, deployer, factory); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_RoleAdminChangedMetadataRole() public { - bytes32 role = keccak256("METADATA_ROLE"); - vm.expectEmit(true, true, true, false); - emit RoleAdminChanged(role, bytes32(0x00), role); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_PlatformFeeInfoUpdated() public { - vm.expectEmit(true, false, false, true); - emit PlatformFeeInfoUpdated(platformFeeRecipient, platformFeeBps); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_DefaultRoyalty() public { - vm.expectEmit(true, false, false, true); - emit DefaultRoyalty(royaltyRecipient, royaltyBps); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_event_PrimarySaleRecipientUpdated() public { - vm.expectEmit(true, false, false, false); - emit PrimarySaleRecipientUpdated(saleRecipient); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - } - - function test_roleCheck() public { - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - - newDropContract = DropERC721(getContract("DropERC721")); - - assertEq(newDropContract.hasRole(bytes32(0x00), deployer), true); - assertEq(newDropContract.hasRole(keccak256("MINTER_ROLE"), deployer), true); - assertEq(newDropContract.hasRole(keccak256("TRANSFER_ROLE"), deployer), true); - assertEq(newDropContract.hasRole(keccak256("TRANSFER_ROLE"), address(0)), true); - assertEq(newDropContract.hasRole(keccak256("METADATA_ROLE"), deployer), true); - - assertEq(newDropContract.getRoleAdmin(keccak256("METADATA_ROLE")), keccak256("METADATA_ROLE")); - } -} diff --git a/src/test/drop/drop-erc721/initalizer/initializer.tree b/src/test/drop/drop-erc721/initalizer/initializer.tree deleted file mode 100644 index cec2fc97d..000000000 --- a/src/test/drop/drop-erc721/initalizer/initializer.tree +++ /dev/null @@ -1,53 +0,0 @@ -function initialize( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _saleRecipient, - address _royaltyRecipient, - uint128 _royaltyBps, - uint128 _platformFeeBps, - address _platformFeeRecipient -) -├── when _trustedForwarders.length > 0 -│ └── it should set _trustedForwarder[_trustedForwarders[i]] as true for each address in _trustedForwarders ✅ -├── it should set _name as the value provided in _name ✅ -├── it should set _symbol as the value provided in _symbol ✅ -├── it should set _currentIndex as 0 -├── it should set contractURI as _contractURI ✅ -├── it should emit ContractURIUpdated with the parameters: prevURI, _uri ✅ -├── it should set _defaultAdmin as the owner of the contract ✅ -├── it should emit OwnerUpdated with the parameters: _prevOwner, _defaultAdmin ✅ -├── it should assign the role DEFAULT_ADMIN_ROLE to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: DEFAULT_ADMIN_ROLE, _defaultAdmin, msg.sender ✅ -├── it should assign the role _minterRole to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: _minterRole, _defaultAdmin, msg.sender ✅ -├── it should assign the role _transferRole to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: _transferRole, _defaultAdmin, msg.sender ✅ -├── it should assign the role _transferRole to address(0) ✅ -├── it should emit RoleGranted with the parameters: _transferRole, address(0), msg.sender ✅ -├── it should assign the role _metadataRole to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: _metadataRole, _defaultAdmin, msg.sender ✅ -├── it should set _getAdminRole[_metadataRole] to equal _metadataRole ✅ -├── it should emit RoleAdminChanged with the parameters _metadataRole, previousAdminRole, _metadataRole ✅ -├── when _platformFeeBps is greater than 10_000 -│ └── it should revert ✅ -├── when _platformFeeBps is less than or equal to 10_000 -│ ├── it should set platformFeeBps to uint16(_platformFeeBps) ✅ -│ ├── it should set platformFeeRecipient to _platformFeeRecipient ✅ -│ └── it should emit PlatformFeeInfoUpdated with the following parameters: _platformFeeRecipient, _platformFeeBps ✅ -├── when _royaltyBps is greater than 10_000 -│ └── it should revert ✅ -├── when _royaltyBps is less than or equal to 10_000 -│ ├── it should set royaltyRecipient as _royaltyRecipient ✅ -│ ├── it should set royaltyBps as uint16(_royaltyBps) ✅ -│ └── it should emit DefaultRoyalty with the parameters _royaltyRecipient, _royaltyBps ✅ -├── it should set recipient as _primarySaleRecipient ✅ -├── it should emit PrimarySaleRecipientUpdated with the parameters _primarySaleRecipient ✅ -├── it should set transferRole as keccak256("TRANSFER_ROLE") -├── it should set minterRole as keccak256("MINTER_ROLE") -└── it should set metadataRole as keccak256("METADATA_ROLE") - - - diff --git a/src/test/drop/drop-erc721/lazyMint/lazyMint.t.sol b/src/test/drop/drop-erc721/lazyMint/lazyMint.t.sol deleted file mode 100644 index 921976be7..000000000 --- a/src/test/drop/drop-erc721/lazyMint/lazyMint.t.sol +++ /dev/null @@ -1,219 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721, LazyMint } from "contracts/prebuilts/drop/DropERC721.sol"; - -// Test imports -import "../../../utils/BaseTest.sol"; - -contract DropERC721Test_lazyMint is BaseTest { - event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI); - - DropERC721 public drop; - - bytes private lazymint_data; - uint256 private lazyMint_amount; - bytes private lazyMint_encryptedURI; - bytes32 private lazyMint_provenanceHash; - string private lazyMint_revealedURI = "test"; - - function setUp() public override { - super.setUp(); - drop = DropERC721(getContract("DropERC721")); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier callerWithoutMinterRole() { - vm.startPrank(address(0x123)); - _; - } - - modifier callerWithMinterRole() { - vm.startPrank(deployer); - _; - } - - modifier amountEqualZero() { - lazyMint_amount = 0; - _; - } - - modifier amountNotEqualZero() { - lazyMint_amount = 1; - _; - } - - modifier dataLengthZero() { - lazymint_data = abi.encode(""); - _; - } - - modifier dataInvalidFormat() { - lazyMint_provenanceHash = bytes32("provenanceHash"); - lazymint_data = abi.encode(lazyMint_provenanceHash); - console.log(lazymint_data.length); - _; - } - - modifier dataValidFormat() { - lazyMint_provenanceHash = bytes32("provenanceHash"); - lazyMint_encryptedURI = "encryptedURI"; - lazymint_data = abi.encode(lazyMint_encryptedURI, lazyMint_provenanceHash); - console.log(lazymint_data.length); - _; - } - - modifier dataValidFormatNoURI() { - lazyMint_provenanceHash = bytes32("provenanceHash"); - lazyMint_encryptedURI = ""; - lazymint_data = abi.encode(lazyMint_encryptedURI, lazyMint_provenanceHash); - console.log(lazymint_data.length); - _; - } - - modifier dataValidFormatNoHash() { - lazyMint_provenanceHash = bytes32(""); - lazyMint_encryptedURI = "encryptedURI"; - lazymint_data = abi.encode(lazyMint_encryptedURI, lazyMint_provenanceHash); - console.log(lazymint_data.length); - _; - } - - function test_revert_NoMinterRole() public callerWithoutMinterRole dataLengthZero { - vm.expectRevert(abi.encodeWithSelector(LazyMint.LazyMintUnauthorized.selector)); - drop.lazyMint(lazyMint_amount, lazyMint_revealedURI, lazymint_data); - } - - function test_revert_AmountEqualZero() public callerWithMinterRole dataLengthZero amountEqualZero { - vm.expectRevert(abi.encodeWithSelector(LazyMint.LazyMintInvalidAmount.selector)); - drop.lazyMint(lazyMint_amount, lazyMint_revealedURI, lazymint_data); - } - - function test_revert_DataInvalidFormat() public callerWithMinterRole amountNotEqualZero dataInvalidFormat { - vm.expectRevert(); - drop.lazyMint(lazyMint_amount, lazyMint_revealedURI, lazymint_data); - } - - function test_state_dataLengthZero() public callerWithMinterRole amountNotEqualZero dataLengthZero { - uint256 nextTokenIdToLazyMintBefore = drop.nextTokenIdToMint(); - uint256 expectedBatchId = nextTokenIdToLazyMintBefore + lazyMint_amount; - - uint256 batchIdReturn = drop.lazyMint(lazyMint_amount, lazyMint_revealedURI, lazymint_data); - - uint256 batchIdState = drop.getBatchIdAtIndex(0); - string memory baseURIState = drop.tokenURI(0); - - assertEq(nextTokenIdToLazyMintBefore + lazyMint_amount, drop.nextTokenIdToMint()); - assertEq(expectedBatchId, batchIdReturn); - assertEq(expectedBatchId, batchIdState); - assertEq(string(abi.encodePacked(lazyMint_revealedURI, "0")), baseURIState); - } - - function test_event_dataLengthZero() public callerWithMinterRole amountNotEqualZero dataLengthZero { - uint256 nextTokenIdToLazyMintBefore = drop.nextTokenIdToMint(); - - vm.expectEmit(true, false, false, true); - emit TokensLazyMinted( - nextTokenIdToLazyMintBefore, - nextTokenIdToLazyMintBefore + lazyMint_amount - 1, - lazyMint_revealedURI, - lazymint_data - ); - drop.lazyMint(lazyMint_amount, lazyMint_revealedURI, lazymint_data); - } - - function test_state_noEncryptedURI() public callerWithMinterRole amountNotEqualZero dataValidFormatNoURI { - uint256 nextTokenIdToLazyMintBefore = drop.nextTokenIdToMint(); - uint256 expectedBatchId = nextTokenIdToLazyMintBefore + lazyMint_amount; - bytes memory expectedEncryptedData; - - uint256 batchIdReturn = drop.lazyMint(lazyMint_amount, lazyMint_revealedURI, lazymint_data); - - uint256 batchIdState = drop.getBatchIdAtIndex(0); - string memory baseURIState = drop.tokenURI(0); - bytes memory encryptedDataState = drop.encryptedData(0); - - assertEq(nextTokenIdToLazyMintBefore + lazyMint_amount, drop.nextTokenIdToMint()); - assertEq(expectedBatchId, batchIdReturn); - assertEq(expectedBatchId, batchIdState); - assertEq(string(abi.encodePacked(lazyMint_revealedURI, "0")), baseURIState); - assertEq(expectedEncryptedData, encryptedDataState); - } - - function test_event_noEncryptedURI() public callerWithMinterRole amountNotEqualZero dataValidFormatNoURI { - uint256 nextTokenIdToLazyMintBefore = drop.nextTokenIdToMint(); - - vm.expectEmit(true, false, false, true); - emit TokensLazyMinted( - nextTokenIdToLazyMintBefore, - nextTokenIdToLazyMintBefore + lazyMint_amount - 1, - lazyMint_revealedURI, - lazymint_data - ); - drop.lazyMint(lazyMint_amount, lazyMint_revealedURI, lazymint_data); - } - - function test_state_noProvenanceHash() public callerWithMinterRole amountNotEqualZero dataValidFormatNoHash { - uint256 nextTokenIdToLazyMintBefore = drop.nextTokenIdToMint(); - uint256 expectedBatchId = nextTokenIdToLazyMintBefore + lazyMint_amount; - bytes memory expectedEncryptedData; - - uint256 batchIdReturn = drop.lazyMint(lazyMint_amount, lazyMint_revealedURI, lazymint_data); - - uint256 batchIdState = drop.getBatchIdAtIndex(0); - string memory baseURIState = drop.tokenURI(0); - bytes memory encryptedDataState = drop.encryptedData(0); - - assertEq(nextTokenIdToLazyMintBefore + lazyMint_amount, drop.nextTokenIdToMint()); - assertEq(expectedBatchId, batchIdReturn); - assertEq(expectedBatchId, batchIdState); - assertEq(string(abi.encodePacked(lazyMint_revealedURI, "0")), baseURIState); - assertEq(expectedEncryptedData, encryptedDataState); - } - - function test_event_noProvenanceHash() public callerWithMinterRole amountNotEqualZero dataValidFormatNoHash { - uint256 nextTokenIdToLazyMintBefore = drop.nextTokenIdToMint(); - - vm.expectEmit(true, false, false, true); - emit TokensLazyMinted( - nextTokenIdToLazyMintBefore, - nextTokenIdToLazyMintBefore + lazyMint_amount - 1, - lazyMint_revealedURI, - lazymint_data - ); - drop.lazyMint(lazyMint_amount, lazyMint_revealedURI, lazymint_data); - } - - function test_state_encryptedURIAndHash() public callerWithMinterRole amountNotEqualZero dataValidFormat { - uint256 nextTokenIdToLazyMintBefore = drop.nextTokenIdToMint(); - uint256 expectedBatchId = nextTokenIdToLazyMintBefore + lazyMint_amount; - - uint256 batchIdReturn = drop.lazyMint(lazyMint_amount, lazyMint_revealedURI, lazymint_data); - - uint256 batchIdState = drop.getBatchIdAtIndex(0); - string memory baseURIState = drop.tokenURI(0); - bytes memory encryptedDataState = drop.encryptedData(batchIdReturn); - - assertEq(nextTokenIdToLazyMintBefore + lazyMint_amount, drop.nextTokenIdToMint()); - assertEq(expectedBatchId, batchIdReturn); - assertEq(expectedBatchId, batchIdState); - assertEq(string(abi.encodePacked(lazyMint_revealedURI, "0")), baseURIState); - assertEq(lazymint_data, encryptedDataState); - } - - function test_event_encryptedURIAndHash() public callerWithMinterRole amountNotEqualZero dataValidFormat { - uint256 nextTokenIdToLazyMintBefore = drop.nextTokenIdToMint(); - - vm.expectEmit(true, false, false, true); - emit TokensLazyMinted( - nextTokenIdToLazyMintBefore, - nextTokenIdToLazyMintBefore + lazyMint_amount - 1, - lazyMint_revealedURI, - lazymint_data - ); - drop.lazyMint(lazyMint_amount, lazyMint_revealedURI, lazymint_data); - } -} diff --git a/src/test/drop/drop-erc721/lazyMint/lazyMint.tree b/src/test/drop/drop-erc721/lazyMint/lazyMint.tree deleted file mode 100644 index 84738d926..000000000 --- a/src/test/drop/drop-erc721/lazyMint/lazyMint.tree +++ /dev/null @@ -1,33 +0,0 @@ -function lazyMint( - uint256 _amount, - string calldata _baseURIForTokens, - bytes calldata _data -) -├── when called by a user without MINTER_ROLE -│ └── it should revert ✅ -└── when called by a user with MINTER_ROLE - ├── when _data.length == 0 - │ ├── when _amount is equal to 0 - │ │ └── it should revert ✅ - │ └── when _amount is greater than 0 - │ ├── it should push batchId (_startId + _amountToMint) to the batchIds array ✅ - │ ├── it should set baseURI[batchId] as _baseURIForTokens ✅ - │ └── it should emit TokensLazyMinted with the parameters: startId, startId + amount - 1, _baseURIForTokens, _data ✅ - └── when _data.length > 0 - ├── when _data invalid format - │ └── it should revert ✅ - └── when _data valid format - ├── it should decode _data into bytes memory encryptedURI and bytes32 provenanceHash ✅ - ├── when encryptedURI.length = 0 - │ ├── it should push batchId (_startId + _amountToMint) to the batchIds array ✅ - │ ├── it should set baseURI[batchId] as _baseURIForTokens ✅ - │ └── it should emit TokensLazyMinted with the parameters: startId, startId + amount - 1, _baseURIForTokens, _data ✅ - ├── when provenanceHash = "" - │ ├── it should push batchId (_startId + _amountToMint) to the batchIds array ✅ - │ ├── it should set baseURI[batchId] as _baseURIForTokens ✅ - │ └── it should emit TokensLazyMinted with the parameters: startId, startId + amount - 1, _baseURIForTokens, _data ✅ - └── when encryptedURI.length > 0 and provenanceHash does not equal "" - ├── it should set the encryptedData[batchId] equal to _data ✅ - ├── it should push batchId (_startId + _amountToMint) to the batchIds array ✅ - ├── it should set baseURI[batchId] as _baseURIForTokens ✅ - └── it should emit TokensLazyMinted with the parameters: startId, startId + amount - 1, _baseURIForTokens, _data ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc721/miscellaneous/miscellaneous.t.sol b/src/test/drop/drop-erc721/miscellaneous/miscellaneous.t.sol deleted file mode 100644 index 9ec52d8f8..000000000 --- a/src/test/drop/drop-erc721/miscellaneous/miscellaneous.t.sol +++ /dev/null @@ -1,195 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721 } from "contracts/prebuilts/drop/DropERC721.sol"; - -// Test imports -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "../../../utils/BaseTest.sol"; -import "../../../../../lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC2981Upgradeable.sol"; - -contract DropERC721Test_misc is BaseTest { - DropERC721 public drop; - - bytes private misc_data; - string private misc_baseURI; - uint256 private misc_amount; - bytes private misc_encryptedURI; - bytes32 private misc_provenanceHash; - string private misc_revealedURI; - uint256 private misc_index; - bytes private misc_key; - address private unauthorized = address(0x123); - - function setUp() public override { - super.setUp(); - drop = DropERC721(getContract("DropERC721")); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier callerNotApproved() { - vm.startPrank(unauthorized); - _; - } - - modifier callerOwner() { - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - vm.startPrank(receiver); - _; - } - - modifier callerApproved() { - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - vm.prank(receiver); - drop.setApprovalForAll(deployer, true); - vm.startPrank(deployer); - _; - } - - modifier validIndex() { - misc_index = 0; - _; - } - - modifier invalidKey() { - misc_key = "invalidKey"; - _; - } - - modifier lazyMintEncrypted() { - misc_amount = 10; - misc_baseURI = "ipfs://"; - misc_revealedURI = "ipfs://revealed"; - misc_key = "key"; - misc_encryptedURI = drop.encryptDecrypt(bytes(misc_revealedURI), misc_key); - misc_provenanceHash = keccak256(abi.encodePacked(misc_revealedURI, misc_key, block.chainid)); - misc_data = abi.encode(misc_encryptedURI, misc_provenanceHash); - vm.prank(deployer); - drop.lazyMint(misc_amount, misc_baseURI, misc_data); - _; - } - - modifier tokenClaimed() { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - DropERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist - - DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - drop.setClaimConditions(conditions, false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - drop.claim(receiver, 10, address(erc20), 0, alp, ""); // claims for free, because allowlist price is 0 - _; - } - - function test_totalMinted_TenLazyMintedZeroClaim() public lazyMintEncrypted { - uint256 totalMinted = drop.totalMinted(); - assertEq(totalMinted, 0); - } - - function test_totalMinted_TenLazyMintedTenClaim() public lazyMintEncrypted tokenClaimed { - uint256 totalMinted = drop.totalMinted(); - assertEq(totalMinted, 10); - } - - function test_nextTokenIdToMint_ZeroLazyMinted() public { - uint256 nextTokenIdToMint = drop.nextTokenIdToMint(); - assertEq(nextTokenIdToMint, 0); - } - - function test_nextTokenIdToMint_TenLazyMinted() public lazyMintEncrypted { - uint256 nextTokenIdToMint = drop.nextTokenIdToMint(); - assertEq(nextTokenIdToMint, 10); - } - - function test_nextTokenIdToClaim_ZeroClaimed() public { - uint256 nextTokenIdToClaim = drop.nextTokenIdToClaim(); - assertEq(nextTokenIdToClaim, 0); - } - - function test_nextTokenIdToClaim_TenClaimed() public lazyMintEncrypted tokenClaimed { - uint256 nextTokenIdToClaim = drop.nextTokenIdToClaim(); - assertEq(nextTokenIdToClaim, 10); - } - - function test_burn_revert_callerNotApproved() public lazyMintEncrypted tokenClaimed callerNotApproved { - vm.expectRevert(IERC721AUpgradeable.TransferCallerNotOwnerNorApproved.selector); - drop.burn(0); - } - - function test_burn_CallerApproved() public lazyMintEncrypted tokenClaimed callerApproved { - drop.burn(0); - uint256 totalSupply = drop.totalSupply(); - assertEq(totalSupply, 9); - vm.expectRevert(IERC721AUpgradeable.OwnerQueryForNonexistentToken.selector); - drop.ownerOf(0); - } - - function test_burn_revert_callerOwnerOfToken() public lazyMintEncrypted tokenClaimed callerOwner { - drop.burn(0); - uint256 totalSupply = drop.totalSupply(); - assertEq(totalSupply, 9); - vm.expectRevert(IERC721AUpgradeable.OwnerQueryForNonexistentToken.selector); - drop.ownerOf(0); - } - - function test_contractType() public { - assertEq(drop.contractType(), bytes32("DropERC721")); - } - - function test_contractVersion() public { - assertEq(drop.contractVersion(), uint8(4)); - } - - function test_supportsInterface() public { - assertEq(drop.supportsInterface(type(IERC2981Upgradeable).interfaceId), true); - assertEq(drop.supportsInterface(type(IERC721Upgradeable).interfaceId), true); - assertEq(drop.supportsInterface(type(IERC721MetadataUpgradeable).interfaceId), true); - } - - function test__msgData() public { - HarnessDropERC721MsgData msgDataDrop = new HarnessDropERC721MsgData(); - bytes memory msgData = msgDataDrop.msgData(); - bytes4 expectedData = msgDataDrop.msgData.selector; - assertEq(bytes4(msgData), expectedData); - } -} - -contract HarnessDropERC721MsgData is DropERC721 { - function msgData() public view returns (bytes memory) { - return _msgData(); - } -} diff --git a/src/test/drop/drop-erc721/miscellaneous/miscellaneous.tree b/src/test/drop/drop-erc721/miscellaneous/miscellaneous.tree deleted file mode 100644 index 0e15c80ad..000000000 --- a/src/test/drop/drop-erc721/miscellaneous/miscellaneous.tree +++ /dev/null @@ -1,23 +0,0 @@ -function totalMinted() -└── it should return total number of minted tokens ✅ - -function nextTokenIdToMint() -└── it should return the next tokenId that is to be lazy minted ✅ - -function nextTokenIdToClaim() -└── it should return the next tokenId to be minted ✅ - -function burn(uint256 tokenId) -├── when caller is not the owner of tokenId -│ ├── when caller is not an approved operator of the owner of tokenId -│ │ └── it should revert ✅ -│ └── when caller is an approved operator of the owner of tokenId -│ └── it should burn the token ✅ -└── when caller is the owner of tokenId - └── it should burn the token ✅ - -function contractType() -└── it should return "DropERC721" in bytes32 format ✅ - -function contractVersion() -└── it should return 4 in uint8 format ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc721/reveal/reveal.t.sol b/src/test/drop/drop-erc721/reveal/reveal.t.sol deleted file mode 100644 index 998c7ee27..000000000 --- a/src/test/drop/drop-erc721/reveal/reveal.t.sol +++ /dev/null @@ -1,137 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721, BatchMintMetadata, DelayedReveal } from "contracts/prebuilts/drop/DropERC721.sol"; - -// Test imports - -import "../../../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract DropERC721Test_reveal is BaseTest { - using Strings for uint256; - - event TokenURIRevealed(uint256 indexed index, string revealedURI); - - DropERC721 public drop; - - bytes private reveal_data; - string private reveal_baseURI; - uint256 private reveal_amount; - bytes private reveal_encryptedURI; - bytes32 private reveal_provenanceHash; - string private reveal_revealedURI; - uint256 private reveal_index; - bytes private reveal_key; - address private unauthorized = address(0x123); - - function setUp() public override { - super.setUp(); - drop = DropERC721(getContract("DropERC721")); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier callerWithoutMetadataRole() { - vm.startPrank(unauthorized); - _; - } - - modifier callerWithMetadataRole() { - vm.startPrank(deployer); - _; - } - - modifier validIndex() { - reveal_index = 0; - _; - } - - modifier invalidKey() { - reveal_key = "invalidKey"; - _; - } - - modifier invalidIndex() { - reveal_index = 1; - _; - } - - modifier lazyMintEncrypted() { - reveal_amount = 10; - reveal_baseURI = "ipfs://"; - reveal_revealedURI = "ipfs://revealed"; - reveal_key = "key"; - reveal_encryptedURI = drop.encryptDecrypt(bytes(reveal_revealedURI), reveal_key); - reveal_provenanceHash = keccak256(abi.encodePacked(reveal_revealedURI, reveal_key, block.chainid)); - reveal_data = abi.encode(reveal_encryptedURI, reveal_provenanceHash); - vm.prank(deployer); - drop.lazyMint(reveal_amount, reveal_baseURI, reveal_data); - _; - } - - modifier lazyMintUnEncrypted() { - reveal_amount = 10; - reveal_baseURI = "ipfs://"; - vm.prank(deployer); - drop.lazyMint(reveal_amount, reveal_baseURI, reveal_data); - _; - } - - function test_revert_NoMetadataRole() public callerWithoutMetadataRole { - bytes32 role = keccak256("METADATA_ROLE"); - - vm.expectRevert( - abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, unauthorized, role) - ); - drop.reveal(reveal_index, reveal_key); - } - - function test_state() public validIndex lazyMintEncrypted callerWithMetadataRole { - for (uint256 i = 0; i < reveal_amount; i += 1) { - string memory uri = drop.tokenURI(i); - assertEq(uri, string(abi.encodePacked(reveal_baseURI, "0"))); - } - - string memory revealedURI = drop.reveal(reveal_index, reveal_key); - assertEq(revealedURI, string(reveal_revealedURI)); - - for (uint256 i = 0; i < reveal_amount; i += 1) { - string memory uri = drop.tokenURI(i); - assertEq(uri, string(abi.encodePacked(reveal_revealedURI, i.toString()))); - } - - assertEq(drop.encryptedData(reveal_index), ""); - } - - function test_event() public validIndex lazyMintEncrypted callerWithMetadataRole { - vm.expectEmit(); - emit TokenURIRevealed(reveal_index, reveal_revealedURI); - drop.reveal(reveal_index, reveal_key); - } - - function test_revert_InvalidIndex() public invalidIndex lazyMintEncrypted callerWithMetadataRole { - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidBatchId.selector, reveal_index)); - drop.reveal(reveal_index, reveal_key); - } - - function test_revert_InvalidKey() public validIndex lazyMintEncrypted invalidKey callerWithMetadataRole { - string memory incorrectURI = string(drop.encryptDecrypt(reveal_encryptedURI, reveal_key)); - - vm.expectRevert( - abi.encodeWithSelector( - DelayedReveal.DelayedRevealIncorrectResultHash.selector, - reveal_provenanceHash, - keccak256(abi.encodePacked(incorrectURI, reveal_key, block.chainid)) - ) - ); - drop.reveal(reveal_index, reveal_key); - } - - function test_revert_NoEncryptedData() public validIndex lazyMintUnEncrypted callerWithMetadataRole { - vm.expectRevert(abi.encodeWithSelector(DelayedReveal.DelayedRevealNothingToReveal.selector)); - drop.reveal(reveal_index, reveal_key); - } -} diff --git a/src/test/drop/drop-erc721/reveal/reveal.tree b/src/test/drop/drop-erc721/reveal/reveal.tree deleted file mode 100644 index 5e1e6717d..000000000 --- a/src/test/drop/drop-erc721/reveal/reveal.tree +++ /dev/null @@ -1,16 +0,0 @@ -function reveal(uint256 _index, bytes calldata _key) -├── when called by a user without the METADATA_ROLE -│ └── it should revert ✅ -└── when called by a user with the METADATA_ROLE - ├── when called with an invalid index - │ └── it should revert ✅ - └── when called with a valid index - ├── when called on a batch with no encryptedData - │ └── it should revert ✅ - └── when called a batch with encryptedData - ├── when called with an invalid key - │ └── it should revert ✅ - └── when called with a valid key - ├── it should set encryptedData[(batchId of _index)] as a blank string ("") ✅ - ├── it should set the baseURI[(batchId of _index)] as the revealed uri ✅ - └── it should emit TokenURIRevealed with the following parameters: _index, revealed uri ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc721/setMaxTotalSupply/setMaxTotalSupply.t.sol b/src/test/drop/drop-erc721/setMaxTotalSupply/setMaxTotalSupply.t.sol deleted file mode 100644 index 592fa0fac..000000000 --- a/src/test/drop/drop-erc721/setMaxTotalSupply/setMaxTotalSupply.t.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721 } from "contracts/prebuilts/drop/DropERC721.sol"; - -// Test imports - -import "../../../utils/BaseTest.sol"; - -contract DropERC721Test_setMaxTotalSupply is BaseTest { - event MaxTotalSupplyUpdated(uint256 maxTotalSupply); - - DropERC721 public drop; - - address private unauthorized = address(0x123); - - function setUp() public override { - super.setUp(); - drop = DropERC721(getContract("DropERC721")); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier callerNotAdmin() { - vm.startPrank(unauthorized); - _; - } - - modifier callerAdmin() { - vm.startPrank(deployer); - _; - } - - function test_revert_CallerNotAdmin() public callerNotAdmin { - bytes32 role = bytes32(0x00); - vm.expectRevert( - abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, unauthorized, role) - ); - drop.setMaxTotalSupply(0); - } - - function test_state() public callerAdmin { - drop.setMaxTotalSupply(0); - assertEq(drop.maxTotalSupply(), 0); - } - - function test_event() public callerAdmin { - vm.expectEmit(false, false, false, false); - emit MaxTotalSupplyUpdated(0); - drop.setMaxTotalSupply(0); - } -} diff --git a/src/test/drop/drop-erc721/setMaxTotalSupply/setMaxTotalSupply.tree b/src/test/drop/drop-erc721/setMaxTotalSupply/setMaxTotalSupply.tree deleted file mode 100644 index 19631c92f..000000000 --- a/src/test/drop/drop-erc721/setMaxTotalSupply/setMaxTotalSupply.tree +++ /dev/null @@ -1,6 +0,0 @@ -function setMaxTotalSupply(uint256 _maxTotalSupply) -├── when called by a user without the DEFAULT_ADMIN_ROLE -│ └── it should revert ✅ -└── when called by a user with the DEFAULT_ADMIN_ROLE - ├── it should set maxTotalSupply to _maxTotalSupply ✅ - └── it should emit MaxTotalSupplyUpdated with the following parameters: _maxTotalSupply ✅ \ No newline at end of file diff --git a/src/test/drop/drop-erc721/updateBatchBaseURI/updateBatchBaseURI.t.sol b/src/test/drop/drop-erc721/updateBatchBaseURI/updateBatchBaseURI.t.sol deleted file mode 100644 index 901d7a7b1..000000000 --- a/src/test/drop/drop-erc721/updateBatchBaseURI/updateBatchBaseURI.t.sol +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { DropERC721, BatchMintMetadata, Permissions } from "contracts/prebuilts/drop/DropERC721.sol"; - -// Test imports - -import "../../../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract DropERC721Test_updateBatchBaseURI is BaseTest { - using Strings for uint256; - - event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); - - DropERC721 public drop; - - bytes private updateBatch_data; - string private updateBatch_baseURI; - string private updateBatch_newBaseURI; - uint256 private updateBatch_amount; - bytes private updateBatch_encryptedURI; - bytes32 private updateBatch_provenanceHash; - string private updateBatch_revealedURI; - bytes private updateBatch_key; - address private unauthorized = address(0x123); - - function setUp() public override { - super.setUp(); - drop = DropERC721(getContract("DropERC721")); - } - - /*/////////////////////////////////////////////////////////////// - Branch Testing - //////////////////////////////////////////////////////////////*/ - - modifier callerWithoutMetadataRole() { - vm.startPrank(unauthorized); - _; - } - - modifier callerWithMetadataRole() { - vm.startPrank(deployer); - _; - } - - modifier lazyMintEncrypted() { - updateBatch_amount = 10; - updateBatch_baseURI = "ipfs://"; - updateBatch_revealedURI = "ipfs://revealed"; - updateBatch_key = "key"; - updateBatch_encryptedURI = drop.encryptDecrypt(bytes(updateBatch_revealedURI), updateBatch_key); - updateBatch_provenanceHash = keccak256( - abi.encodePacked(updateBatch_revealedURI, updateBatch_key, block.chainid) - ); - updateBatch_data = abi.encode(updateBatch_encryptedURI, updateBatch_provenanceHash); - vm.prank(deployer); - drop.lazyMint(updateBatch_amount, updateBatch_baseURI, updateBatch_data); - _; - } - - modifier lazyMintUnEncryptedEmptyBaseURI() { - updateBatch_amount = 10; - updateBatch_baseURI = ""; - vm.prank(deployer); - drop.lazyMint(updateBatch_amount, updateBatch_baseURI, updateBatch_data); - _; - } - - modifier lazyMintUnEncryptedRegularBaseURI() { - updateBatch_amount = 10; - updateBatch_baseURI = "ipfs://"; - vm.prank(deployer); - drop.lazyMint(updateBatch_amount, updateBatch_baseURI, updateBatch_data); - _; - } - - modifier batchFrozen() { - drop.freezeBatchBaseURI(0); - _; - } - - function test_revert_NoMetadataRole() public callerWithoutMetadataRole { - bytes32 role = keccak256("METADATA_ROLE"); - vm.expectRevert( - abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, unauthorized, role) - ); - drop.updateBatchBaseURI(0, updateBatch_newBaseURI); - } - - function test_revert_EncryptedBatch() public lazyMintEncrypted callerWithMetadataRole { - vm.expectRevert("Encrypted batch"); - drop.updateBatchBaseURI(0, updateBatch_newBaseURI); - } - - function test_revert_FrozenBatch() public lazyMintUnEncryptedRegularBaseURI callerWithMetadataRole batchFrozen { - vm.expectRevert( - abi.encodeWithSelector(BatchMintMetadata.BatchMintMetadataFrozen.selector, drop.getBatchIdAtIndex(0)) - ); - drop.updateBatchBaseURI(0, updateBatch_newBaseURI); - } - - function test_state() public lazyMintUnEncryptedRegularBaseURI callerWithMetadataRole { - drop.updateBatchBaseURI(0, updateBatch_newBaseURI); - for (uint256 i = 0; i < updateBatch_amount; i += 1) { - string memory uri = drop.tokenURI(i); - assertEq(uri, string(abi.encodePacked(updateBatch_newBaseURI, i.toString()))); - } - } - - function test_event() public lazyMintUnEncryptedRegularBaseURI callerWithMetadataRole { - vm.expectEmit(false, false, false, false); - emit BatchMetadataUpdate(0, 10); - drop.updateBatchBaseURI(0, updateBatch_newBaseURI); - } -} diff --git a/src/test/drop/drop-erc721/updateBatchBaseURI/updateBatchBaseURI.tree b/src/test/drop/drop-erc721/updateBatchBaseURI/updateBatchBaseURI.tree deleted file mode 100644 index b03e1198f..000000000 --- a/src/test/drop/drop-erc721/updateBatchBaseURI/updateBatchBaseURI.tree +++ /dev/null @@ -1,12 +0,0 @@ -function updateBatchBaseURI(uint256 _index, string calldata _uri) -├── when called by a user without the METADATA_ROLE -│ └── it should revert ✅ -└── when called by a user with the METADATA_ROLE - ├── when the batchId for the provided _index is an encrypted batch - │ └── it should revert ✅ - └── when the batchId for the provided _index is not an encrypted batch - ├── when the batchId for the provided _index is frozen - │ └── it should revert ✅ - └── when the batchId for the provided _index is not frozen - ├── it should set the baseURI for the batchId as _uri ✅ - └── it should emit BatchMetadataUpdate with the following parameters: starting tokenId of batch, ending tokenId of batch ✅ \ No newline at end of file diff --git a/src/test/marketplace/DirectListings.t.sol b/src/test/marketplace/DirectListings.t.sol deleted file mode 100644 index dbde0422d..000000000 --- a/src/test/marketplace/DirectListings.t.sol +++ /dev/null @@ -1,2163 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test helper imports -import "../utils/BaseTest.sol"; - -// Test contracts and interfaces -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { MarketplaceV3, IPlatformFee } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { DirectListingsLogic } from "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC721Base } from "contracts/base/ERC721Base.sol"; -import { MockRoyaltyEngineV1 } from "../mocks/MockRoyaltyEngineV1.sol"; - -import { IDirectListings } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -contract MarketplaceDirectListingsTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - address private defaultFeeRecipient; - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `DirectListings` - address directListings = address(new DirectListingsLogic(address(weth))); - defaultFeeRecipient = DirectListingsLogic(directListings).DEFAULT_FEE_RECIPIENT(); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "DirectListingsLogic", - metadataURI: "ipfs://DirectListings", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](13); - extension_directListings.functions[0] = ExtensionFunction( - DirectListingsLogic.totalListings.selector, - "totalListings()" - ); - extension_directListings.functions[1] = ExtensionFunction( - DirectListingsLogic.isBuyerApprovedForListing.selector, - "isBuyerApprovedForListing(uint256,address)" - ); - extension_directListings.functions[2] = ExtensionFunction( - DirectListingsLogic.isCurrencyApprovedForListing.selector, - "isCurrencyApprovedForListing(uint256,address)" - ); - extension_directListings.functions[3] = ExtensionFunction( - DirectListingsLogic.currencyPriceForListing.selector, - "currencyPriceForListing(uint256,address)" - ); - extension_directListings.functions[4] = ExtensionFunction( - DirectListingsLogic.createListing.selector, - "createListing((address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[5] = ExtensionFunction( - DirectListingsLogic.updateListing.selector, - "updateListing(uint256,(address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[6] = ExtensionFunction( - DirectListingsLogic.cancelListing.selector, - "cancelListing(uint256)" - ); - extension_directListings.functions[7] = ExtensionFunction( - DirectListingsLogic.approveBuyerForListing.selector, - "approveBuyerForListing(uint256,address,bool)" - ); - extension_directListings.functions[8] = ExtensionFunction( - DirectListingsLogic.approveCurrencyForListing.selector, - "approveCurrencyForListing(uint256,address,uint256)" - ); - extension_directListings.functions[9] = ExtensionFunction( - DirectListingsLogic.buyFromListing.selector, - "buyFromListing(uint256,address,uint256,address,uint256)" - ); - extension_directListings.functions[10] = ExtensionFunction( - DirectListingsLogic.getAllListings.selector, - "getAllListings(uint256,uint256)" - ); - extension_directListings.functions[11] = ExtensionFunction( - DirectListingsLogic.getAllValidListings.selector, - "getAllValidListings(uint256,uint256)" - ); - extension_directListings.functions[12] = ExtensionFunction( - DirectListingsLogic.getListing.selector, - "getListing(uint256)" - ); - - extensions[0] = extension_directListings; - } - - function _setupERC721BalanceForSeller(address _seller, uint256 _numOfTokens) private { - erc721.mint(_seller, _numOfTokens); - } - - function test_state_initial() public { - uint256 totalListings = DirectListingsLogic(marketplace).totalListings(); - assertEq(totalListings, 0); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_getValidListings_burnListedTokens() public { - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(seller, 1); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(seller); - DirectListingsLogic(marketplace).createListing(listingParams); - - // Total listings incremented - assertEq(DirectListingsLogic(marketplace).totalListings(), 1); - - // burn listed token - vm.prank(seller); - erc721.burn(0); - - vm.warp(150); - // Fetch listing and verify state. - uint256 totalListings = DirectListingsLogic(marketplace).totalListings(); - assertEq(DirectListingsLogic(marketplace).getAllValidListings(0, totalListings - 1).length, 0); - } - - /** - * @dev Tests contract state for Lister role. - */ - function test_state_getRoleMember_listerRole() public { - bytes32 role = keccak256("LISTER_ROLE"); - - uint256 roleMemberCount = PermissionsEnumerable(marketplace).getRoleMemberCount(role); - assertEq(roleMemberCount, 1); - - address roleMember = PermissionsEnumerable(marketplace).getRoleMember(role, 1); - assertEq(roleMember, address(0)); - - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).grantRole(role, address(2)); - Permissions(marketplace).grantRole(role, address(3)); - Permissions(marketplace).grantRole(role, address(4)); - - roleMemberCount = PermissionsEnumerable(marketplace).getRoleMemberCount(role); - assertEq(roleMemberCount, 4); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(marketplace).getRoleMember(role, i)); - } - console.log(""); - - Permissions(marketplace).revokeRole(role, address(2)); - roleMemberCount = PermissionsEnumerable(marketplace).getRoleMemberCount(role); - assertEq(roleMemberCount, 3); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(marketplace).getRoleMember(role, i)); - } - console.log(""); - - Permissions(marketplace).grantRole(role, address(5)); - roleMemberCount = PermissionsEnumerable(marketplace).getRoleMemberCount(role); - assertEq(roleMemberCount, 4); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(marketplace).getRoleMember(role, i)); - } - console.log(""); - - Permissions(marketplace).grantRole(role, address(0)); - roleMemberCount = PermissionsEnumerable(marketplace).getRoleMemberCount(role); - assertEq(roleMemberCount, 5); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(marketplace).getRoleMember(role, i)); - } - console.log(""); - - Permissions(marketplace).grantRole(role, address(6)); - roleMemberCount = PermissionsEnumerable(marketplace).getRoleMemberCount(role); - assertEq(roleMemberCount, 6); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(marketplace).getRoleMember(role, i)); - } - console.log(""); - - Permissions(marketplace).revokeRole(role, address(3)); - roleMemberCount = PermissionsEnumerable(marketplace).getRoleMemberCount(role); - assertEq(roleMemberCount, 5); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(marketplace).getRoleMember(role, i)); - } - console.log(""); - - Permissions(marketplace).revokeRole(role, address(4)); - roleMemberCount = PermissionsEnumerable(marketplace).getRoleMemberCount(role); - assertEq(roleMemberCount, 4); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(marketplace).getRoleMember(role, i)); - } - console.log(""); - - Permissions(marketplace).revokeRole(role, address(0)); - roleMemberCount = PermissionsEnumerable(marketplace).getRoleMemberCount(role); - assertEq(roleMemberCount, 3); - console.log(roleMemberCount); - for (uint256 i = 0; i < roleMemberCount; i++) { - console.log(PermissionsEnumerable(marketplace).getRoleMember(role, i)); - } - console.log(""); - - vm.stopPrank(); - } - - function test_state_approvedCurrencies() public { - (uint256 listingId, IDirectListings.ListingParameters memory listingParams) = _setup_updateListing(); - address currencyToApprove = address(erc20); // same currency as main listing - uint256 pricePerTokenForCurrency = 2 ether; - - // Seller approves currency for listing. - vm.prank(seller); - vm.expectRevert("Marketplace: approving listing currency with different price."); - DirectListingsLogic(marketplace).approveCurrencyForListing( - listingId, - currencyToApprove, - pricePerTokenForCurrency - ); - - // change currency - currencyToApprove = NATIVE_TOKEN; - - vm.prank(seller); - DirectListingsLogic(marketplace).approveCurrencyForListing( - listingId, - currencyToApprove, - pricePerTokenForCurrency - ); - - assertEq(DirectListingsLogic(marketplace).isCurrencyApprovedForListing(listingId, NATIVE_TOKEN), true); - assertEq( - DirectListingsLogic(marketplace).currencyPriceForListing(listingId, NATIVE_TOKEN), - pricePerTokenForCurrency - ); - - // should revert when updating listing with an approved currency but different price - listingParams.currency = NATIVE_TOKEN; - vm.prank(seller); - vm.expectRevert("Marketplace: price different from approved price"); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - - // change listingParams.pricePerToken to approved price - listingParams.pricePerToken = pricePerTokenForCurrency; - vm.prank(seller); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - /*/////////////////////////////////////////////////////////////// - Royalty Tests (incl Royalty Engine / Registry) - //////////////////////////////////////////////////////////////*/ - - function _setupRoyaltyEngine() - private - returns ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory mockRecipients, - uint256[] memory mockAmounts - ) - { - mockRecipients = new address payable[](2); - mockAmounts = new uint256[](2); - - mockRecipients[0] = payable(address(0x12345)); - mockRecipients[1] = payable(address(0x56789)); - - mockAmounts[0] = 10; - mockAmounts[1] = 15; - - royaltyEngine = new MockRoyaltyEngineV1(mockRecipients, mockAmounts); - } - - function _setupListingForRoyaltyTests(address erc721TokenAddress) private returns (uint256 listingId) { - // Sample listing parameters. - address assetContract = erc721TokenAddress; - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 100 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = false; - - // Approve Marketplace to transfer token. - vm.prank(seller); - IERC721(erc721TokenAddress).setApprovalForAll(marketplace, true); - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(seller); - listingId = DirectListingsLogic(marketplace).createListing(listingParams); - } - - function _buyFromListingForRoyaltyTests(uint256 listingId) private returns (uint256 totalPrice) { - IDirectListings.Listing memory listing = DirectListingsLogic(marketplace).getListing(listingId); - - address buyFor = buyer; - uint256 quantityToBuy = listing.quantity; - address currency = listing.currency; - uint256 pricePerToken = listing.pricePerToken; - totalPrice = pricePerToken * quantityToBuy; - - // Mint requisite total price to buyer. - erc20.mint(buyer, totalPrice); - - // Approve marketplace to transfer currency - vm.prank(buyer); - erc20.increaseAllowance(marketplace, totalPrice); - - // Buy tokens from listing. - vm.warp(listing.startTimestamp); - vm.prank(buyer); - DirectListingsLogic(marketplace).buyFromListing(listingId, buyFor, quantityToBuy, currency, totalPrice); - } - - function test_royaltyEngine_tokenWithCustomRoyalties() public { - ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory customRoyaltyRecipients, - uint256[] memory customRoyaltyAmounts - ) = _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // 1. ========= Create listing ========= - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(seller, 1); - uint256 listingId = _setupListingForRoyaltyTests(address(erc721)); - - // 2. ========= Buy from listing ========= - - uint256 totalPrice = _buyFromListingForRoyaltyTests(listingId); - - // 3. ======== Check balances after royalty payments ======== - - { - uint256 defaultFee = (totalPrice * 100) / 10_000; - - // Royalty recipients receive correct amounts - assertBalERC20Eq(address(erc20), customRoyaltyRecipients[0], customRoyaltyAmounts[0]); - assertBalERC20Eq(address(erc20), customRoyaltyRecipients[1], customRoyaltyAmounts[1]); - - // Seller gets total price minus royalty amounts - assertBalERC20Eq( - address(erc20), - seller, - totalPrice - customRoyaltyAmounts[0] - customRoyaltyAmounts[1] - defaultFee - ); - } - } - - function test_royaltyEngine_tokenWithERC2981() public { - (MockRoyaltyEngineV1 royaltyEngine, , ) = _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // create token with ERC2981 - address royaltyRecipient = address(0x12345); - uint128 royaltyBps = 10; - ERC721Base nft2981 = new ERC721Base(address(0x12345), "NFT 2981", "NFT2981", royaltyRecipient, royaltyBps); - // Mint the ERC721 tokens to seller. These tokens will be listed. - vm.prank(address(0x12345)); - nft2981.mintTo(seller, ""); - - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(nft2981)); - - // 1. ========= Create listing ========= - - uint256 listingId = _setupListingForRoyaltyTests(address(nft2981)); - - // 2. ========= Buy from listing ========= - - uint256 totalPrice = _buyFromListingForRoyaltyTests(listingId); - - // 3. ======== Check balances after royalty payments ======== - - { - uint256 defaultFee = (totalPrice * 100) / 10_000; - - uint256 royaltyAmount = (royaltyBps * totalPrice) / 10_000; - // Royalty recipient receives correct amounts - assertBalERC20Eq(address(erc20), royaltyRecipient, royaltyAmount); - - // Seller gets total price minus royalty amount - assertBalERC20Eq(address(erc20), seller, totalPrice - royaltyAmount - defaultFee); - } - } - - function test_noRoyaltyEngine_defaultERC2981Token() public { - // create token with ERC2981 - address royaltyRecipient = address(0x12345); - uint128 royaltyBps = 10; - ERC721Base nft2981 = new ERC721Base(address(0x12345), "NFT 2981", "NFT2981", royaltyRecipient, royaltyBps); - vm.prank(address(0x12345)); - nft2981.mintTo(seller, ""); - - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(nft2981)); - - // 1. ========= Create listing ========= - - uint256 listingId = _setupListingForRoyaltyTests(address(nft2981)); - - // 2. ========= Buy from listing ========= - - uint256 totalPrice = _buyFromListingForRoyaltyTests(listingId); - uint256 defaultFee = (totalPrice * 100) / 10_000; - - // 3. ======== Check balances after royalty payments ======== - - { - uint256 royaltyAmount = (royaltyBps * totalPrice) / 10_000; - // Royalty recipient receives correct amounts - assertBalERC20Eq(address(erc20), royaltyRecipient, royaltyAmount); - - // Seller gets total price minus royalty amount - assertBalERC20Eq(address(erc20), seller, totalPrice - royaltyAmount - defaultFee); - } - } - - function test_royaltyEngine_correctlyDistributeAllFees() public { - ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory customRoyaltyRecipients, - uint256[] memory customRoyaltyAmounts - ) = _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // Set platform fee on marketplace - address platformFeeRecipient = marketplaceDeployer; - uint128 platformFeeBps = 5; - vm.prank(marketplaceDeployer); - IPlatformFee(marketplace).setPlatformFeeInfo(platformFeeRecipient, platformFeeBps); - - // 1. ========= Create listing ========= - - _setupERC721BalanceForSeller(seller, 1); - uint256 listingId = _setupListingForRoyaltyTests(address(erc721)); - - // 2. ========= Buy from listing ========= - - uint256 totalPrice = _buyFromListingForRoyaltyTests(listingId); - - // 3. ======== Check balances after fee payments (platform fee + royalty) ======== - - { - uint256 defaultFee = (totalPrice * 100) / 10_000; - - // Royalty recipients receive correct amounts - assertBalERC20Eq(address(erc20), customRoyaltyRecipients[0], customRoyaltyAmounts[0]); - assertBalERC20Eq(address(erc20), customRoyaltyRecipients[1], customRoyaltyAmounts[1]); - - // Platform fee recipient - uint256 platformFeeAmount = (platformFeeBps * totalPrice) / 10_000; - assertBalERC20Eq(address(erc20), platformFeeRecipient, platformFeeAmount); - - // Seller gets total price minus royalty amounts - assertBalERC20Eq( - address(erc20), - seller, - totalPrice - customRoyaltyAmounts[0] - customRoyaltyAmounts[1] - platformFeeAmount - defaultFee - ); - } - } - - function test_revert_feesExceedTotalPrice() public { - (MockRoyaltyEngineV1 royaltyEngine, , ) = _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // Set platform fee on marketplace - address platformFeeRecipient = marketplaceDeployer; - uint128 platformFeeBps = 9900; // along with default fee of 100 bps => equal to max bps 10_000 or 100% - vm.prank(marketplaceDeployer); - IPlatformFee(marketplace).setPlatformFeeInfo(platformFeeRecipient, platformFeeBps); - - // 1. ========= Create listing ========= - - _setupERC721BalanceForSeller(seller, 1); - uint256 listingId = _setupListingForRoyaltyTests(address(erc721)); - - // 2. ========= Buy from listing ========= - - IDirectListings.Listing memory listing = DirectListingsLogic(marketplace).getListing(listingId); - - address buyFor = buyer; - uint256 quantityToBuy = listing.quantity; - address currency = listing.currency; - uint256 pricePerToken = listing.pricePerToken; - uint256 totalPrice = pricePerToken * quantityToBuy; - - // Mint requisite total price to buyer. - erc20.mint(buyer, totalPrice); - - // Approve marketplace to transfer currency - vm.prank(buyer); - erc20.increaseAllowance(marketplace, totalPrice); - - // Buy tokens from listing. - vm.warp(listing.startTimestamp); - - vm.expectRevert("fees exceed the price"); - vm.prank(buyer); - DirectListingsLogic(marketplace).buyFromListing(listingId, buyFor, quantityToBuy, currency, totalPrice); - } - - /*/////////////////////////////////////////////////////////////// - Create listing - //////////////////////////////////////////////////////////////*/ - - function test_state_createListing() public { - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(seller); - uint256 listingId = DirectListingsLogic(marketplace).createListing(listingParams); - - // Test consequent state of the contract. - - // Seller is still owner of token. - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Total listings incremented - assertEq(DirectListingsLogic(marketplace).totalListings(), 1); - - // Fetch listing and verify state. - IDirectListings.Listing memory listing = DirectListingsLogic(marketplace).getListing(listingId); - - assertEq(listing.listingId, listingId); - assertEq(listing.listingCreator, seller); - assertEq(listing.assetContract, assetContract); - assertEq(listing.tokenId, tokenId); - assertEq(listing.quantity, quantity); - assertEq(listing.currency, currency); - assertEq(listing.pricePerToken, pricePerToken); - assertEq(listing.startTimestamp, startTimestamp); - assertEq(listing.endTimestamp, endTimestamp); - assertEq(listing.reserved, reserved); - assertEq(uint256(listing.tokenType), uint256(IDirectListings.TokenType.ERC721)); - } - - function test_revert_createListing_notOwnerOfListedToken() public { - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - // Don't mint to 'token to be listed' to the seller. - address someWallet = getActor(1000); - _setupERC721BalanceForSeller(someWallet, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), someWallet, tokenIds); - assertIsNotOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(someWallet); - erc721.setApprovalForAll(marketplace, true); - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: not owner or approved tokens."); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - function test_revert_createListing_notApprovedMarketplaceToTransferToken() public { - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Don't approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, false); - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: not owner or approved tokens."); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - function test_revert_createListing_listingZeroQuantity() public { - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 0; // Listing ZERO quantity - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: listing zero quantity."); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - function test_revert_createListing_listingInvalidQuantity() public { - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 2; // Listing more than `1` quantity - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: listing invalid quantity."); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - function test_revert_createListing_invalidStartTimestamp() public { - uint256 blockTimestamp = 100 minutes; - // Set block.timestamp - vm.warp(blockTimestamp); - - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = uint128(blockTimestamp - 61 minutes); // start time is less than block timestamp. - uint128 endTimestamp = uint128(startTimestamp + 1); - bool reserved = true; - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: invalid startTimestamp."); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - function test_revert_createListing_invalidEndTimestamp() public { - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = uint128(startTimestamp - 1); // End timestamp is less than start timestamp. - bool reserved = true; - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: endTimestamp not greater than startTimestamp."); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - function test_revert_createListing_listingNonERC721OrERC1155Token() public { - // Sample listing parameters. - address assetContract = address(erc20); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Grant ERC20 token asset role. - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc20)); - - vm.prank(seller); - vm.expectRevert("Marketplace: listed token must be ERC1155 or ERC721."); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - function test_revert_createListing_noListerRoleWhenRestrictionsActive() public { - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Revoke LISTER_ROLE from seller. - vm.startPrank(marketplaceDeployer); - assertEq(Permissions(marketplace).hasRole(keccak256("LISTER_ROLE"), address(0)), false); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), seller); - assertEq(Permissions(marketplace).hasRole(keccak256("LISTER_ROLE"), seller), false); - - vm.stopPrank(); - - vm.prank(seller); - vm.expectRevert("!LISTER_ROLE"); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - function test_revert_createListing_noAssetRoleWhenRestrictionsActive() public { - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Revoke ASSET_ROLE from token to list. - vm.startPrank(marketplaceDeployer); - assertEq(Permissions(marketplace).hasRole(keccak256("ASSET_ROLE"), address(0)), false); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(erc721)); - assertEq(Permissions(marketplace).hasRole(keccak256("ASSET_ROLE"), address(erc721)), false); - - vm.stopPrank(); - - vm.prank(seller); - vm.expectRevert("!ASSET_ROLE"); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - /*/////////////////////////////////////////////////////////////// - Update listing - //////////////////////////////////////////////////////////////*/ - - function _setup_updateListing() - private - returns (uint256 listingId, IDirectListings.ListingParameters memory listingParams) - { - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // List tokens. - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(seller); - listingId = DirectListingsLogic(marketplace).createListing(listingParams); - } - - function test_state_updateListing() public { - (uint256 listingId, IDirectListings.ListingParameters memory listingParamsToUpdate) = _setup_updateListing(); - - // Mint MORE ERC721 tokens to seller. A new tokenId will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](2); - tokenIds[0] = 0; - tokenIds[1] = 1; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - listingParamsToUpdate.pricePerToken = 2 ether; - - vm.prank(seller); - DirectListingsLogic(marketplace).updateListing(listingId, listingParamsToUpdate); - - // Test consequent state of the contract. - - // Seller is still owner of token. - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Total listings not incremented on update. - assertEq(DirectListingsLogic(marketplace).totalListings(), 1); - - // Fetch listing and verify state. - IDirectListings.Listing memory listing = DirectListingsLogic(marketplace).getListing(listingId); - - assertEq(listing.listingId, listingId); - assertEq(listing.listingCreator, seller); - assertEq(listing.assetContract, listingParamsToUpdate.assetContract); - assertEq(listing.tokenId, 0); - assertEq(listing.quantity, listingParamsToUpdate.quantity); - assertEq(listing.currency, listingParamsToUpdate.currency); - assertEq(listing.pricePerToken, listingParamsToUpdate.pricePerToken); - assertEq(listing.startTimestamp, listingParamsToUpdate.startTimestamp); - assertEq(listing.endTimestamp, listingParamsToUpdate.endTimestamp); - assertEq(listing.reserved, listingParamsToUpdate.reserved); - assertEq(uint256(listing.tokenType), uint256(IDirectListings.TokenType.ERC721)); - } - - function test_revert_updateListing_notListingCreator() public { - (uint256 listingId, IDirectListings.ListingParameters memory listingParamsToUpdate) = _setup_updateListing(); - - // Mint MORE ERC721 tokens to seller. A new tokenId will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](2); - tokenIds[0] = 0; - tokenIds[1] = 1; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - address notSeller = getActor(1000); // Someone other than the seller calls update. - vm.prank(notSeller); - vm.expectRevert("Marketplace: not listing creator."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParamsToUpdate); - } - - function test_revert_updateListing_notOwnerOfListedToken() public { - (uint256 listingId, IDirectListings.ListingParameters memory listingParamsToUpdate) = _setup_updateListing(); - - // Mint MORE ERC721 tokens but NOT to seller. A new tokenId will be listed. - address notSeller = getActor(1000); - _setupERC721BalanceForSeller(notSeller, 1); - - // Approve Marketplace to transfer token. - vm.prank(notSeller); - erc721.setApprovalForAll(marketplace, true); - - // Transfer away owned token. - vm.prank(seller); - erc721.transferFrom(seller, address(0x1234), 0); - - vm.prank(seller); - vm.expectRevert("Marketplace: not owner or approved tokens."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParamsToUpdate); - } - - function test_revert_updateListing_notApprovedMarketplaceToTransferToken() public { - (uint256 listingId, IDirectListings.ListingParameters memory listingParamsToUpdate) = _setup_updateListing(); - - // Mint MORE ERC721 tokens to seller. A new tokenId will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](2); - tokenIds[0] = 0; - tokenIds[1] = 1; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Don't approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, false); - - vm.prank(seller); - vm.expectRevert("Marketplace: not owner or approved tokens."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParamsToUpdate); - } - - function test_revert_updateListing_listingZeroQuantity() public { - (uint256 listingId, IDirectListings.ListingParameters memory listingParamsToUpdate) = _setup_updateListing(); - - // Mint MORE ERC721 tokens to seller. A new tokenId will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](2); - tokenIds[0] = 0; - tokenIds[1] = 1; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - listingParamsToUpdate.quantity = 0; // Listing zero quantity - - vm.prank(seller); - vm.expectRevert("Marketplace: listing zero quantity."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParamsToUpdate); - } - - function test_revert_updateListing_listingInvalidQuantity() public { - (uint256 listingId, IDirectListings.ListingParameters memory listingParamsToUpdate) = _setup_updateListing(); - - // Mint MORE ERC721 tokens to seller. A new tokenId will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](2); - tokenIds[0] = 0; - tokenIds[1] = 1; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - listingParamsToUpdate.quantity = 2; // Listing more than `1` of the ERC721 token - - vm.prank(seller); - vm.expectRevert("Marketplace: listing invalid quantity."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParamsToUpdate); - } - - function test_revert_updateListing_listingNonERC721OrERC1155Token() public { - (uint256 listingId, IDirectListings.ListingParameters memory listingParamsToUpdate) = _setup_updateListing(); - - // Mint MORE ERC721 tokens to seller. A new tokenId will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](2); - tokenIds[0] = 0; - tokenIds[1] = 1; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - listingParamsToUpdate.assetContract = address(erc20); // Listing non ERC721 / ERC1155 token. - - // Grant ERC20 token asset role. - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc20)); - - vm.prank(seller); - vm.expectRevert("Marketplace: listed token must be ERC1155 or ERC721."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParamsToUpdate); - } - - function test_revert_updateListing_invalidStartTimestamp() public { - (uint256 listingId, IDirectListings.ListingParameters memory listingParamsToUpdate) = _setup_updateListing(); - - // Mint MORE ERC721 tokens to seller. A new tokenId will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](2); - tokenIds[0] = 0; - tokenIds[1] = 1; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - uint128 currentStartTimestamp = listingParamsToUpdate.startTimestamp; - listingParamsToUpdate.startTimestamp = currentStartTimestamp - 1; // Retroactively decreasing startTimestamp. - - vm.warp(currentStartTimestamp + 50); - vm.prank(seller); - vm.expectRevert("Marketplace: listing already active."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParamsToUpdate); - } - - function test_revert_updateListing_invalidEndTimestamp() public { - (uint256 listingId, IDirectListings.ListingParameters memory listingParamsToUpdate) = _setup_updateListing(); - - // Mint MORE ERC721 tokens to seller. A new tokenId will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](2); - tokenIds[0] = 0; - tokenIds[1] = 1; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - uint128 currentStartTimestamp = listingParamsToUpdate.startTimestamp; - listingParamsToUpdate.endTimestamp = currentStartTimestamp - 1; // End timestamp less than startTimestamp - - vm.prank(seller); - vm.expectRevert("Marketplace: endTimestamp not greater than startTimestamp."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParamsToUpdate); - } - - function test_revert_updateListing_noAssetRoleWhenRestrictionsActive() public { - (uint256 listingId, IDirectListings.ListingParameters memory listingParamsToUpdate) = _setup_updateListing(); - - // Mint MORE ERC721 tokens to seller. A new tokenId will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](2); - tokenIds[0] = 0; - tokenIds[1] = 1; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Revoke ASSET_ROLE from token to list. - vm.startPrank(marketplaceDeployer); - assertEq(Permissions(marketplace).hasRole(keccak256("ASSET_ROLE"), address(0)), false); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(erc721)); - assertEq(Permissions(marketplace).hasRole(keccak256("ASSET_ROLE"), address(erc721)), false); - - vm.stopPrank(); - - vm.prank(seller); - vm.expectRevert("!ASSET_ROLE"); - DirectListingsLogic(marketplace).updateListing(listingId, listingParamsToUpdate); - } - - /*/////////////////////////////////////////////////////////////// - Cancel listing - //////////////////////////////////////////////////////////////*/ - - function _setup_cancelListing() private returns (uint256 listingId, IDirectListings.Listing memory listing) { - (listingId, ) = _setup_updateListing(); - listing = DirectListingsLogic(marketplace).getListing(listingId); - } - - function test_state_cancelListing() public { - (uint256 listingId, IDirectListings.Listing memory existingListingAtId) = _setup_cancelListing(); - - // Verify existing listing at `listingId` - assertEq(existingListingAtId.assetContract, address(erc721)); - - vm.prank(seller); - DirectListingsLogic(marketplace).cancelListing(listingId); - - // status should be `CANCELLED` - IDirectListings.Listing memory cancelledListing = DirectListingsLogic(marketplace).getListing(listingId); - assertTrue(cancelledListing.status == IDirectListings.Status.CANCELLED); - } - - function test_revert_cancelListing_notListingCreator() public { - (uint256 listingId, IDirectListings.Listing memory existingListingAtId) = _setup_cancelListing(); - - // Verify existing listing at `listingId` - assertEq(existingListingAtId.assetContract, address(erc721)); - - address notSeller = getActor(1000); - vm.prank(notSeller); - vm.expectRevert("Marketplace: not listing creator."); - DirectListingsLogic(marketplace).cancelListing(listingId); - } - - function test_revert_cancelListing_nonExistentListing() public { - _setup_cancelListing(); - - // Verify no listing exists at `nexListingId` - uint256 nextListingId = DirectListingsLogic(marketplace).totalListings(); - - vm.prank(seller); - vm.expectRevert("Marketplace: invalid listing."); - DirectListingsLogic(marketplace).cancelListing(nextListingId); - } - - /*/////////////////////////////////////////////////////////////// - Approve buyer for listing - //////////////////////////////////////////////////////////////*/ - - function _setup_approveBuyerForListing() private returns (uint256 listingId) { - (listingId, ) = _setup_updateListing(); - } - - function test_state_approveBuyerForListing() public { - uint256 listingId = _setup_approveBuyerForListing(); - bool toApprove = true; - - assertEq(DirectListingsLogic(marketplace).getListing(listingId).reserved, true); - - // Seller approves buyer for reserved listing. - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, toApprove); - - assertEq(DirectListingsLogic(marketplace).isBuyerApprovedForListing(listingId, buyer), true); - } - - function test_revert_approveBuyerForListing_notListingCreator() public { - uint256 listingId = _setup_approveBuyerForListing(); - bool toApprove = true; - - assertEq(DirectListingsLogic(marketplace).getListing(listingId).reserved, true); - - // Someone other than the seller approves buyer for reserved listing. - address notSeller = getActor(1000); - vm.prank(notSeller); - vm.expectRevert("Marketplace: not listing creator."); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, toApprove); - } - - function test_revert_approveBuyerForListing_listingNotReserved() public { - (uint256 listingId, IDirectListings.ListingParameters memory listingParamsToUpdate) = _setup_updateListing(); - bool toApprove = true; - - assertEq(DirectListingsLogic(marketplace).getListing(listingId).reserved, true); - - listingParamsToUpdate.reserved = false; - - vm.prank(seller); - DirectListingsLogic(marketplace).updateListing(listingId, listingParamsToUpdate); - - assertEq(DirectListingsLogic(marketplace).getListing(listingId).reserved, false); - - // Seller approves buyer for reserved listing. - vm.prank(seller); - vm.expectRevert("Marketplace: listing not reserved."); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, toApprove); - } - - /*/////////////////////////////////////////////////////////////// - Approve currency for listing - //////////////////////////////////////////////////////////////*/ - - function _setup_approveCurrencyForListing() private returns (uint256 listingId) { - (listingId, ) = _setup_updateListing(); - } - - function test_state_approveCurrencyForListing() public { - uint256 listingId = _setup_approveCurrencyForListing(); - address currencyToApprove = NATIVE_TOKEN; - uint256 pricePerTokenForCurrency = 2 ether; - - // Seller approves buyer for reserved listing. - vm.prank(seller); - DirectListingsLogic(marketplace).approveCurrencyForListing( - listingId, - currencyToApprove, - pricePerTokenForCurrency - ); - - assertEq(DirectListingsLogic(marketplace).isCurrencyApprovedForListing(listingId, NATIVE_TOKEN), true); - assertEq( - DirectListingsLogic(marketplace).currencyPriceForListing(listingId, NATIVE_TOKEN), - pricePerTokenForCurrency - ); - } - - function test_revert_approveCurrencyForListing_notListingCreator() public { - uint256 listingId = _setup_approveCurrencyForListing(); - address currencyToApprove = NATIVE_TOKEN; - uint256 pricePerTokenForCurrency = 2 ether; - - // Someone other than seller approves buyer for reserved listing. - address notSeller = getActor(1000); - vm.prank(notSeller); - vm.expectRevert("Marketplace: not listing creator."); - DirectListingsLogic(marketplace).approveCurrencyForListing( - listingId, - currencyToApprove, - pricePerTokenForCurrency - ); - } - - function test_revert_approveCurrencyForListing_reApprovingMainCurrency() public { - uint256 listingId = _setup_approveCurrencyForListing(); - address currencyToApprove = DirectListingsLogic(marketplace).getListing(listingId).currency; - uint256 pricePerTokenForCurrency = 2 ether; - - // Seller approves buyer for reserved listing. - vm.prank(seller); - vm.expectRevert("Marketplace: approving listing currency with different price."); - DirectListingsLogic(marketplace).approveCurrencyForListing( - listingId, - currencyToApprove, - pricePerTokenForCurrency - ); - } - - /*/////////////////////////////////////////////////////////////// - Buy from listing - //////////////////////////////////////////////////////////////*/ - - function _setup_buyFromListing() private returns (uint256 listingId, IDirectListings.Listing memory listing) { - (listingId, ) = _setup_updateListing(); - listing = DirectListingsLogic(marketplace).getListing(listingId); - } - - function test_state_buyFromListing() public { - (uint256 listingId, IDirectListings.Listing memory listing) = _setup_buyFromListing(); - - address buyFor = buyer; - uint256 quantityToBuy = listing.quantity; - address currency = listing.currency; - uint256 pricePerToken = listing.pricePerToken; - uint256 totalPrice = pricePerToken * quantityToBuy; - - // Seller approves buyer for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - - // Verify that seller is owner of listed tokens, pre-sale. - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = 0; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - assertIsNotOwnerERC721(address(erc721), buyer, tokenIds); - - // Mint requisite total price to buyer. - erc20.mint(buyer, totalPrice); - assertBalERC20Eq(address(erc20), buyer, totalPrice); - assertBalERC20Eq(address(erc20), seller, 0); - - // Approve marketplace to transfer currency - vm.prank(buyer); - erc20.increaseAllowance(marketplace, totalPrice); - - // Buy tokens from listing. - vm.warp(listing.startTimestamp); - vm.prank(buyer); - DirectListingsLogic(marketplace).buyFromListing(listingId, buyFor, quantityToBuy, currency, totalPrice); - - // Verify that buyer is owner of listed tokens, post-sale. - assertIsOwnerERC721(address(erc721), buyer, tokenIds); - assertIsNotOwnerERC721(address(erc721), seller, tokenIds); - - uint256 defaultFee = (totalPrice * 100) / 10_000; - - // Verify seller is paid total price. - assertBalERC20Eq(address(erc20), buyer, 0); - assertBalERC20Eq(address(erc20), seller, totalPrice - defaultFee); - - if (quantityToBuy == listing.quantity) { - // Verify listing status is `COMPLETED` if listing tokens are all bought. - IDirectListings.Listing memory completedListing = DirectListingsLogic(marketplace).getListing(listingId); - assertTrue(completedListing.status == IDirectListings.Status.COMPLETED); - } - } - - function test_state_buyFromListing_nativeToken() public { - (uint256 listingId, IDirectListings.Listing memory listing) = _setup_buyFromListing(); - - address buyFor = buyer; - uint256 quantityToBuy = listing.quantity; - address currency = NATIVE_TOKEN; - uint256 pricePerToken = listing.pricePerToken; - uint256 totalPrice = pricePerToken * quantityToBuy; - - // Approve NATIVE_TOKEN for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveCurrencyForListing(listingId, currency, pricePerToken); - - // Seller approves buyer for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - - // Verify that seller is owner of listed tokens, pre-sale. - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = 0; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - assertIsNotOwnerERC721(address(erc721), buyer, tokenIds); - - // Deal requisite total price to buyer. - vm.deal(buyer, totalPrice); - uint256 buyerBalBefore = buyer.balance; - uint256 sellerBalBefore = seller.balance; - - // Buy tokens from listing. - vm.warp(listing.startTimestamp); - vm.prank(buyer); - DirectListingsLogic(marketplace).buyFromListing{ value: totalPrice }( - listingId, - buyFor, - quantityToBuy, - currency, - totalPrice - ); - - // Verify that buyer is owner of listed tokens, post-sale. - assertIsOwnerERC721(address(erc721), buyer, tokenIds); - assertIsNotOwnerERC721(address(erc721), seller, tokenIds); - - uint256 defaultFee = (totalPrice * 100) / 10_000; - - // Verify seller is paid total price. - assertEq(buyer.balance, buyerBalBefore - totalPrice); - assertEq(seller.balance, sellerBalBefore + totalPrice - defaultFee); - - if (quantityToBuy == listing.quantity) { - // Verify listing status is `COMPLETED` if listing tokens are all bought. - IDirectListings.Listing memory completedListing = DirectListingsLogic(marketplace).getListing(listingId); - assertTrue(completedListing.status == IDirectListings.Status.COMPLETED); - } - } - - function test_revert_buyFromListing_nativeToken_incorrectValueSent() public { - (uint256 listingId, IDirectListings.Listing memory listing) = _setup_buyFromListing(); - - address buyFor = buyer; - uint256 quantityToBuy = listing.quantity; - address currency = NATIVE_TOKEN; - uint256 pricePerToken = listing.pricePerToken; - uint256 totalPrice = pricePerToken * quantityToBuy; - - // Approve NATIVE_TOKEN for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveCurrencyForListing(listingId, currency, pricePerToken); - - // Seller approves buyer for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - - // Verify that seller is owner of listed tokens, pre-sale. - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = 0; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - assertIsNotOwnerERC721(address(erc721), buyer, tokenIds); - - // Deal requisite total price to buyer. - vm.deal(buyer, totalPrice); - - // Buy tokens from listing. - vm.warp(listing.startTimestamp); - vm.prank(buyer); - vm.expectRevert("Marketplace: msg.value must exactly be the total price."); - DirectListingsLogic(marketplace).buyFromListing{ value: totalPrice - 1 }( // sending insufficient value - listingId, - buyFor, - quantityToBuy, - currency, - totalPrice - ); - } - - function test_revert_buyFromListing_unexpectedTotalPrice() public { - (uint256 listingId, IDirectListings.Listing memory listing) = _setup_buyFromListing(); - - address buyFor = buyer; - uint256 quantityToBuy = listing.quantity; - address currency = NATIVE_TOKEN; - uint256 pricePerToken = listing.pricePerToken; - uint256 totalPrice = pricePerToken * quantityToBuy; - - // Approve NATIVE_TOKEN for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveCurrencyForListing(listingId, currency, pricePerToken); - - // Seller approves buyer for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - - // Verify that seller is owner of listed tokens, pre-sale. - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = 0; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - assertIsNotOwnerERC721(address(erc721), buyer, tokenIds); - - // Deal requisite total price to buyer. - vm.deal(buyer, totalPrice); - - // Buy tokens from listing. - vm.warp(listing.startTimestamp); - vm.prank(buyer); - vm.expectRevert("Unexpected total price"); - DirectListingsLogic(marketplace).buyFromListing{ value: totalPrice }( - listingId, - buyFor, - quantityToBuy, - currency, - totalPrice + 1 // Pass unexpected total price - ); - } - - function test_revert_buyFromListing_invalidCurrency() public { - (uint256 listingId, IDirectListings.Listing memory listing) = _setup_buyFromListing(); - - address buyFor = buyer; - uint256 quantityToBuy = listing.quantity; - uint256 pricePerToken = listing.pricePerToken; - uint256 totalPrice = pricePerToken * quantityToBuy; - - // Seller approves buyer for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - - // Verify that seller is owner of listed tokens, pre-sale. - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = 0; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - assertIsNotOwnerERC721(address(erc721), buyer, tokenIds); - - // Mint requisite total price to buyer. - erc20.mint(buyer, totalPrice); - assertBalERC20Eq(address(erc20), buyer, totalPrice); - assertBalERC20Eq(address(erc20), seller, 0); - - // Approve marketplace to transfer currency - vm.prank(buyer); - erc20.increaseAllowance(marketplace, totalPrice); - - // Buy tokens from listing. - - assertEq(listing.currency, address(erc20)); - assertEq(DirectListingsLogic(marketplace).isCurrencyApprovedForListing(listingId, NATIVE_TOKEN), false); - - vm.warp(listing.startTimestamp); - vm.prank(buyer); - vm.expectRevert("Paying in invalid currency."); - DirectListingsLogic(marketplace).buyFromListing(listingId, buyFor, quantityToBuy, NATIVE_TOKEN, totalPrice); - } - - function test_revert_buyFromListing_buyerBalanceLessThanPrice() public { - (uint256 listingId, IDirectListings.Listing memory listing) = _setup_buyFromListing(); - - address buyFor = buyer; - uint256 quantityToBuy = listing.quantity; - address currency = listing.currency; - uint256 pricePerToken = listing.pricePerToken; - uint256 totalPrice = pricePerToken * quantityToBuy; - - // Seller approves buyer for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - - // Verify that seller is owner of listed tokens, pre-sale. - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = 0; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - assertIsNotOwnerERC721(address(erc721), buyer, tokenIds); - - // Mint requisite total price to buyer. - erc20.mint(buyer, totalPrice - 1); // Buyer balance less than total price - assertBalERC20Eq(address(erc20), buyer, totalPrice - 1); - assertBalERC20Eq(address(erc20), seller, 0); - - // Approve marketplace to transfer currency - vm.prank(buyer); - erc20.increaseAllowance(marketplace, totalPrice); - - // Buy tokens from listing. - vm.warp(listing.startTimestamp); - vm.prank(buyer); - vm.expectRevert("!BAL20"); - DirectListingsLogic(marketplace).buyFromListing(listingId, buyFor, quantityToBuy, currency, totalPrice); - } - - function test_revert_buyFromListing_notApprovedMarketplaceToTransferPrice() public { - (uint256 listingId, IDirectListings.Listing memory listing) = _setup_buyFromListing(); - - address buyFor = buyer; - uint256 quantityToBuy = listing.quantity; - address currency = listing.currency; - uint256 pricePerToken = listing.pricePerToken; - uint256 totalPrice = pricePerToken * quantityToBuy; - - // Seller approves buyer for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - - // Verify that seller is owner of listed tokens, pre-sale. - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = 0; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - assertIsNotOwnerERC721(address(erc721), buyer, tokenIds); - - // Mint requisite total price to buyer. - erc20.mint(buyer, totalPrice); - assertBalERC20Eq(address(erc20), buyer, totalPrice); - assertBalERC20Eq(address(erc20), seller, 0); - - // Don't approve marketplace to transfer currency - vm.prank(buyer); - erc20.approve(marketplace, 0); - - // Buy tokens from listing. - vm.warp(listing.startTimestamp); - vm.prank(buyer); - vm.expectRevert("!BAL20"); - DirectListingsLogic(marketplace).buyFromListing(listingId, buyFor, quantityToBuy, currency, totalPrice); - } - - function test_revert_buyFromListing_buyingZeroQuantity() public { - (uint256 listingId, IDirectListings.Listing memory listing) = _setup_buyFromListing(); - - address buyFor = buyer; - uint256 quantityToBuy = 0; // Buying zero quantity - address currency = listing.currency; - uint256 pricePerToken = listing.pricePerToken; - uint256 totalPrice = pricePerToken * quantityToBuy; - - // Seller approves buyer for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - - // Verify that seller is owner of listed tokens, pre-sale. - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = 0; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - assertIsNotOwnerERC721(address(erc721), buyer, tokenIds); - - // Mint requisite total price to buyer. - erc20.mint(buyer, totalPrice); - assertBalERC20Eq(address(erc20), buyer, totalPrice); - assertBalERC20Eq(address(erc20), seller, 0); - - // Don't approve marketplace to transfer currency - vm.prank(buyer); - erc20.increaseAllowance(marketplace, totalPrice); - - // Buy tokens from listing. - vm.warp(listing.startTimestamp); - vm.prank(buyer); - vm.expectRevert("Buying invalid quantity"); - DirectListingsLogic(marketplace).buyFromListing(listingId, buyFor, quantityToBuy, currency, totalPrice); - } - - function test_revert_buyFromListing_buyingMoreQuantityThanListed() public { - (uint256 listingId, IDirectListings.Listing memory listing) = _setup_buyFromListing(); - - address buyFor = buyer; - uint256 quantityToBuy = listing.quantity + 1; // Buying more than listed. - address currency = listing.currency; - uint256 pricePerToken = listing.pricePerToken; - uint256 totalPrice = pricePerToken * quantityToBuy; - - // Seller approves buyer for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - - // Verify that seller is owner of listed tokens, pre-sale. - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = 0; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - assertIsNotOwnerERC721(address(erc721), buyer, tokenIds); - - // Mint requisite total price to buyer. - erc20.mint(buyer, totalPrice); - assertBalERC20Eq(address(erc20), buyer, totalPrice); - assertBalERC20Eq(address(erc20), seller, 0); - - // Don't approve marketplace to transfer currency - vm.prank(buyer); - erc20.increaseAllowance(marketplace, totalPrice); - - // Buy tokens from listing. - vm.warp(listing.startTimestamp); - vm.prank(buyer); - vm.expectRevert("Buying invalid quantity"); - DirectListingsLogic(marketplace).buyFromListing(listingId, buyFor, quantityToBuy, currency, totalPrice); - } - - /*/////////////////////////////////////////////////////////////// - View functions - //////////////////////////////////////////////////////////////*/ - - function _createListing(address _seller) private returns (uint256 listingId) { - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(_seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), _seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(_seller); - erc721.setApprovalForAll(marketplace, true); - - // List tokens. - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(_seller); - listingId = DirectListingsLogic(marketplace).createListing(listingParams); - } - - function test_audit_native_tokens_locked() public { - (uint256 listingId, IDirectListings.Listing memory existingListing) = _setup_buyFromListing(); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingListing.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingListing.assetContract, address(erc721)); - - vm.warp(existingListing.startTimestamp); - - // No ether is locked in contract - assertEq(marketplace.balance, 0); - - // buy from listing - erc20.mint(buyer, 10 ether); - vm.deal(buyer, 1 ether); - - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - - vm.startPrank(buyer); - erc20.approve(marketplace, 10 ether); - - vm.expectRevert("Marketplace: invalid native tokens sent."); - DirectListingsLogic(marketplace).buyFromListing{ value: 1 ether }(listingId, buyer, 1, address(erc20), 1 ether); - vm.stopPrank(); - - // 1 ether is temporary locked in contract - assertEq(marketplace.balance, 0 ether); - } -} - -contract IssueC2_MarketplaceDirectListingsTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `DirectListings` - address directListings = address(new DirectListingsLogic(address(weth))); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "DirectListingsLogic", - metadataURI: "ipfs://DirectListings", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](13); - extension_directListings.functions[0] = ExtensionFunction( - DirectListingsLogic.totalListings.selector, - "totalListings()" - ); - extension_directListings.functions[1] = ExtensionFunction( - DirectListingsLogic.isBuyerApprovedForListing.selector, - "isBuyerApprovedForListing(uint256,address)" - ); - extension_directListings.functions[2] = ExtensionFunction( - DirectListingsLogic.isCurrencyApprovedForListing.selector, - "isCurrencyApprovedForListing(uint256,address)" - ); - extension_directListings.functions[3] = ExtensionFunction( - DirectListingsLogic.currencyPriceForListing.selector, - "currencyPriceForListing(uint256,address)" - ); - extension_directListings.functions[4] = ExtensionFunction( - DirectListingsLogic.createListing.selector, - "createListing((address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[5] = ExtensionFunction( - DirectListingsLogic.updateListing.selector, - "updateListing(uint256,(address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[6] = ExtensionFunction( - DirectListingsLogic.cancelListing.selector, - "cancelListing(uint256)" - ); - extension_directListings.functions[7] = ExtensionFunction( - DirectListingsLogic.approveBuyerForListing.selector, - "approveBuyerForListing(uint256,address,bool)" - ); - extension_directListings.functions[8] = ExtensionFunction( - DirectListingsLogic.approveCurrencyForListing.selector, - "approveCurrencyForListing(uint256,address,uint256)" - ); - extension_directListings.functions[9] = ExtensionFunction( - DirectListingsLogic.buyFromListing.selector, - "buyFromListing(uint256,address,uint256,address,uint256)" - ); - extension_directListings.functions[10] = ExtensionFunction( - DirectListingsLogic.getAllListings.selector, - "getAllListings(uint256,uint256)" - ); - extension_directListings.functions[11] = ExtensionFunction( - DirectListingsLogic.getAllValidListings.selector, - "getAllValidListings(uint256,uint256)" - ); - extension_directListings.functions[12] = ExtensionFunction( - DirectListingsLogic.getListing.selector, - "getListing(uint256)" - ); - - extensions[0] = extension_directListings; - } - - function _setupERC721BalanceForSeller(address _seller, uint256 _numOfTokens) private { - erc721.mint(_seller, _numOfTokens); - } - - function _setup_updateListing() - private - returns (uint256 listingId, IDirectListings.ListingParameters memory listingParams) - { - // Sample listing parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - // Mint the ERC721 tokens to seller. These tokens will be listed. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // List tokens. - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(seller); - listingId = DirectListingsLogic(marketplace).createListing(listingParams); - } - - function _setup_buyFromListing() private returns (uint256 listingId, IDirectListings.Listing memory listing) { - (listingId, ) = _setup_updateListing(); - listing = DirectListingsLogic(marketplace).getListing(listingId); - } - - function test_state_buyFromListing_after_update() public { - (uint256 listingId, IDirectListings.Listing memory listing) = _setup_buyFromListing(); - - uint256 quantityToBuy = listing.quantity; - uint256 pricePerToken = listing.pricePerToken; - uint256 totalPrice = pricePerToken * quantityToBuy; - - // Seller approves buyer for listing - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - - // Verify that seller is owner of listed tokens, pre-sale. - // This token (Id = 0) was created in the above _setup_buyFromListing - uint256[] memory expectedTokenIds = new uint256[](1); - expectedTokenIds[0] = 0; - assertIsOwnerERC721(address(erc721), seller, expectedTokenIds); - assertIsNotOwnerERC721(address(erc721), buyer, expectedTokenIds); - - // Mint a new token. This is token we will "swap out" via updateListing - // It should be tokenId of 1 - _setupERC721BalanceForSeller(seller, 1); - - // Verify that seller is owner of new token, pre-sale. - uint256[] memory swappedTokenIds = new uint256[](1); - swappedTokenIds[0] = 1; - assertIsOwnerERC721(address(erc721), seller, swappedTokenIds); - assertIsNotOwnerERC721(address(erc721), buyer, swappedTokenIds); - - // Mint requisite total price to buyer. - erc20.mint(buyer, totalPrice); - assertBalERC20Eq(address(erc20), buyer, totalPrice); - assertBalERC20Eq(address(erc20), seller, 0); - - // Approve marketplace to transfer currency - vm.prank(buyer); - erc20.increaseAllowance(marketplace, totalPrice); - - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Create ListingParameters with new tokenId (1) and update - IDirectListings.ListingParameters memory listingParams = IDirectListings.ListingParameters( - address(erc721), - 1, - 1, - address(erc20), - 1 ether, - 100, - 200, - true - ); - vm.prank(seller); - vm.expectRevert("Marketplace: cannot update what token is listed."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - - // Buy listing - // vm.warp(listing.startTimestamp); - // vm.prank(buyer); - // DirectListingsLogic(marketplace).buyFromListing(listingId, buyFor, quantityToBuy, currency, totalPrice); - - // // Buyer is owner of the swapped out token (tokenId = 1) and not the expected (tokenId = 0) - // assertIsOwnerERC721(address(erc721), buyer, swappedTokenIds); - // assertIsNotOwnerERC721(address(erc721), buyer, expectedTokenIds); - - // // Verify seller is paid total price. - // assertBalERC20Eq(address(erc20), buyer, 0); - // assertBalERC20Eq(address(erc20), seller, totalPrice); - } -} diff --git a/src/test/marketplace/EnglishAuctions.t.sol b/src/test/marketplace/EnglishAuctions.t.sol deleted file mode 100644 index 7dc719254..000000000 --- a/src/test/marketplace/EnglishAuctions.t.sol +++ /dev/null @@ -1,2744 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test helper imports -import "../utils/BaseTest.sol"; - -// Test contracts and interfaces -import { PluginMap, IPluginMap } from "contracts/extension/plugin/PluginMap.sol"; -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { MarketplaceV3, IPlatformFee } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { EnglishAuctionsLogic } from "contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC721Base } from "contracts/base/ERC721Base.sol"; -import { MockRoyaltyEngineV1 } from "../mocks/MockRoyaltyEngineV1.sol"; - -import { IEnglishAuctions } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -contract MarketplaceEnglishAuctionsTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - address private defaultFeeRecipient; - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `EnglishAuctions` - address englishAuctions = address(new EnglishAuctionsLogic(address(weth))); - defaultFeeRecipient = EnglishAuctionsLogic(englishAuctions).DEFAULT_FEE_RECIPIENT(); - vm.label(englishAuctions, "EnglishAuctions_Extension"); - - // Extension: EnglishAuctionsLogic - Extension memory extension_englishAuctions; - extension_englishAuctions.metadata = ExtensionMetadata({ - name: "EnglishAuctionsLogic", - metadataURI: "ipfs://EnglishAuctions", - implementation: englishAuctions - }); - - extension_englishAuctions.functions = new ExtensionFunction[](12); - extension_englishAuctions.functions[0] = ExtensionFunction( - EnglishAuctionsLogic.totalAuctions.selector, - "totalAuctions()" - ); - extension_englishAuctions.functions[1] = ExtensionFunction( - EnglishAuctionsLogic.createAuction.selector, - "createAuction((address,uint256,uint256,address,uint256,uint256,uint64,uint64,uint64,uint64))" - ); - extension_englishAuctions.functions[2] = ExtensionFunction( - EnglishAuctionsLogic.cancelAuction.selector, - "cancelAuction(uint256)" - ); - extension_englishAuctions.functions[3] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionPayout.selector, - "collectAuctionPayout(uint256)" - ); - extension_englishAuctions.functions[4] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionTokens.selector, - "collectAuctionTokens(uint256)" - ); - extension_englishAuctions.functions[5] = ExtensionFunction( - EnglishAuctionsLogic.bidInAuction.selector, - "bidInAuction(uint256,uint256)" - ); - extension_englishAuctions.functions[6] = ExtensionFunction( - EnglishAuctionsLogic.isNewWinningBid.selector, - "isNewWinningBid(uint256,uint256)" - ); - extension_englishAuctions.functions[7] = ExtensionFunction( - EnglishAuctionsLogic.getAuction.selector, - "getAuction(uint256)" - ); - extension_englishAuctions.functions[8] = ExtensionFunction( - EnglishAuctionsLogic.getAllAuctions.selector, - "getAllAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[9] = ExtensionFunction( - EnglishAuctionsLogic.getAllValidAuctions.selector, - "getAllValidAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[10] = ExtensionFunction( - EnglishAuctionsLogic.getWinningBid.selector, - "getWinningBid(uint256)" - ); - extension_englishAuctions.functions[11] = ExtensionFunction( - EnglishAuctionsLogic.isAuctionExpired.selector, - "isAuctionExpired(uint256)" - ); - - extensions[0] = extension_englishAuctions; - } - - function _setupERC721BalanceForSeller(address _seller, uint256 _numOfTokens) private { - erc721.mint(_seller, _numOfTokens); - } - - function test_state_initial() public { - uint256 totoalAuctions = EnglishAuctionsLogic(marketplace).totalAuctions(); - assertEq(totoalAuctions, 0); - } - - /*/////////////////////////////////////////////////////////////// - Royalty Tests (incl Royalty Engine / Registry) - //////////////////////////////////////////////////////////////*/ - - function _setupRoyaltyEngine() - private - returns ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory mockRecipients, - uint256[] memory mockAmounts - ) - { - mockRecipients = new address payable[](2); - mockAmounts = new uint256[](2); - - mockRecipients[0] = payable(address(0x12345)); - mockRecipients[1] = payable(address(0x56789)); - - mockAmounts[0] = 10; - mockAmounts[1] = 15; - - royaltyEngine = new MockRoyaltyEngineV1(mockRecipients, mockAmounts); - } - - function _setupAuctionForRoyaltyTests(address erc721TokenAddress) private returns (uint256 auctionId) { - // Sample auction parameters. - address assetContract = erc721TokenAddress; - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Approve Marketplace to transfer token. - vm.prank(seller); - IERC721(erc721TokenAddress).setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - auctionId = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function _buyoutAuctionForRoyaltyTests(uint256 auctionId) private returns (uint256 buyoutAmount) { - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - buyoutAmount = existingAuction.buyoutBidAmount; - - // Mint requisite total price to buyer. - erc20.mint(buyer, buyoutAmount); - - // Approve marketplace to transfer currency - vm.prank(buyer); - erc20.approve(marketplace, buyoutAmount); - - // Place buyout bid in auction. - vm.warp(existingAuction.startTimestamp); - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, buyoutAmount); - } - - function test_royaltyEngine_tokenWithCustomRoyalties() public { - ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory customRoyaltyRecipients, - uint256[] memory customRoyaltyAmounts - ) = _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // 1. ========= Create auction ========= - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - uint256 auctionId = _setupAuctionForRoyaltyTests(address(erc721)); - - // 2. ========= Bid in auction ========= - - uint256 buyoutAmount = _buyoutAuctionForRoyaltyTests(auctionId); - - // 3. ========= Seller collects auction payout - - vm.prank(seller); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - - // 4. ======== Check balances after royalty payments ======== - - { - uint256 defaultFee = (buyoutAmount * 100) / 10_000; - - // Royalty recipients receive correct amounts - assertBalERC20Eq(address(erc20), customRoyaltyRecipients[0], customRoyaltyAmounts[0]); - assertBalERC20Eq(address(erc20), customRoyaltyRecipients[1], customRoyaltyAmounts[1]); - - // Seller gets total price minus royalty amounts - assertBalERC20Eq( - address(erc20), - seller, - buyoutAmount - customRoyaltyAmounts[0] - customRoyaltyAmounts[1] - defaultFee - ); - } - } - - function test_royaltyEngine_tokenWithERC2981() public { - (MockRoyaltyEngineV1 royaltyEngine, , ) = _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // create token with ERC2981 - address royaltyRecipient = address(0x12345); - uint128 royaltyBps = 10; - ERC721Base nft2981 = new ERC721Base(address(0x12345), "NFT 2981", "NFT2981", royaltyRecipient, royaltyBps); - // Mint the ERC721 tokens to seller. These tokens will be listed. - vm.prank(address(0x12345)); - nft2981.mintTo(seller, ""); - - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(nft2981)); - - // 1. ========= Create auction ========= - - uint256 auctionId = _setupAuctionForRoyaltyTests(address(nft2981)); - - // 2. ========= Bid in auction ========= - - uint256 buyoutAmount = _buyoutAuctionForRoyaltyTests(auctionId); - - // 3. ========= Seller collects auction payout - - vm.prank(seller); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - - // 4. ======== Check balances after royalty payments ======== - - { - uint256 defaultFee = (buyoutAmount * 100) / 10_000; - - uint256 royaltyAmount = (royaltyBps * buyoutAmount) / 10_000; - // Royalty recipient receives correct amounts - assertBalERC20Eq(address(erc20), royaltyRecipient, royaltyAmount); - - // Seller gets total price minus royalty amount - assertBalERC20Eq(address(erc20), seller, buyoutAmount - royaltyAmount - defaultFee); - } - } - - function test_noRoyaltyEngine_defaultERC2981Token() public { - // create token with ERC2981 - address royaltyRecipient = address(0x12345); - uint128 royaltyBps = 10; - ERC721Base nft2981 = new ERC721Base(address(0x12345), "NFT 2981", "NFT2981", royaltyRecipient, royaltyBps); - vm.prank(address(0x12345)); - nft2981.mintTo(seller, ""); - - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(nft2981)); - - // 1. ========= Create auction ========= - - uint256 auctionId = _setupAuctionForRoyaltyTests(address(nft2981)); - - // 2. ========= Bid in auction ========= - - uint256 buyoutAmount = _buyoutAuctionForRoyaltyTests(auctionId); - - // 3. ========= Seller collects auction payout - - vm.prank(seller); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - - // 4. ======== Check balances after royalty payments ======== - - { - uint256 defaultFee = (buyoutAmount * 100) / 10_000; - uint256 royaltyAmount = (royaltyBps * buyoutAmount) / 10_000; - // Royalty recipient receives correct amounts - assertBalERC20Eq(address(erc20), royaltyRecipient, royaltyAmount); - - // Seller gets total price minus royalty amount - assertBalERC20Eq(address(erc20), seller, buyoutAmount - royaltyAmount - defaultFee); - } - } - - function test_royaltyEngine_correctlyDistributeAllFees() public { - ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory customRoyaltyRecipients, - uint256[] memory customRoyaltyAmounts - ) = _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // Set platform fee on marketplace - address platformFeeRecipient = marketplaceDeployer; - uint128 platformFeeBps = 5; - vm.prank(marketplaceDeployer); - IPlatformFee(marketplace).setPlatformFeeInfo(platformFeeRecipient, platformFeeBps); - - // 1. ========= Create auction ========= - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - uint256 auctionId = _setupAuctionForRoyaltyTests(address(erc721)); - - // 2. ========= Bid in auction ========= - - uint256 buyoutAmount = _buyoutAuctionForRoyaltyTests(auctionId); - - // 3. ========= Seller collects auction payout - - vm.prank(seller); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - - // 4. ======== Check balances after royalty payments ======== - - { - uint256 defaultFee = (buyoutAmount * 100) / 10_000; - - // Royalty recipients receive correct amounts - assertBalERC20Eq(address(erc20), customRoyaltyRecipients[0], customRoyaltyAmounts[0]); - assertBalERC20Eq(address(erc20), customRoyaltyRecipients[1], customRoyaltyAmounts[1]); - - // Platform fee recipient - uint256 platformFeeAmount = (platformFeeBps * buyoutAmount) / 10_000; - assertBalERC20Eq(address(erc20), platformFeeRecipient, platformFeeAmount); - - // Seller gets total price minus royalty amounts - assertBalERC20Eq( - address(erc20), - seller, - buyoutAmount - customRoyaltyAmounts[0] - customRoyaltyAmounts[1] - platformFeeAmount - defaultFee - ); - } - } - - function test_revert_feesExceedTotalPrice() public { - (MockRoyaltyEngineV1 royaltyEngine, , ) = _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // Set platform fee on marketplace - address platformFeeRecipient = marketplaceDeployer; - uint128 platformFeeBps = 9900; // equal to max bps 10_000 or 100% with 100 bps default - vm.prank(marketplaceDeployer); - IPlatformFee(marketplace).setPlatformFeeInfo(platformFeeRecipient, platformFeeBps); - - // 1. ========= Create auction ========= - - _setupERC721BalanceForSeller(seller, 1); - uint256 auctionId = _setupAuctionForRoyaltyTests(address(erc721)); - - // 2. ========= Bid in auction ========= - - IEnglishAuctions.Auction memory auction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256 buyoutAmount = auction.buyoutBidAmount; - - // Mint requisite total price to buyer. - erc20.mint(buyer, buyoutAmount); - - // Approve marketplace to transfer currency - vm.prank(buyer); - erc20.increaseAllowance(marketplace, buyoutAmount); - - // Buy tokens from auction. - vm.warp(auction.startTimestamp); - - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, buyoutAmount); - - // 3. ========= Seller collects auction payout - - vm.expectRevert("fees exceed the price"); - vm.prank(seller); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - } - - /*/////////////////////////////////////////////////////////////// - Create Auction - //////////////////////////////////////////////////////////////*/ - - function test_state_createAuction() public { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - uint256 auctionId = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - - // Test consequent state of the contract. - - // Marketplace is owner of token. - assertIsOwnerERC721(address(erc721), marketplace, tokenIds); - - // Total listings incremented - assertEq(EnglishAuctionsLogic(marketplace).totalAuctions(), 1); - - // Fetch listing and verify state. - IEnglishAuctions.Auction memory auction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - assertEq(auction.auctionId, auctionId); - assertEq(auction.auctionCreator, seller); - assertEq(auction.assetContract, assetContract); - assertEq(auction.tokenId, tokenId); - assertEq(auction.quantity, quantity); - assertEq(auction.currency, currency); - assertEq(auction.minimumBidAmount, minimumBidAmount); - assertEq(auction.buyoutBidAmount, buyoutBidAmount); - assertEq(auction.timeBufferInSeconds, timeBufferInSeconds); - assertEq(auction.bidBufferBps, bidBufferBps); - assertEq(auction.startTimestamp, startTimestamp); - assertEq(auction.endTimestamp, endTimestamp); - assertEq(uint256(auction.tokenType), uint256(IEnglishAuctions.TokenType.ERC721)); - } - - function test_revert_createAuction_notOwnerOfAuctionedToken() public { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Don't mint to 'token to be auctioned' to the seller. - address someWallet = getActor(1000); - _setupERC721BalanceForSeller(someWallet, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), someWallet, tokenIds); - assertIsNotOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(someWallet); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - vm.expectRevert("ERC721: transfer from incorrect owner"); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_revert_createAuction_notApprovedMarketplaceToTransferToken() public { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Don't approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, false); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - vm.expectRevert("ERC721: caller is not token owner or approved"); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_revert_createAuction_auctioningZeroQuantity() public { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 0; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: auctioning zero quantity."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_revert_createAuction_invalidQuantity() public { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 2; // Listing more than `1` quantity - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: auctioning invalid quantity."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_revert_createAuction_noBidOrTimeBuffer() public { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 0; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: no time-buffer."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - - timeBufferInSeconds = 10 seconds; - bidBufferBps = 0; - - auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: no bid-buffer."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_revert_createAuction_invalidBidAmounts() public { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 10 ether; // set minimumBidAmount greater than buyoutBidAmount - uint256 buyoutBidAmount = 1 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: invalid bid amounts."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_revert_createAuction_invalidStartTimestamp() public { - uint256 blockTimestamp = 100 minutes; - // Set block.timestamp - vm.warp(blockTimestamp); - - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = uint64(blockTimestamp - 61 minutes); // start time is less than block timestamp. - uint64 endTimestamp = startTimestamp + 1; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: invalid timestamps."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_revert_createAuction_invalidEndTimestamp() public { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = startTimestamp - 1; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - vm.expectRevert("Marketplace: invalid timestamps."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_revert_createAuction_invalidAssetContract() public { - // Sample auction parameters. - address assetContract = address(erc20); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = startTimestamp - 1; - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - // Grant ERC20 token asset role. - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc20)); - - vm.prank(seller); - vm.expectRevert("Marketplace: auctioned token must be ERC1155 or ERC721."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_revert_createAuction_noListerRoleWhenRestrictionsActive() public { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - // Revoke LISTER_ROLE from seller. - vm.startPrank(marketplaceDeployer); - assertEq(Permissions(marketplace).hasRole(keccak256("LISTER_ROLE"), address(0)), false); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), seller); - assertEq(Permissions(marketplace).hasRole(keccak256("LISTER_ROLE"), seller), false); - - vm.stopPrank(); - - vm.prank(seller); - vm.expectRevert("!LISTER_ROLE"); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_revert_createAuction_noAssetRoleWhenRestrictionsActive() public { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - // Revoke ASSET_ROLE from token to list. - vm.startPrank(marketplaceDeployer); - assertEq(Permissions(marketplace).hasRole(keccak256("ASSET_ROLE"), address(0)), false); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(erc721)); - assertEq(Permissions(marketplace).hasRole(keccak256("ASSET_ROLE"), address(erc721)), false); - - vm.stopPrank(); - - vm.prank(seller); - vm.expectRevert("!ASSET_ROLE"); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - /*/////////////////////////////////////////////////////////////// - Cancel Auction - //////////////////////////////////////////////////////////////*/ - - function _setup_newAuction() private returns (uint256 auctionId) { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - auctionId = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function _setup_newAuction_nativeToken() private returns (uint256 auctionId) { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = NATIVE_TOKEN; - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - auctionId = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_state_cancelAuction() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.prank(seller); - EnglishAuctionsLogic(marketplace).cancelAuction(auctionId); - - // Test consequent states. - - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Total auction count should include deleted auctions too - assertEq(EnglishAuctionsLogic(marketplace).totalAuctions(), 1); - - // status should be `CANCELLED` - IEnglishAuctions.Auction memory cancelledAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - assertTrue(cancelledAuction.status == IEnglishAuctions.Status.CANCELLED); - } - - function test_revert_cancelAuction_bidsAlreadyMade() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - erc20.mint(buyer, 1 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 1 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 1 ether); - vm.stopPrank(); - - vm.prank(seller); - vm.expectRevert("Marketplace: bids already made."); - EnglishAuctionsLogic(marketplace).cancelAuction(auctionId); - } - - /*/////////////////////////////////////////////////////////////// - Bid In Auction - //////////////////////////////////////////////////////////////*/ - - function test_state_bidInAuction_firstBid() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - erc20.mint(buyer, 1 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 1 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 1 ether); - vm.stopPrank(); - - (address bidder, address currency, uint256 bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid( - auctionId - ); - - // Test consequent states. - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), marketplace, tokenIds); - assertEq(erc20.balanceOf(marketplace), 1 ether); - assertEq(erc20.balanceOf(buyer), 0); - assertEq(buyer, bidder); - assertEq(currency, address(erc20)); - assertEq(bidAmount, 1 ether); - } - - function test_state_bidInAuction_secondBid() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place first bid - erc20.mint(buyer, 1 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 1 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 1 ether); - vm.stopPrank(); - - (address bidder, address currency, uint256 bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid( - auctionId - ); - - // Test consequent states. - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), marketplace, tokenIds); - assertEq(erc20.balanceOf(marketplace), 1 ether); - assertEq(erc20.balanceOf(buyer), 0); - assertEq(buyer, bidder); - assertEq(currency, address(erc20)); - assertEq(bidAmount, 1 ether); - - // place second winning bid - erc20.mint(address(0x345), 2 ether); - vm.startPrank(address(0x345)); - erc20.approve(marketplace, 2 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 2 ether); - vm.stopPrank(); - - (bidder, currency, bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid(auctionId); - - // Test consequent states. - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), marketplace, tokenIds); - assertEq(erc20.balanceOf(marketplace), 2 ether); - assertEq(erc20.balanceOf(buyer), 1 ether); - assertEq(erc20.balanceOf(address(0x345)), 0); - assertEq(address(0x345), bidder); - assertEq(currency, address(erc20)); - assertEq(bidAmount, 2 ether); - } - - function test_state_bidInAuction_buyoutBid() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place first bid - erc20.mint(buyer, 1 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 1 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 1 ether); - vm.stopPrank(); - - (address bidder, address currency, uint256 bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid( - auctionId - ); - - // Test consequent states. - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), marketplace, tokenIds); - assertEq(erc20.balanceOf(marketplace), 1 ether); - assertEq(erc20.balanceOf(buyer), 0); - assertEq(buyer, bidder); - assertEq(currency, address(erc20)); - assertEq(bidAmount, 1 ether); - - // place buyout bid - erc20.mint(address(0x345), 10 ether); - vm.startPrank(address(0x345)); - erc20.approve(marketplace, 10 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 10 ether); - vm.stopPrank(); - - (bidder, currency, bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid(auctionId); - - // Test consequent states. - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), address(0x345), tokenIds); - assertEq(erc20.balanceOf(marketplace), 10 ether); - assertEq(erc20.balanceOf(buyer), 1 ether); - assertEq(erc20.balanceOf(address(0x345)), 0); - assertEq(address(0x345), bidder); - assertEq(currency, address(erc20)); - assertEq(bidAmount, 10 ether); - } - - function test_revert_bidInAuction_inactiveAuction() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - // place bid before start-time - erc20.mint(buyer, 1 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 1 ether); - vm.expectRevert("Marketplace: inactive auction."); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 1 ether); - vm.stopPrank(); - - // place bid after end-time - vm.warp(existingAuction.endTimestamp); - - erc20.mint(buyer, 1 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 1 ether); - vm.expectRevert("Marketplace: inactive auction."); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 1 ether); - vm.stopPrank(); - } - - function test_revert_bidInAuction_notOwnerOfBidTokens() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - vm.startPrank(buyer); - erc20.approve(marketplace, 1 ether); - vm.expectRevert("ERC20: transfer amount exceeds balance"); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 1 ether); - vm.stopPrank(); - } - - function test_revert_bidInAuction_notApprovedMarketplaceToTransferToken() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - erc20.mint(buyer, 1 ether); - vm.startPrank(buyer); - vm.expectRevert("ERC20: insufficient allowance"); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 1 ether); - vm.stopPrank(); - } - - function test_revert_bidInAuction_notNewWinningBid_firstBid() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place first bid less than minimum bid amount - erc20.mint(buyer, 0.5 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 0.5 ether); - vm.expectRevert("Marketplace: not winning bid."); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 0.5 ether); - vm.stopPrank(); - } - - function test_revert_bidInAuction_notNewWinningBid_secondBid() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place first bid - erc20.mint(buyer, 1 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 1 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 1 ether); - vm.stopPrank(); - - (address bidder, address currency, uint256 bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid( - auctionId - ); - - // Test consequent states. - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), marketplace, tokenIds); - assertEq(erc20.balanceOf(marketplace), 1 ether); - assertEq(erc20.balanceOf(buyer), 0); - assertEq(buyer, bidder); - assertEq(currency, address(erc20)); - assertEq(bidAmount, 1 ether); - - // place second bid less-than/equal-to previous winning bid - erc20.mint(address(0x345), 1 ether); - vm.startPrank(address(0x345)); - erc20.approve(marketplace, 1 ether); - vm.expectRevert("Marketplace: not winning bid."); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 1 ether); - vm.stopPrank(); - } - - function test_state_bidInAuction_nativeToken() public { - uint256 auctionId = _setup_newAuction_nativeToken(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - vm.deal(buyer, 10 ether); - vm.startPrank(buyer); - EnglishAuctionsLogic(marketplace).bidInAuction{ value: 1 ether }(auctionId, 1 ether); - vm.stopPrank(); - - (address bidder, address currency, uint256 bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid( - auctionId - ); - - // Test consequent states. - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), marketplace, tokenIds); - assertEq(weth.balanceOf(marketplace), 1 ether); - assertEq(buyer.balance, 9 ether); - assertEq(buyer, bidder); - assertEq(currency, NATIVE_TOKEN); - assertEq(bidAmount, 1 ether); - } - - /*/////////////////////////////////////////////////////////////// - Collect Auction Payout - //////////////////////////////////////////////////////////////*/ - - function test_state_collectAuctionPayout_buyoutBid() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place buyout bid - erc20.mint(buyer, 10 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 10 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 10 ether); - vm.stopPrank(); - - (address bidder, address currency, uint256 bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid( - auctionId - ); - - // Test consequent states. - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), buyer, tokenIds); - assertEq(erc20.balanceOf(marketplace), 10 ether); - assertEq(erc20.balanceOf(buyer), 0); - assertEq(buyer, bidder); - assertEq(currency, address(erc20)); - assertEq(bidAmount, 10 ether); - - uint256 defaultFee = (10 ether * 100) / 10_000; - - // collect auction payout - vm.prank(seller); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - - assertEq(erc20.balanceOf(marketplace), 0); - assertEq(erc20.balanceOf(seller), 10 ether - defaultFee); - assertEq(erc20.balanceOf(defaultFeeRecipient), defaultFee); - } - - function test_state_collectAuctionPayout_afterAuctionEnds() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - erc20.mint(buyer, 5 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 5 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 5 ether); - vm.stopPrank(); - - (address bidder, address currency, uint256 bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid( - auctionId - ); - - // Test consequent states. - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), marketplace, tokenIds); - assertEq(erc20.balanceOf(marketplace), 5 ether); - assertEq(erc20.balanceOf(buyer), 0); - assertEq(buyer, bidder); - assertEq(currency, address(erc20)); - assertEq(bidAmount, 5 ether); - - vm.warp(existingAuction.endTimestamp); - - // collect auction payout - vm.prank(seller); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - - uint256 defaultFee = (5 ether * 100) / 10_000; - - assertIsOwnerERC721(address(erc721), marketplace, tokenIds); - assertEq(erc20.balanceOf(marketplace), 0); - assertEq(erc20.balanceOf(seller), 5 ether - defaultFee); - assertEq(erc20.balanceOf(defaultFeeRecipient), defaultFee); - } - - function test_revert_collectAuctionPayout_auctionNotExpired() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - erc20.mint(buyer, 5 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 5 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 5 ether); - vm.stopPrank(); - - // collect auction payout before auction has ended - vm.prank(seller); - vm.expectRevert("Marketplace: auction still active."); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - } - - function test_revert_collectAuctionPayout_noBidsInAuction() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.endTimestamp); - - // collect auction payout without any bids made - vm.prank(seller); - vm.expectRevert("Marketplace: no bids were made."); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - } - - /*/////////////////////////////////////////////////////////////// - Collect Auction Tokens - //////////////////////////////////////////////////////////////*/ - - function test_state_collectAuctionTokens() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - erc20.mint(buyer, 5 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 5 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 5 ether); - vm.stopPrank(); - - (address bidder, address currency, uint256 bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid( - auctionId - ); - - // Test consequent states. - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), marketplace, tokenIds); - assertEq(erc20.balanceOf(marketplace), 5 ether); - assertEq(erc20.balanceOf(buyer), 0); - assertEq(buyer, bidder); - assertEq(currency, address(erc20)); - assertEq(bidAmount, 5 ether); - - vm.warp(existingAuction.endTimestamp); - - // collect auction tokens - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).collectAuctionTokens(auctionId); - - assertIsOwnerERC721(address(erc721), buyer, tokenIds); - assertEq(erc20.balanceOf(marketplace), 5 ether); - } - - function test_revert_collectAuctionTokens_auctionNotExpired() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - erc20.mint(buyer, 5 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 5 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 5 ether); - vm.stopPrank(); - - (address bidder, address currency, uint256 bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid( - auctionId - ); - - // Test consequent states. - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), marketplace, tokenIds); - assertEq(erc20.balanceOf(marketplace), 5 ether); - assertEq(erc20.balanceOf(buyer), 0); - assertEq(buyer, bidder); - assertEq(currency, address(erc20)); - assertEq(bidAmount, 5 ether); - - // collect auction tokens before auction has ended - vm.prank(buyer); - vm.expectRevert("Marketplace: auction still active."); - EnglishAuctionsLogic(marketplace).collectAuctionTokens(auctionId); - } - - /*/////////////////////////////////////////////////////////////// - View functions - //////////////////////////////////////////////////////////////*/ - - function test_state_isNewWinningBid() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - erc20.mint(buyer, 5 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 5 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 5 ether); - vm.stopPrank(); - - // check if new winning bid - assertTrue(EnglishAuctionsLogic(marketplace).isNewWinningBid(auctionId, 6 ether)); - assertFalse(EnglishAuctionsLogic(marketplace).isNewWinningBid(auctionId, 5 ether)); - assertFalse(EnglishAuctionsLogic(marketplace).isNewWinningBid(auctionId, 4 ether)); - } - - function test_revert_isNewWinningBid() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - erc20.mint(buyer, 5 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 5 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 5 ether); - vm.stopPrank(); - - // check winning bid for a non-existent auction - vm.expectRevert("Marketplace: invalid auction."); - EnglishAuctionsLogic(marketplace).isNewWinningBid(auctionId + 1, 6 ether); - } - - function test_state_getAllAuctions() public { - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 6); - - uint256[] memory auctionIds = new uint256[](5); - uint256[] memory tokenIds = new uint256[](5); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Sample auction parameters. - address assetContract = address(erc721); - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = uint64(block.timestamp); - uint64 endTimestamp = startTimestamp + 200; - - IEnglishAuctions.AuctionParameters memory auctionParams; - - for (uint256 i = 0; i < 5; i += 1) { - tokenIds[i] = i; - - // Auction tokens. - auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenIds[i], - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - auctionIds[i] = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - IEnglishAuctions.Auction[] memory activeAuctions = EnglishAuctionsLogic(marketplace).getAllAuctions(0, 4); - assertEq(activeAuctions.length, 5); - - for (uint256 i = 0; i < 5; i += 1) { - assertEq(activeAuctions[i].auctionId, auctionIds[i]); - assertEq(activeAuctions[i].auctionCreator, seller); - assertEq(activeAuctions[i].assetContract, assetContract); - assertEq(activeAuctions[i].tokenId, tokenIds[i]); - assertEq(activeAuctions[i].quantity, quantity); - assertEq(activeAuctions[i].currency, currency); - assertEq(activeAuctions[i].minimumBidAmount, minimumBidAmount); - assertEq(activeAuctions[i].buyoutBidAmount, buyoutBidAmount); - assertEq(activeAuctions[i].timeBufferInSeconds, timeBufferInSeconds); - assertEq(activeAuctions[i].bidBufferBps, bidBufferBps); - assertEq(activeAuctions[i].startTimestamp, startTimestamp); - assertEq(activeAuctions[i].endTimestamp, endTimestamp); - assertEq(uint256(activeAuctions[i].tokenType), uint256(IEnglishAuctions.TokenType.ERC721)); - } - } - - function test_state_getAllValidAuctions() public { - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 6); - - uint256[] memory auctionIds = new uint256[](5); - uint256[] memory tokenIds = new uint256[](5); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Sample auction parameters. - address assetContract = address(erc721); - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = uint64(block.timestamp); - uint64 endTimestamp = startTimestamp + 200; - - IEnglishAuctions.AuctionParameters memory auctionParams; - - for (uint256 i = 0; i < 5; i += 1) { - tokenIds[i] = i; - - // Auction tokens. - auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenIds[i], - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - auctionIds[i] = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - IEnglishAuctions.Auction[] memory activeAuctions = EnglishAuctionsLogic(marketplace).getAllValidAuctions(0, 4); - assertEq(activeAuctions.length, 5); - - for (uint256 i = 0; i < 5; i += 1) { - assertEq(activeAuctions[i].auctionId, auctionIds[i]); - assertEq(activeAuctions[i].auctionCreator, seller); - assertEq(activeAuctions[i].assetContract, assetContract); - assertEq(activeAuctions[i].tokenId, tokenIds[i]); - assertEq(activeAuctions[i].quantity, quantity); - assertEq(activeAuctions[i].currency, currency); - assertEq(activeAuctions[i].minimumBidAmount, minimumBidAmount); - assertEq(activeAuctions[i].buyoutBidAmount, buyoutBidAmount); - assertEq(activeAuctions[i].timeBufferInSeconds, timeBufferInSeconds); - assertEq(activeAuctions[i].bidBufferBps, bidBufferBps); - assertEq(activeAuctions[i].startTimestamp, startTimestamp); - assertEq(activeAuctions[i].endTimestamp, endTimestamp); - assertEq(uint256(activeAuctions[i].tokenType), uint256(IEnglishAuctions.TokenType.ERC721)); - } - - // create an inactive auction, and check the auctions returned - auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - 5, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp + 100, - endTimestamp - ); - - vm.prank(seller); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - - activeAuctions = EnglishAuctionsLogic(marketplace).getAllValidAuctions(0, 5); - assertEq(activeAuctions.length, 5); - } - - function test_state_isAuctionExpired() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - vm.warp(existingAuction.endTimestamp); - assertTrue(EnglishAuctionsLogic(marketplace).isAuctionExpired(auctionId)); - } - - function test_revert_isAuctionExpired() public { - uint256 auctionId = _setup_newAuction(); - - vm.expectRevert("Marketplace: invalid auction."); - EnglishAuctionsLogic(marketplace).isAuctionExpired(auctionId + 1); - } - - /*/////////////////////////////////////////////////////////////// - Audit POCs - //////////////////////////////////////////////////////////////*/ - - function test_state_collectAuctionPayout_buyoutBid_nativeToken() public { - uint256 auctionId = _setup_newAuction_nativeToken(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - vm.deal(buyer, 10 ether); - vm.startPrank(buyer); - EnglishAuctionsLogic(marketplace).bidInAuction{ value: 10 ether }(auctionId, 10 ether); - vm.stopPrank(); - - (address bidder, address currency, uint256 bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid( - auctionId - ); - - // Test consequent states. - // Seller is owner of token. - assertIsOwnerERC721(address(erc721), buyer, tokenIds); - assertEq(weth.balanceOf(marketplace), 10 ether); - assertEq(buyer.balance, 0 ether); - assertEq(buyer, bidder); - assertEq(currency, NATIVE_TOKEN); - assertEq(bidAmount, 10 ether); - - uint256 defaultFee = (10 ether * 100) / 10_000; - - vm.prank(seller); - // calls WETH.withdraw (which calls receive function of Marketplace) and sends native tokens to seller - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - assertEq(weth.balanceOf(marketplace), 0 ether); - assertEq(seller.balance, 10 ether - defaultFee); - assertEq(defaultFeeRecipient.balance, defaultFee); - - // sending eth directly should fail - vm.deal(address(this), 1 ether); - (bool success, ) = marketplace.call{ value: 1 ether }(""); - assertEq(success, false); - } - - function test_audit_native_tokens_locked() public { - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - // place buyout bid - erc20.mint(buyer, 10 ether); - vm.deal(buyer, 1 ether); - - vm.startPrank(buyer); - erc20.approve(marketplace, 10 ether); - - vm.expectRevert("Marketplace: invalid native tokens sent."); - EnglishAuctionsLogic(marketplace).bidInAuction{ value: 1 ether }(auctionId, 10 ether); - vm.stopPrank(); - - // No ether is temporary locked in contract - assertEq(marketplace.balance, 0); - } - - function test_revert_collectAuctionPayout_buyoutBid_poc() public { - /*/////////////////////////////////////////////////////////////// - Initial State - //////////////////////////////////////////////////////////////*/ - - // consider that market place already has 200 ETH worth of tokens from all bids made - erc20.mint(marketplace, 200 ether); - - /*/////////////////////////////////////////////////////////////// - Create Auction - //////////////////////////////////////////////////////////////*/ - - // Buyout bid : 10 ETH - uint256 auctionId = _setup_newAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - /*/////////////////////////////////////////////////////////////// - BID - //////////////////////////////////////////////////////////////*/ - - // place bid : 200 ETH - erc20.mint(buyer, 200 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 200 ether); - - vm.expectRevert("Marketplace: Bidding above buyout price."); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 200 ether); - vm.stopPrank(); - } - - function _setup_nativeTokenAuction() private returns (uint256 auctionId) { - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = NATIVE_TOKEN; - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - auctionId = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_revert_collectAuctionPayout_buyoutBid_nativeTokens_poc() public { - /*/////////////////////////////////////////////////////////////// - Initial State - //////////////////////////////////////////////////////////////*/ - - // consider that market place already has 200 ETH worth of tokens from all bids made - vm.deal(address(marketplace), 200 ether); - - /*/////////////////////////////////////////////////////////////// - Create Auction - //////////////////////////////////////////////////////////////*/ - - // Buyout bid : 10 ETH - uint256 auctionId = _setup_nativeTokenAuction(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = existingAuction.tokenId; - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc721)); - - vm.warp(existingAuction.startTimestamp); - - /*/////////////////////////////////////////////////////////////// - BID - //////////////////////////////////////////////////////////////*/ - - // place bid : 200 ETH - vm.deal(buyer, 200 ether); - vm.prank(buyer); - vm.expectRevert("Marketplace: Bidding above buyout price."); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 200 ether); - } -} - -contract BreitwieserTheCreator is BaseTest, IERC721Receiver, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - address private defaultFeeRecipient; - - function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) { - return IERC721Receiver.onERC721Received.selector; - } - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `EnglishAuctions` - address englishAuctions = address(new EnglishAuctionsLogic(address(weth))); - defaultFeeRecipient = EnglishAuctionsLogic(englishAuctions).DEFAULT_FEE_RECIPIENT(); - vm.label(englishAuctions, "EnglishAuctions_Extension"); - - // Extension: EnglishAuctionsLogic - Extension memory extension_englishAuctions; - extension_englishAuctions.metadata = ExtensionMetadata({ - name: "EnglishAuctionsLogic", - metadataURI: "ipfs://EnglishAuctions", - implementation: englishAuctions - }); - - extension_englishAuctions.functions = new ExtensionFunction[](12); - extension_englishAuctions.functions[0] = ExtensionFunction( - EnglishAuctionsLogic.totalAuctions.selector, - "totalAuctions()" - ); - extension_englishAuctions.functions[1] = ExtensionFunction( - EnglishAuctionsLogic.createAuction.selector, - "createAuction((address,uint256,uint256,address,uint256,uint256,uint64,uint64,uint64,uint64))" - ); - extension_englishAuctions.functions[2] = ExtensionFunction( - EnglishAuctionsLogic.cancelAuction.selector, - "cancelAuction(uint256)" - ); - extension_englishAuctions.functions[3] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionPayout.selector, - "collectAuctionPayout(uint256)" - ); - extension_englishAuctions.functions[4] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionTokens.selector, - "collectAuctionTokens(uint256)" - ); - extension_englishAuctions.functions[5] = ExtensionFunction( - EnglishAuctionsLogic.bidInAuction.selector, - "bidInAuction(uint256,uint256)" - ); - extension_englishAuctions.functions[6] = ExtensionFunction( - EnglishAuctionsLogic.isNewWinningBid.selector, - "isNewWinningBid(uint256,uint256)" - ); - extension_englishAuctions.functions[7] = ExtensionFunction( - EnglishAuctionsLogic.getAuction.selector, - "getAuction(uint256)" - ); - extension_englishAuctions.functions[8] = ExtensionFunction( - EnglishAuctionsLogic.getAllAuctions.selector, - "getAllAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[9] = ExtensionFunction( - EnglishAuctionsLogic.getAllValidAuctions.selector, - "getAllValidAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[10] = ExtensionFunction( - EnglishAuctionsLogic.getWinningBid.selector, - "getWinningBid(uint256)" - ); - extension_englishAuctions.functions[11] = ExtensionFunction( - EnglishAuctionsLogic.isAuctionExpired.selector, - "isAuctionExpired(uint256)" - ); - - extensions[0] = extension_englishAuctions; - } - - function _setupERC721BalanceForSeller(address _seller, uint256 _numOfTokens) private { - erc721.mint(_seller, _numOfTokens); - } - - function test_rob_as_creator() public { - ///////////////////////////// Setup: dummy NFT //////////////////////////// - - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 50 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 0; - uint64 endTimestamp = 200; - - // Mint the ERC721 tokens to seller. These tokens will be auctioned. - _setupERC721BalanceForSeller(seller, 1); - - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = tokenId; - assertIsOwnerERC721(address(erc721), seller, tokenIds); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - ////////////////////////////// Setup: auction tokens ////////////////////////////////// - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - vm.prank(seller); - uint256 auctionId = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - - /////////////////////////// Setup: marketplace has currency ///////////// - uint256 mbalance = 100 ether; - erc20.mint(marketplace, mbalance); - - /////////////////////////// Attack: win to drain /////////////////////////////////////////// - - // 1. Buy out the token. - assertEq(erc20.balanceOf(seller), 0); - erc20.mint(seller, buyoutBidAmount); - assertEq(erc20.balanceOf(seller), buyoutBidAmount); - - vm.startPrank(seller); - - erc20.approve(marketplace, buyoutBidAmount); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, buyoutBidAmount); - - // 2. Collect their own bid. - uint256 defaultFee = (buyoutBidAmount * 100) / 10_000; - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - assertEq(erc20.balanceOf(seller), buyoutBidAmount - defaultFee); - assertEq(erc20.balanceOf(defaultFeeRecipient), defaultFee); - - // 3. Profit. (FIXED) - - vm.expectRevert("Marketplace: payout already completed."); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - // EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - // assertEq(erc20.balanceOf(seller), buyoutBidAmount + mbalance); - } -} - -contract BreitwieserTheBidder is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `EnglishAuctions` - address englishAuctions = address(new EnglishAuctionsLogic(address(weth))); - vm.label(englishAuctions, "EnglishAuctions_Extension"); - - // Extension: EnglishAuctionsLogic - Extension memory extension_englishAuctions; - extension_englishAuctions.metadata = ExtensionMetadata({ - name: "EnglishAuctionsLogic", - metadataURI: "ipfs://EnglishAuctions", - implementation: englishAuctions - }); - - extension_englishAuctions.functions = new ExtensionFunction[](12); - extension_englishAuctions.functions[0] = ExtensionFunction( - EnglishAuctionsLogic.totalAuctions.selector, - "totalAuctions()" - ); - extension_englishAuctions.functions[1] = ExtensionFunction( - EnglishAuctionsLogic.createAuction.selector, - "createAuction((address,uint256,uint256,address,uint256,uint256,uint64,uint64,uint64,uint64))" - ); - extension_englishAuctions.functions[2] = ExtensionFunction( - EnglishAuctionsLogic.cancelAuction.selector, - "cancelAuction(uint256)" - ); - extension_englishAuctions.functions[3] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionPayout.selector, - "collectAuctionPayout(uint256)" - ); - extension_englishAuctions.functions[4] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionTokens.selector, - "collectAuctionTokens(uint256)" - ); - extension_englishAuctions.functions[5] = ExtensionFunction( - EnglishAuctionsLogic.bidInAuction.selector, - "bidInAuction(uint256,uint256)" - ); - extension_englishAuctions.functions[6] = ExtensionFunction( - EnglishAuctionsLogic.isNewWinningBid.selector, - "isNewWinningBid(uint256,uint256)" - ); - extension_englishAuctions.functions[7] = ExtensionFunction( - EnglishAuctionsLogic.getAuction.selector, - "getAuction(uint256)" - ); - extension_englishAuctions.functions[8] = ExtensionFunction( - EnglishAuctionsLogic.getAllAuctions.selector, - "getAllAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[9] = ExtensionFunction( - EnglishAuctionsLogic.getAllValidAuctions.selector, - "getAllValidAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[10] = ExtensionFunction( - EnglishAuctionsLogic.getWinningBid.selector, - "getWinningBid(uint256)" - ); - extension_englishAuctions.functions[11] = ExtensionFunction( - EnglishAuctionsLogic.isAuctionExpired.selector, - "isAuctionExpired(uint256)" - ); - - extensions[0] = extension_englishAuctions; - } - - function _setupERC721BalanceForSeller(address _seller, uint256 _numOfTokens) private { - erc721.mint(_seller, _numOfTokens); - } - - function test_rob_as_bidder() public { - address attacker = address(0xbeef); - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), attacker); - - // Condition: multiple copies in circulation and attacker has at least 1. - uint256 tokenId = 999; - // Victim. - erc1155.mint(seller, tokenId, 1); - erc1155.mint(attacker, tokenId, 1); - - ////////////////// Setup: auction 1 ////////////////// - - IEnglishAuctions.AuctionParameters memory auctionParams1; - { - address assetContract = address(erc1155); - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint256 qty = 1; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 0; - uint64 endTimestamp = 200; - auctionParams1 = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - qty, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - } - - vm.startPrank(seller); - - erc1155.setApprovalForAll(marketplace, true); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams1); - - assertEq(erc1155.balanceOf(marketplace, tokenId), 1, "Marketplace should have the token."); - - vm.stopPrank(); - - ////////////////// Attack: auction the 2nd and steal the 1st token ////////////////// - - // 1. Set up auction. - erc20.mint(attacker, 1); - - vm.startPrank(attacker); - - erc1155.setApprovalForAll(marketplace, true); - - IEnglishAuctions.AuctionParameters memory auctionParams2; - { - address assetContract = address(erc1155); - address currency = address(erc20); - uint256 minimumBidAmount = 1; - uint256 buyoutBidAmount = 1; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 0; - uint64 endTimestamp = 200; - auctionParams2 = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - 1, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - } - uint256 auctionId2 = EnglishAuctionsLogic(marketplace).createAuction(auctionParams2); - - assertEq(erc1155.balanceOf(marketplace, tokenId), 2, "Marketplace should have 2 tokens."); - - // 2. Bid and collect back token. - erc20.increaseAllowance(marketplace, 1); - // Bid a small amount: 1 wei. - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId2, 1); - - assertEq(erc1155.balanceOf(attacker, tokenId), 1, "Attack should have collected back their token."); - - // Note: Attacker does not collect payout, it sets auction quantity to 0 and prevent further token collections. - - // 3. Fixed: Profit. - assertEq(erc1155.balanceOf(marketplace, tokenId), 1); - - vm.expectRevert("Marketplace: payout already completed."); - EnglishAuctionsLogic(marketplace).collectAuctionTokens(auctionId2); - - // assertEq(erc1155.balanceOf(attacker, tokenId), 2, "Attacker should have collected the 2nd token for free."); - - vm.stopPrank(); - } -} - -contract IssueC3_MarketplaceEnglishAuctionsTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `EnglishAuctions` - address englishAuctions = address(new EnglishAuctionsLogic(address(weth))); - vm.label(englishAuctions, "EnglishAuctions_Extension"); - - // Extension: EnglishAuctionsLogic - Extension memory extension_englishAuctions; - extension_englishAuctions.metadata = ExtensionMetadata({ - name: "EnglishAuctionsLogic", - metadataURI: "ipfs://EnglishAuctions", - implementation: englishAuctions - }); - - extension_englishAuctions.functions = new ExtensionFunction[](12); - extension_englishAuctions.functions[0] = ExtensionFunction( - EnglishAuctionsLogic.totalAuctions.selector, - "totalAuctions()" - ); - extension_englishAuctions.functions[1] = ExtensionFunction( - EnglishAuctionsLogic.createAuction.selector, - "createAuction((address,uint256,uint256,address,uint256,uint256,uint64,uint64,uint64,uint64))" - ); - extension_englishAuctions.functions[2] = ExtensionFunction( - EnglishAuctionsLogic.cancelAuction.selector, - "cancelAuction(uint256)" - ); - extension_englishAuctions.functions[3] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionPayout.selector, - "collectAuctionPayout(uint256)" - ); - extension_englishAuctions.functions[4] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionTokens.selector, - "collectAuctionTokens(uint256)" - ); - extension_englishAuctions.functions[5] = ExtensionFunction( - EnglishAuctionsLogic.bidInAuction.selector, - "bidInAuction(uint256,uint256)" - ); - extension_englishAuctions.functions[6] = ExtensionFunction( - EnglishAuctionsLogic.isNewWinningBid.selector, - "isNewWinningBid(uint256,uint256)" - ); - extension_englishAuctions.functions[7] = ExtensionFunction( - EnglishAuctionsLogic.getAuction.selector, - "getAuction(uint256)" - ); - extension_englishAuctions.functions[8] = ExtensionFunction( - EnglishAuctionsLogic.getAllAuctions.selector, - "getAllAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[9] = ExtensionFunction( - EnglishAuctionsLogic.getAllValidAuctions.selector, - "getAllValidAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[10] = ExtensionFunction( - EnglishAuctionsLogic.getWinningBid.selector, - "getWinningBid(uint256)" - ); - extension_englishAuctions.functions[11] = ExtensionFunction( - EnglishAuctionsLogic.isAuctionExpired.selector, - "isAuctionExpired(uint256)" - ); - - extensions[0] = extension_englishAuctions; - } - - function _setupERC1155BalanceForSeller(address _seller, uint256 _numOfTokens) private { - erc1155.mint(_seller, 0, _numOfTokens); - } - - function _setup_newAuction_1155() private returns (uint256 auctionId) { - // Sample auction parameters. - address assetContract = address(erc1155); - uint256 tokenId = 0; - uint256 quantity = 2; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Mint the erc1155 tokens to seller. These tokens will be auctioned. - _setupERC1155BalanceForSeller(seller, 2); - - // Approve Marketplace to transfer token. - vm.prank(seller); - erc1155.setApprovalForAll(marketplace, true); - - // Auction tokens. - IEnglishAuctions.AuctionParameters memory auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - vm.prank(seller); - auctionId = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - function test_state_collectAuctionTokens_afterAuctionPayout() public { - uint256 auctionId = _setup_newAuction_1155(); - IEnglishAuctions.Auction memory existingAuction = EnglishAuctionsLogic(marketplace).getAuction(auctionId); - - // Verify existing auction at `auctionId` - assertEq(existingAuction.assetContract, address(erc1155)); - - vm.warp(existingAuction.startTimestamp); - - // place bid - erc20.mint(buyer, 5 ether); - vm.startPrank(buyer); - erc20.approve(marketplace, 5 ether); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 5 ether); - vm.stopPrank(); - - (address bidder, address currency, uint256 bidAmount) = EnglishAuctionsLogic(marketplace).getWinningBid( - auctionId - ); - - // Seller is owner of token. - assertEq(erc20.balanceOf(marketplace), 5 ether); - assertEq(erc20.balanceOf(buyer), 0); - assertEq(buyer, bidder); - assertEq(currency, address(erc20)); - assertEq(bidAmount, 5 ether); - - vm.warp(existingAuction.endTimestamp); - - // collect auction payout - vm.prank(seller); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - - // collect buyer token - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).collectAuctionTokens(auctionId); - - // token is NOT stuck in the marketplace - assertEq(erc1155.balanceOf(marketplace, 0), 0); - assertEq(erc1155.balanceOf(buyer, 0), 2); - } -} diff --git a/src/test/marketplace/Offers.t.sol b/src/test/marketplace/Offers.t.sol deleted file mode 100644 index fa9c2df9c..000000000 --- a/src/test/marketplace/Offers.t.sol +++ /dev/null @@ -1,1102 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test helper imports -import "../utils/BaseTest.sol"; - -// Test contracts and interfaces - -import { PluginMap, IPluginMap } from "contracts/extension/plugin/PluginMap.sol"; -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { MarketplaceV3, IPlatformFee } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { OffersLogic } from "contracts/prebuilts/marketplace/offers/OffersLogic.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC721Base } from "contracts/base/ERC721Base.sol"; -import { MockRoyaltyEngineV1 } from "../mocks/MockRoyaltyEngineV1.sol"; - -import { IOffers } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -contract MarketplaceOffersTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - address private defaultFeeRecipient; - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `Offers` - address offers = address(new OffersLogic()); - defaultFeeRecipient = OffersLogic(offers).DEFAULT_FEE_RECIPIENT(); - vm.label(offers, "Offers_Extension"); - - // Extension: OffersLogic - Extension memory extension_offers; - extension_offers.metadata = ExtensionMetadata({ - name: "OffersLogic", - metadataURI: "ipfs://Offers", - implementation: offers - }); - - extension_offers.functions = new ExtensionFunction[](7); - extension_offers.functions[0] = ExtensionFunction(OffersLogic.totalOffers.selector, "totalOffers()"); - extension_offers.functions[1] = ExtensionFunction( - OffersLogic.makeOffer.selector, - "makeOffer((address,uint256,uint256,address,uint256,uint256))" - ); - extension_offers.functions[2] = ExtensionFunction(OffersLogic.cancelOffer.selector, "cancelOffer(uint256)"); - extension_offers.functions[3] = ExtensionFunction(OffersLogic.acceptOffer.selector, "acceptOffer(uint256)"); - extension_offers.functions[4] = ExtensionFunction( - OffersLogic.getAllValidOffers.selector, - "getAllValidOffers(uint256,uint256)" - ); - extension_offers.functions[5] = ExtensionFunction( - OffersLogic.getAllOffers.selector, - "getAllOffers(uint256,uint256)" - ); - extension_offers.functions[6] = ExtensionFunction(OffersLogic.getOffer.selector, "getOffer(uint256)"); - - extensions[0] = extension_offers; - } - - function test_state_initial() public { - uint256 totalOffers = OffersLogic(marketplace).totalOffers(); - assertEq(totalOffers, 0); - } - - /*/////////////////////////////////////////////////////////////// - Royalty Tests (incl Royalty Engine / Registry) - //////////////////////////////////////////////////////////////*/ - - function _setupRoyaltyEngine() - private - returns ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory mockRecipients, - uint256[] memory mockAmounts - ) - { - mockRecipients = new address payable[](2); - mockAmounts = new uint256[](2); - - mockRecipients[0] = payable(address(0x12345)); - mockRecipients[1] = payable(address(0x56789)); - - mockAmounts[0] = 10; - mockAmounts[1] = 15; - - royaltyEngine = new MockRoyaltyEngineV1(mockRecipients, mockAmounts); - } - - function _setupOfferForRoyaltyTests(address erc721TokenAddress) private returns (uint256 offerId) { - // Sample offer parameters. - address assetContract = erc721TokenAddress; - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - offerId = OffersLogic(marketplace).makeOffer(offerParams); - } - - function _acceptOfferForRoyaltyTests(uint256 offerId) private returns (uint256 totalPrice) { - IOffers.Offer memory offer = OffersLogic(marketplace).getOffer(offerId); - - totalPrice = offer.totalPrice; - - // Approve Marketplace to transfer token. - vm.prank(seller); - IERC721(offer.assetContract).setApprovalForAll(marketplace, true); - - // Accept offer - vm.prank(seller); - OffersLogic(marketplace).acceptOffer(offerId); - } - - function test_royaltyEngine_tokenWithCustomRoyalties() public { - ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory customRoyaltyRecipients, - uint256[] memory customRoyaltyAmounts - ) = _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // 1. ========= Make offer ========= - - uint256 offerId = _setupOfferForRoyaltyTests(address(erc721)); - - // 2. ========= Accept offer ========= - - // Mint the ERC721 tokens to seller. These tokens will be sold. - erc721.mint(seller, 1); - uint256 totalPrice = _acceptOfferForRoyaltyTests(offerId); - - // 3. ======== Check balances after royalty payments ======== - - { - uint256 defaultFee = (totalPrice * 100) / 10_000; - - // Royalty recipients receive correct amounts - assertBalERC20Eq(address(erc20), customRoyaltyRecipients[0], customRoyaltyAmounts[0]); - assertBalERC20Eq(address(erc20), customRoyaltyRecipients[1], customRoyaltyAmounts[1]); - - // Seller gets total price minus royalty amounts - assertBalERC20Eq( - address(erc20), - seller, - totalPrice - customRoyaltyAmounts[0] - customRoyaltyAmounts[1] - defaultFee - ); - assertBalERC20Eq(address(erc20), defaultFeeRecipient, defaultFee); - } - } - - function test_royaltyEngine_tokenWithERC2981() public { - (MockRoyaltyEngineV1 royaltyEngine, , ) = _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // create token with ERC2981 - address royaltyRecipient = address(0x12345); - uint128 royaltyBps = 10; - ERC721Base nft2981 = new ERC721Base(address(0x12345), "NFT 2981", "NFT2981", royaltyRecipient, royaltyBps); - // Mint the ERC721 tokens to seller. These tokens will be sold. - vm.prank(address(0x12345)); - nft2981.mintTo(seller, ""); - - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(nft2981)); - - // 1. ========= Make offer ========= - - uint256 offerId = _setupOfferForRoyaltyTests(address(nft2981)); - - // 2. ========= Accept offer ========= - - uint256 totalPrice = _acceptOfferForRoyaltyTests(offerId); - - // 3. ======== Check balances after royalty payments ======== - - { - uint256 defaultFee = (totalPrice * 100) / 10_000; - uint256 royaltyAmount = (royaltyBps * totalPrice) / 10_000; - // Royalty recipient receives correct amounts - assertBalERC20Eq(address(erc20), royaltyRecipient, royaltyAmount); - - // Seller gets total price minus royalty amount - assertBalERC20Eq(address(erc20), seller, totalPrice - royaltyAmount - defaultFee); - assertBalERC20Eq(address(erc20), defaultFeeRecipient, defaultFee); - } - } - - function test_noRoyaltyEngine_defaultERC2981Token() public { - // create token with ERC2981 - address royaltyRecipient = address(0x12345); - uint128 royaltyBps = 10; - ERC721Base nft2981 = new ERC721Base(address(0x12345), "NFT 2981", "NFT2981", royaltyRecipient, royaltyBps); - vm.prank(address(0x12345)); - nft2981.mintTo(seller, ""); - - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(nft2981)); - - // 1. ========= Make offer ========= - - uint256 offerId = _setupOfferForRoyaltyTests(address(nft2981)); - - // 2. ========= Accept offer ========= - - uint256 totalPrice = _acceptOfferForRoyaltyTests(offerId); - - // 3. ======== Check balances after royalty payments ======== - - { - uint256 defaultFee = (totalPrice * 100) / 10_000; - - uint256 royaltyAmount = (royaltyBps * totalPrice) / 10_000; - // Royalty recipient receives correct amounts - assertBalERC20Eq(address(erc20), royaltyRecipient, royaltyAmount); - - // Seller gets total price minus royalty amount - assertBalERC20Eq(address(erc20), seller, totalPrice - royaltyAmount - defaultFee); - assertBalERC20Eq(address(erc20), defaultFeeRecipient, defaultFee); - } - } - - function test_royaltyEngine_correctlyDistributeAllFees() public { - ( - MockRoyaltyEngineV1 royaltyEngine, - address payable[] memory customRoyaltyRecipients, - uint256[] memory customRoyaltyAmounts - ) = _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // Set platform fee on marketplace - address platformFeeRecipient = marketplaceDeployer; - uint128 platformFeeBps = 5; - vm.prank(marketplaceDeployer); - IPlatformFee(marketplace).setPlatformFeeInfo(platformFeeRecipient, platformFeeBps); - - // 1. ========= Make offer ========= - - uint256 offerId = _setupOfferForRoyaltyTests(address(erc721)); - - // 2. ========= Accept offer ========= - - // Mint the ERC721 tokens to seller. These tokens will be sold. - erc721.mint(seller, 1); - uint256 totalPrice = _acceptOfferForRoyaltyTests(offerId); - - // 3. ======== Check balances after royalty payments ======== - - { - uint256 defaultFee = (totalPrice * 100) / 10_000; - - // Royalty recipients receive correct amounts - assertBalERC20Eq(address(erc20), customRoyaltyRecipients[0], customRoyaltyAmounts[0]); - assertBalERC20Eq(address(erc20), customRoyaltyRecipients[1], customRoyaltyAmounts[1]); - - // Platform fee recipient - uint256 platformFeeAmount = (platformFeeBps * totalPrice) / 10_000; - assertBalERC20Eq(address(erc20), platformFeeRecipient, platformFeeAmount); - - // Seller gets total price minus royalty amounts - assertBalERC20Eq( - address(erc20), - seller, - totalPrice - customRoyaltyAmounts[0] - customRoyaltyAmounts[1] - platformFeeAmount - defaultFee - ); - - assertBalERC20Eq(address(erc20), defaultFeeRecipient, defaultFee); - } - } - - function test_revert_feesExceedTotalPrice() public { - (MockRoyaltyEngineV1 royaltyEngine, , ) = _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // Set platform fee on marketplace - address platformFeeRecipient = marketplaceDeployer; - uint128 platformFeeBps = 9900; // equal to max bps 10_000 or 100% with 100 bps default - vm.prank(marketplaceDeployer); - IPlatformFee(marketplace).setPlatformFeeInfo(platformFeeRecipient, platformFeeBps); - - // 1. ========= Make offer ========= - - uint256 offerId = _setupOfferForRoyaltyTests(address(erc721)); - - // 2. ========= Accept offer ========= - - // Mint the ERC721 tokens to seller. These tokens will be sold. - erc721.mint(seller, 1); - - IOffers.Offer memory offer = OffersLogic(marketplace).getOffer(offerId); - - // Approve Marketplace to transfer token. - vm.prank(seller); - IERC721(offer.assetContract).setApprovalForAll(marketplace, true); - - // Accept offer - vm.expectRevert("fees exceed the price"); - vm.prank(seller); - OffersLogic(marketplace).acceptOffer(offerId); - } - - /*/////////////////////////////////////////////////////////////// - Make Offer - //////////////////////////////////////////////////////////////*/ - - function test_state_makeOffer() public { - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - uint256 offerId = OffersLogic(marketplace).makeOffer(offerParams); - - // Test consequent state of the contract. - - // Total offers incremented - assertEq(OffersLogic(marketplace).totalOffers(), 1); - - // Fetch listing and verify state. - IOffers.Offer memory offer = OffersLogic(marketplace).getOffer(offerId); - - assertEq(offer.offerId, offerId); - assertEq(offer.offeror, buyer); - assertEq(offer.assetContract, assetContract); - assertEq(offer.tokenId, tokenId); - assertEq(offer.quantity, quantity); - assertEq(offer.currency, currency); - assertEq(offer.totalPrice, totalPrice); - assertEq(offer.expirationTimestamp, expirationTimestamp); - assertEq(uint256(offer.tokenType), uint256(IOffers.TokenType.ERC721)); - } - - function test_revert_makeOffer_notOwnerOfOfferedTokens() public { - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // Approve Marketplace to transfer currency tokens. (without owning) - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - vm.expectRevert("Marketplace: insufficient currency balance."); - OffersLogic(marketplace).makeOffer(offerParams); - } - - function test_revert_makeOffer_notApprovedMarketplaceToTransferTokens() public { - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // mint total-price to buyer, but not approved to marketplace - erc20.mint(buyer, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - vm.expectRevert("Marketplace: insufficient currency balance."); - OffersLogic(marketplace).makeOffer(offerParams); - } - - function test_revert_makeOffer_wantedZeroTokens() public { - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 0; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - vm.expectRevert("Marketplace: wanted zero tokens."); - OffersLogic(marketplace).makeOffer(offerParams); - } - - function test_revert_makeOffer_invalidQuantity() public { - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 2; // Asking for more than `1` quantity of erc721 tokenId - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - vm.expectRevert("Marketplace: wanted invalid quantity."); - OffersLogic(marketplace).makeOffer(offerParams); - } - - function test_revert_makeOffer_invalidExpirationTimestamp() public { - uint256 blockTimestamp = 100 minutes; - // Set block.timestamp - vm.warp(blockTimestamp); - - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = blockTimestamp - 61 minutes; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - vm.expectRevert("Marketplace: invalid expiration timestamp."); - OffersLogic(marketplace).makeOffer(offerParams); - } - - function test_revert_makeOffer_invalidAssetContract() public { - // Sample offer parameters. - address assetContract = address(erc20); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = block.timestamp; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - // Grant ERC20 token asset role. - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc20)); - - vm.prank(buyer); - vm.expectRevert("Marketplace: token must be ERC1155 or ERC721."); - OffersLogic(marketplace).makeOffer(offerParams); - } - - function test_revert_createListing_noAssetRoleWhenRestrictionsActive() public { - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = block.timestamp; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - // Revoke ASSET_ROLE from token to list. - vm.startPrank(marketplaceDeployer); - assertEq(Permissions(marketplace).hasRole(keccak256("ASSET_ROLE"), address(0)), false); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(erc721)); - assertEq(Permissions(marketplace).hasRole(keccak256("ASSET_ROLE"), address(erc721)), false); - - vm.stopPrank(); - - vm.prank(buyer); - vm.expectRevert("!ASSET_ROLE"); - OffersLogic(marketplace).makeOffer(offerParams); - } - - /*/////////////////////////////////////////////////////////////// - Cancel Offer - //////////////////////////////////////////////////////////////*/ - - function test_state_cancelOffer() public { - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - uint256 offerId = OffersLogic(marketplace).makeOffer(offerParams); - - IOffers.Offer memory offer = OffersLogic(marketplace).getOffer(offerId); - - assertEq(offer.offerId, offerId); - assertEq(offer.offeror, buyer); - assertEq(offer.assetContract, assetContract); - assertEq(offer.tokenId, tokenId); - assertEq(offer.quantity, quantity); - assertEq(offer.currency, currency); - assertEq(offer.totalPrice, totalPrice); - assertEq(offer.expirationTimestamp, expirationTimestamp); - assertEq(uint256(offer.tokenType), uint256(IOffers.TokenType.ERC721)); - - vm.prank(buyer); - OffersLogic(marketplace).cancelOffer(offerId); - - // Total offers count shouldn't change - assertEq(OffersLogic(marketplace).totalOffers(), 1); - - // status should be `CANCELLED` - IOffers.Offer memory cancelledOffer = OffersLogic(marketplace).getOffer(offerId); - assertTrue(cancelledOffer.status == IOffers.Status.CANCELLED); - } - - function test_revert_cancelOffer_callerNotOfferor() public { - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - uint256 offerId = OffersLogic(marketplace).makeOffer(offerParams); - - vm.prank(address(0x345)); - vm.expectRevert("!Offeror"); - OffersLogic(marketplace).cancelOffer(offerId); - } - - /*/////////////////////////////////////////////////////////////// - Accept Offer - //////////////////////////////////////////////////////////////*/ - - function test_state_acceptOffer() public { - // set owner of NFT - erc721.mint(seller, 1); - - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - uint256 offerId = OffersLogic(marketplace).makeOffer(offerParams); - - // accept offer - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - OffersLogic(marketplace).acceptOffer(offerId); - vm.stopPrank(); - - // Total offers count shouldn't change - assertEq(OffersLogic(marketplace).totalOffers(), 1); - - // status should be `COMPLETED` - IOffers.Offer memory completedOffer = OffersLogic(marketplace).getOffer(offerId); - assertTrue(completedOffer.status == IOffers.Status.COMPLETED); - - uint256 defaultFee = (totalPrice * 100) / 10_000; - // check states after accepting offer - assertEq(erc721.ownerOf(tokenId), buyer); - assertEq(erc20.balanceOf(seller), totalPrice - defaultFee); - assertEq(erc20.balanceOf(defaultFeeRecipient), defaultFee); - assertEq(erc20.balanceOf(buyer), 0); - } - - function test_revert_acceptOffer_notOwnedRequiredTokens() public { - // set owner of NFT to address other than seller - erc721.mint(address(0x345), 1); - - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. (but not owned) - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - uint256 offerId = OffersLogic(marketplace).makeOffer(offerParams); - - // accept offer - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - vm.expectRevert("Marketplace: not owner or approved tokens."); - OffersLogic(marketplace).acceptOffer(offerId); - vm.stopPrank(); - } - - function test_revert_acceptOffer_notApprovedMarketplaceToTransferOfferedTokens() public { - // set owner of NFT - erc721.mint(seller, 1); - - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. (but not owned) - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - uint256 offerId = OffersLogic(marketplace).makeOffer(offerParams); - - // accept offer, without approving NFT to marketplace - vm.startPrank(seller); - vm.expectRevert("Marketplace: not owner or approved tokens."); - OffersLogic(marketplace).acceptOffer(offerId); - vm.stopPrank(); - } - - function test_revert_acceptOffer_offerorBalanceLessThanPrice() public { - // set owner of NFT - erc721.mint(seller, 1); - - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. (but not owned) - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - uint256 offerId = OffersLogic(marketplace).makeOffer(offerParams); - - // reduce erc20 balance of buyer - vm.prank(buyer); - erc20.burn(totalPrice); - - // accept offer - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - vm.expectRevert("Marketplace: insufficient currency balance."); - OffersLogic(marketplace).acceptOffer(offerId); - vm.stopPrank(); - } - - function test_revert_acceptOffer_notApprovedMarketplaceToTransferPrice() public { - // set owner of NFT - erc721.mint(seller, 1); - - // Sample offer parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - // mint total-price to buyer - erc20.mint(buyer, totalPrice); - - // Approve Marketplace to transfer currency tokens. (but not owned) - vm.prank(buyer); - erc20.approve(marketplace, totalPrice); - - // Make offer. - IOffers.OfferParams memory offerParams = IOffers.OfferParams( - assetContract, - tokenId, - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - uint256 offerId = OffersLogic(marketplace).makeOffer(offerParams); - - // remove erc20 approval - vm.prank(buyer); - erc20.approve(marketplace, 0); - - // accept offer - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - vm.expectRevert("Marketplace: insufficient currency balance."); - OffersLogic(marketplace).acceptOffer(offerId); - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - View functions - //////////////////////////////////////////////////////////////*/ - - function test_state_getAllOffers() public { - uint256[] memory offerIds = new uint256[](5); - uint256[] memory tokenIds = new uint256[](5); - - // mint total-price to buyer - erc20.mint(buyer, 1000 ether); - - // Approve Marketplace to transfer currency tokens. (but not owned) - vm.prank(buyer); - erc20.approve(marketplace, 1000 ether); - - // Sample offer parameters. - address assetContract = address(erc721); - uint256 quantity = 1; - address currency = address(erc20); - uint256 totalPrice = 1 ether; - uint256 expirationTimestamp = 200; - - IOffers.OfferParams memory offerParams; - - for (uint256 i = 0; i < 5; i += 1) { - tokenIds[i] = i; - - // make offer - offerParams = IOffers.OfferParams( - assetContract, - tokenIds[i], - quantity, - currency, - totalPrice, - expirationTimestamp - ); - - vm.prank(buyer); - offerIds[i] = OffersLogic(marketplace).makeOffer(offerParams); - } - - IOffers.Offer[] memory allOffers = OffersLogic(marketplace).getAllOffers(0, 4); - assertEq(allOffers.length, 5); - - for (uint256 i = 0; i < 5; i += 1) { - assertEq(allOffers[i].offerId, offerIds[i]); - assertEq(allOffers[i].offeror, buyer); - assertEq(allOffers[i].assetContract, assetContract); - assertEq(allOffers[i].tokenId, tokenIds[i]); - assertEq(allOffers[i].quantity, quantity); - assertEq(allOffers[i].currency, currency); - assertEq(allOffers[i].totalPrice, totalPrice); - assertEq(allOffers[i].expirationTimestamp, expirationTimestamp); - assertEq(uint256(allOffers[i].tokenType), uint256(IOffers.TokenType.ERC721)); - } - } - - function test_state_getAllValidOffers() public { - uint256[] memory offerIds = new uint256[](5); - uint256[] memory tokenIds = new uint256[](5); - - // mint total-price to buyer - erc20.mint(buyer, 5 ether); - - // Approve Marketplace to transfer currency tokens. (but not owned) - vm.prank(buyer); - erc20.approve(marketplace, 5 ether); - - // Sample offer parameters. - address assetContract = address(erc721); - uint256 quantity = 1; - address currency = address(erc20); - uint256 expirationTimestamp = 200; - - IOffers.OfferParams memory offerParams; - - for (uint256 i = 0; i < 5; i += 1) { - tokenIds[i] = i; - - // make offer, with total-price as i - offerParams = IOffers.OfferParams( - assetContract, - tokenIds[i], - quantity, - currency, - (i + 1) * 1 ether, - expirationTimestamp - ); - - vm.prank(buyer); - offerIds[i] = OffersLogic(marketplace).makeOffer(offerParams); - } - - vm.prank(buyer); - erc20.burn(2 ether); // reduce balance to make some offers invalid - - IOffers.Offer[] memory allOffers = OffersLogic(marketplace).getAllValidOffers(0, 4); - assertEq(allOffers.length, 3); - - for (uint256 i = 0; i < 3; i += 1) { - assertEq(allOffers[i].offerId, offerIds[i]); - assertEq(allOffers[i].offeror, buyer); - assertEq(allOffers[i].assetContract, assetContract); - assertEq(allOffers[i].tokenId, tokenIds[i]); - assertEq(allOffers[i].quantity, quantity); - assertEq(allOffers[i].currency, currency); - assertEq(allOffers[i].totalPrice, (i + 1) * 1 ether); - assertEq(allOffers[i].expirationTimestamp, expirationTimestamp); - assertEq(uint256(allOffers[i].tokenType), uint256(IOffers.TokenType.ERC721)); - } - - // create an offer, and check the offers returned post its expiry - offerParams = IOffers.OfferParams(assetContract, 5, quantity, currency, 10, 10); - - vm.prank(buyer); - OffersLogic(marketplace).makeOffer(offerParams); - - vm.warp(10); - allOffers = OffersLogic(marketplace).getAllValidOffers(0, 5); - assertEq(allOffers.length, 3); - } -} diff --git a/src/test/marketplace/direct-listings/_payout/_payout.t.sol b/src/test/marketplace/direct-listings/_payout/_payout.t.sol deleted file mode 100644 index 5f8133d54..000000000 --- a/src/test/marketplace/direct-listings/_payout/_payout.t.sol +++ /dev/null @@ -1,355 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { PlatformFee } from "contracts/extension/PlatformFee.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { MarketplaceV3 } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { DirectListingsLogic } from "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol"; -import { IDirectListings } from "contracts/prebuilts/marketplace/IMarketplace.sol"; -import { MockRoyaltyEngineV1 } from "../../../mocks/MockRoyaltyEngineV1.sol"; - -contract PayoutTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - address private defaultFeeRecipient; - - // Default listing parameters - IDirectListings.ListingParameters internal listingParams; - uint256 internal listingId = 0; - - // Events to test - - /// @notice Emitted when a listing is updated. - event UpdatedListing( - address indexed listingCreator, - uint256 indexed listingId, - address indexed assetContract, - IDirectListings.Listing listing - ); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), platformFeeRecipient, uint16(platformFeeBps)) - ) - ) - ); - - // Setup listing params - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100 minutes; - uint128 endTimestamp = 200 minutes; - bool reserved = false; - - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Mint 1 ERC721 NFT to seller - erc721.mint(seller, listingParams.quantity); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `DirectListings` - address directListings = address(new DirectListingsLogic(address(weth))); - defaultFeeRecipient = DirectListingsLogic(directListings).DEFAULT_FEE_RECIPIENT(); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "DirectListingsLogic", - metadataURI: "ipfs://DirectListings", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](13); - extension_directListings.functions[0] = ExtensionFunction( - DirectListingsLogic.totalListings.selector, - "totalListings()" - ); - extension_directListings.functions[1] = ExtensionFunction( - DirectListingsLogic.isBuyerApprovedForListing.selector, - "isBuyerApprovedForListing(uint256,address)" - ); - extension_directListings.functions[2] = ExtensionFunction( - DirectListingsLogic.isCurrencyApprovedForListing.selector, - "isCurrencyApprovedForListing(uint256,address)" - ); - extension_directListings.functions[3] = ExtensionFunction( - DirectListingsLogic.currencyPriceForListing.selector, - "currencyPriceForListing(uint256,address)" - ); - extension_directListings.functions[4] = ExtensionFunction( - DirectListingsLogic.createListing.selector, - "createListing((address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[5] = ExtensionFunction( - DirectListingsLogic.updateListing.selector, - "updateListing(uint256,(address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[6] = ExtensionFunction( - DirectListingsLogic.cancelListing.selector, - "cancelListing(uint256)" - ); - extension_directListings.functions[7] = ExtensionFunction( - DirectListingsLogic.approveBuyerForListing.selector, - "approveBuyerForListing(uint256,address,bool)" - ); - extension_directListings.functions[8] = ExtensionFunction( - DirectListingsLogic.approveCurrencyForListing.selector, - "approveCurrencyForListing(uint256,address,uint256)" - ); - extension_directListings.functions[9] = ExtensionFunction( - DirectListingsLogic.buyFromListing.selector, - "buyFromListing(uint256,address,uint256,address,uint256)" - ); - extension_directListings.functions[10] = ExtensionFunction( - DirectListingsLogic.getAllListings.selector, - "getAllListings(uint256,uint256)" - ); - extension_directListings.functions[11] = ExtensionFunction( - DirectListingsLogic.getAllValidListings.selector, - "getAllValidListings(uint256,uint256)" - ); - extension_directListings.functions[12] = ExtensionFunction( - DirectListingsLogic.getListing.selector, - "getListing(uint256)" - ); - - extensions[0] = extension_directListings; - } - - address payable[] internal mockRecipients; - uint256[] internal mockAmounts; - MockRoyaltyEngineV1 internal royaltyEngine; - - function _setupRoyaltyEngine() private { - mockRecipients.push(payable(address(0x12345))); - mockRecipients.push(payable(address(0x56789))); - - mockAmounts.push(10 ether); - mockAmounts.push(15 ether); - - royaltyEngine = new MockRoyaltyEngineV1(mockRecipients, mockAmounts); - } - - function _setupListingForRoyaltyTests(address erc721TokenAddress) private returns (uint256 _listingId) { - // Sample listing parameters. - address assetContract = erc721TokenAddress; - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 100 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = false; - - // Approve Marketplace to transfer token. - vm.prank(seller); - IERC721(erc721TokenAddress).setApprovalForAll(marketplace, true); - - // List tokens. - IDirectListings.ListingParameters memory listingParameters = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - vm.prank(seller); - _listingId = DirectListingsLogic(marketplace).createListing(listingParameters); - } - - function _buyFromListingForRoyaltyTests(uint256 _listingId) private returns (uint256 totalPrice) { - IDirectListings.Listing memory listing = DirectListingsLogic(marketplace).getListing(_listingId); - - address buyFor = buyer; - uint256 quantityToBuy = listing.quantity; - address currency = listing.currency; - uint256 pricePerToken = listing.pricePerToken; - totalPrice = pricePerToken * quantityToBuy; - - // Mint requisite total price to buyer. - erc20.mint(buyer, totalPrice); - - // Approve marketplace to transfer currency - vm.prank(buyer); - erc20.increaseAllowance(marketplace, totalPrice); - - // Buy tokens from listing. - vm.warp(listing.startTimestamp); - vm.prank(buyer); - DirectListingsLogic(marketplace).buyFromListing(_listingId, buyFor, quantityToBuy, currency, totalPrice); - } - - function test_payout_whenZeroRoyaltyRecipients() public { - // 1. ========= Create listing ========= - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - listingId = DirectListingsLogic(marketplace).createListing(listingParams); - vm.stopPrank(); - - // 2. ========= Buy from listing ========= - - uint256 totalPrice = listingParams.pricePerToken; - - // Mint requisite total price to buyer. - erc20.mint(buyer, totalPrice); - - // Approve marketplace to transfer currency - vm.prank(buyer); - erc20.increaseAllowance(marketplace, totalPrice); - - // Buy tokens from listing. - vm.warp(listingParams.startTimestamp); - vm.prank(buyer); - DirectListingsLogic(marketplace).buyFromListing( - listingId, - buyer, - listingParams.quantity, - listingParams.currency, - totalPrice - ); - - // 3. ======== Check balances after royalty payments ======== - - uint256 platformFees = (totalPrice * platformFeeBps) / 10_000; - - { - uint256 defaultFee = (totalPrice * 100) / 10_000; - // Platform fee recipient receives correct amount - assertBalERC20Eq(address(erc20), platformFeeRecipient, platformFees); - - // Seller gets total price minus royalty amounts - assertBalERC20Eq(address(erc20), seller, totalPrice - platformFees - defaultFee); - - assertBalERC20Eq(address(erc20), defaultFeeRecipient, defaultFee); - } - } - - modifier whenNonZeroRoyaltyRecipients() { - _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - _; - } - - function test_payout_whenInsufficientFundsToPayRoyaltyAfterPlatformFeePayout() public whenNonZeroRoyaltyRecipients { - vm.prank(marketplaceDeployer); - PlatformFee(marketplace).setPlatformFeeInfo(platformFeeRecipient, 9899); // 99.99% fees with 100 bps default - - // Mint the ERC721 tokens to seller. These tokens will be listed. - erc721.mint(seller, 1); - listingId = _setupListingForRoyaltyTests(address(erc721)); - - IDirectListings.Listing memory listing = DirectListingsLogic(marketplace).getListing(listingId); - - address buyFor = buyer; - uint256 quantityToBuy = listing.quantity; - address currency = listing.currency; - uint256 pricePerToken = listing.pricePerToken; - uint256 totalPrice = pricePerToken * quantityToBuy; - - // Mint requisite total price to buyer. - erc20.mint(buyer, totalPrice); - - // Approve marketplace to transfer currency - vm.prank(buyer); - erc20.increaseAllowance(marketplace, totalPrice); - - // Buy tokens from listing. - vm.warp(listing.startTimestamp); - vm.prank(buyer); - vm.expectRevert("fees exceed the price"); - DirectListingsLogic(marketplace).buyFromListing(listingId, buyFor, quantityToBuy, currency, totalPrice); - } - - function test_payout_whenSufficientFundsToPayRoyaltyAfterPlatformFeePayout() public whenNonZeroRoyaltyRecipients { - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - // 1. ========= Create listing ========= - - // Mint the ERC721 tokens to seller. These tokens will be listed. - erc721.mint(seller, 1); - listingId = _setupListingForRoyaltyTests(address(erc721)); - - // 2. ========= Buy from listing ========= - - uint256 totalPrice = _buyFromListingForRoyaltyTests(listingId); - - // 3. ======== Check balances after royalty payments ======== - - uint256 platformFees = (totalPrice * platformFeeBps) / 10_000; - - { - uint256 defaultFee = (totalPrice * 100) / 10_000; - // Royalty recipients receive correct amounts - assertBalERC20Eq(address(erc20), mockRecipients[0], mockAmounts[0]); - assertBalERC20Eq(address(erc20), mockRecipients[1], mockAmounts[1]); - - // Platform fee recipient receives correct amount - assertBalERC20Eq(address(erc20), platformFeeRecipient, platformFees); - - // Seller gets total price minus royalty amounts - assertBalERC20Eq( - address(erc20), - seller, - totalPrice - mockAmounts[0] - mockAmounts[1] - platformFees - defaultFee - ); - assertBalERC20Eq(address(erc20), defaultFeeRecipient, defaultFee); - } - } -} diff --git a/src/test/marketplace/direct-listings/_payout/_payout.tree b/src/test/marketplace/direct-listings/_payout/_payout.tree deleted file mode 100644 index 3d09e5d13..000000000 --- a/src/test/marketplace/direct-listings/_payout/_payout.tree +++ /dev/null @@ -1,17 +0,0 @@ -function _payout( - address _payer, - address _payee, - address _currencyToUse, - uint256 _totalPayoutAmount, - Listing memory _listing -) -├── when there are zero royalty recipients ✅ -│ ├── it should transfer platform fee from payer to platform fee recipient -│ └── it should transfer remainder of currency from payer to payee -└── when there are non-zero royalty recipients - ├── when the total royalty payout exceeds remainder payout after having paid platform fee - │ └── it should revert ✅ - └── when the total royalty payout does not exceed remainder payout after having paid platform fee ✅ - ├── it should transfer platform fee from payer to platform fee recipient - ├── it should transfer royalty fee from payer to royalty recipients - └── it should transfer remainder of currency from payer to payee \ No newline at end of file diff --git a/src/test/marketplace/direct-listings/_transferListingTokens/_transferListingTokens.t.sol b/src/test/marketplace/direct-listings/_transferListingTokens/_transferListingTokens.t.sol deleted file mode 100644 index 3673ef854..000000000 --- a/src/test/marketplace/direct-listings/_transferListingTokens/_transferListingTokens.t.sol +++ /dev/null @@ -1,175 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { MarketplaceV3 } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { DirectListingsLogic } from "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol"; -import { IDirectListings } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -contract MockTransferListingTokens is DirectListingsLogic { - constructor(address _nativeTokenWrapper) DirectListingsLogic(_nativeTokenWrapper) {} - - function transferListingTokens( - address _from, - address _to, - uint256 _quantity, - IDirectListings.Listing memory _listing - ) external { - _transferListingTokens(_from, _to, _quantity, _listing); - } -} - -contract TransferListingTokensTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public recipient; - - // Default listing parameters - IDirectListings.ListingParameters internal listingParams; - uint256 listingId_erc721 = 0; - uint256 listingId_erc1155 = 1; - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - recipient = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - - vm.stopPrank(); - - // Setup listing params - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Mint 1 ERC721 NFT to seller - erc721.mint(seller, listingParams.quantity); - // Mint 100 ERC1155 NFT to seller - erc1155.mint(seller, listingParams.tokenId, 100); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - - // Create listings - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - vm.stopPrank(); - - vm.startPrank(seller); - - erc721.setApprovalForAll(marketplace, true); - erc1155.setApprovalForAll(marketplace, true); - - listingId_erc721 = DirectListingsLogic(marketplace).createListing(listingParams); - - listingParams.assetContract = address(erc1155); - listingParams.quantity = 100; - listingId_erc1155 = DirectListingsLogic(marketplace).createListing(listingParams); - - vm.stopPrank(); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `MockTransferListingTokens` - address directListings = address(new MockTransferListingTokens(address(weth))); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "MockTransferListingTokens", - metadataURI: "ipfs://MockTransferListingTokens", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](3); - extension_directListings.functions[0] = ExtensionFunction( - MockTransferListingTokens.transferListingTokens.selector, - "transferListingTokens(address,address,uint256,(uint256,uint256,uint256,uint256,uint128,uint128,address,address,address,uint8,uint8,bool))" - ); - extension_directListings.functions[1] = ExtensionFunction( - DirectListingsLogic.createListing.selector, - "createListing((address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[2] = ExtensionFunction( - DirectListingsLogic.getListing.selector, - "getListing(uint256)" - ); - extensions[0] = extension_directListings; - } - - function test_transferListingTokens_erc1155() public { - IDirectListings.Listing memory listing = DirectListingsLogic(marketplace).getListing(listingId_erc1155); - - assertEq(erc1155.balanceOf(seller, listing.tokenId), 100); - assertEq(erc1155.balanceOf(recipient, listing.tokenId), 0); - - MockTransferListingTokens(marketplace).transferListingTokens(seller, recipient, 100, listing); - - assertEq(erc1155.balanceOf(seller, listing.tokenId), 0); - assertEq(erc1155.balanceOf(recipient, listing.tokenId), 100); - } - - function test_transferListingTokens_erc721() public { - IDirectListings.Listing memory listing = DirectListingsLogic(marketplace).getListing(listingId_erc721); - - assertEq(erc721.ownerOf(listing.tokenId), seller); - - MockTransferListingTokens(marketplace).transferListingTokens(seller, recipient, 1, listing); - - assertEq(erc721.ownerOf(listing.tokenId), recipient); - } -} diff --git a/src/test/marketplace/direct-listings/_transferListingTokens/_transferListingTokens.tree b/src/test/marketplace/direct-listings/_transferListingTokens/_transferListingTokens.tree deleted file mode 100644 index 02204ec44..000000000 --- a/src/test/marketplace/direct-listings/_transferListingTokens/_transferListingTokens.tree +++ /dev/null @@ -1,10 +0,0 @@ -function _transferListingTokens( - address _from, - address _to, - uint256 _quantity, - Listing memory _listing -) -├── when the token is ERC1155 -│ └── it should transfer ERC1155 tokens from the specified owner to recipient -└── when the token is ERC721 - └── it should transfer ERC721 tokens from the specified owner to recipient \ No newline at end of file diff --git a/src/test/marketplace/direct-listings/_validateERC20BalAndAllowance/_validateERC20BalAndAllowance.t.sol b/src/test/marketplace/direct-listings/_validateERC20BalAndAllowance/_validateERC20BalAndAllowance.t.sol deleted file mode 100644 index f57643ca0..000000000 --- a/src/test/marketplace/direct-listings/_validateERC20BalAndAllowance/_validateERC20BalAndAllowance.t.sol +++ /dev/null @@ -1,156 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { MarketplaceV3 } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { DirectListingsLogic } from "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol"; -import { IDirectListings } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -contract MockValidateERC20BalAndAllowance is DirectListingsLogic { - constructor(address _nativeTokenWrapper) DirectListingsLogic(_nativeTokenWrapper) {} - - function validateERC20BalAndAllowance( - address _tokenOwner, - address _currency, - uint256 _amount - ) external returns (bool) { - _validateERC20BalAndAllowance(_tokenOwner, _currency, _amount); - return true; - } -} - -contract ValidateERC20BalAndAllowanceTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - - // Default listing parameters - IDirectListings.ListingParameters internal listingParams; - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - - vm.stopPrank(); - - // Setup listing params - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Mint 1 ERC721 NFT to seller - erc721.mint(seller, listingParams.quantity); - // Mint 100 ERC1155 NFT to seller - erc1155.mint(seller, listingParams.tokenId, 100); - // Mint some ERC20 tokens to seller - erc20.mint(seller, 100 ether); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `MockValidateERC20BalAndAllowance` - address directListings = address(new MockValidateERC20BalAndAllowance(address(weth))); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "MockValidateERC20BalAndAllowance", - metadataURI: "ipfs://MockValidateERC20BalAndAllowance", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](1); - extension_directListings.functions[0] = ExtensionFunction( - MockValidateERC20BalAndAllowance.validateERC20BalAndAllowance.selector, - "validateERC20BalAndAllowance(address,address,uint256)" - ); - extensions[0] = extension_directListings; - } - - function test_validateERC20BalAndAllowance_whenInsufficientTokensOwned() public { - vm.startPrank(seller); - - erc20.approve(marketplace, 100 ether); - erc20.burn(1 ether); - - vm.stopPrank(); - - vm.expectRevert("!BAL20"); - MockValidateERC20BalAndAllowance(marketplace).validateERC20BalAndAllowance(seller, address(erc20), 100 ether); - } - - function test_validateERC20BalAndAllowance_whenTokensNotApprovedToTransfer() public { - vm.startPrank(seller); - erc20.approve(marketplace, 0); - vm.stopPrank(); - - vm.expectRevert("!BAL20"); - MockValidateERC20BalAndAllowance(marketplace).validateERC20BalAndAllowance(seller, address(erc20), 100 ether); - } - - function test_validateERC20BalAndAllowance_whenTokensOwnedAndApproved() public { - vm.prank(seller); - erc20.approve(marketplace, 100 ether); - - bool result = MockValidateERC20BalAndAllowance(marketplace).validateERC20BalAndAllowance( - seller, - address(erc20), - 100 ether - ); - assertEq(result, true); - } -} diff --git a/src/test/marketplace/direct-listings/_validateERC20BalAndAllowance/_validateERC20BalAndAllowance.tree b/src/test/marketplace/direct-listings/_validateERC20BalAndAllowance/_validateERC20BalAndAllowance.tree deleted file mode 100644 index 04b6010d4..000000000 --- a/src/test/marketplace/direct-listings/_validateERC20BalAndAllowance/_validateERC20BalAndAllowance.tree +++ /dev/null @@ -1,11 +0,0 @@ -function _validateERC20BalAndAllowance( - address _tokenOwner, - address _currency, - uint256 _amount -) -├── when the balance of token owner is less than expected _amount -│ └── it should revert ✅ -├── when marketplace is not approved to spend token owner's token -│ └── it should revert ✅ -└── when the balance of token owner is greater than or equal to expected _amount and marketplace is approved to spend token owner's token - └── it should return ✅ \ No newline at end of file diff --git a/src/test/marketplace/direct-listings/_validateNewListing/_validateNewListing.t.sol b/src/test/marketplace/direct-listings/_validateNewListing/_validateNewListing.t.sol deleted file mode 100644 index 51b681e34..000000000 --- a/src/test/marketplace/direct-listings/_validateNewListing/_validateNewListing.t.sol +++ /dev/null @@ -1,292 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { MarketplaceV3 } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { DirectListingsLogic } from "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol"; -import { IDirectListings } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -contract MockValidateListing is DirectListingsLogic { - constructor(address _nativeTokenWrapper) DirectListingsLogic(_nativeTokenWrapper) {} - - function validateNewListing(ListingParameters memory _params, TokenType _tokenType) external returns (bool) { - _validateNewListing(_params, _tokenType); - return true; - } -} - -contract ValidateNewListingTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - - // Default listing parameters - IDirectListings.ListingParameters internal listingParams; - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - - vm.stopPrank(); - - // Setup listing params - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Mint 1 ERC721 NFT to seller - erc721.mint(seller, listingParams.quantity); - // Mint 100 ERC1155 NFT to seller - erc1155.mint(seller, listingParams.tokenId, 100); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `MockValidateListing` - address directListings = address(new MockValidateListing(address(weth))); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "MockValidateListing", - metadataURI: "ipfs://MockValidateListing", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](1); - extension_directListings.functions[0] = ExtensionFunction( - MockValidateListing.validateNewListing.selector, - "validateNewListing((address,uint256,uint256,address,uint256,uint128,uint128,bool),uint8)" - ); - extensions[0] = extension_directListings; - } - - function test_validateNewListing_whenQuantityIsZero() public { - listingParams.quantity = 0; - - vm.expectRevert("Marketplace: listing zero quantity."); - MockValidateListing(marketplace).validateNewListing(listingParams, IDirectListings.TokenType.ERC721); - } - - modifier whenQuantityIsOne() { - listingParams.quantity = 1; - _; - } - - modifier whenQuantityIsGtOne() { - listingParams.quantity = 2; - _; - } - - modifier whenTokenIsERC721() { - listingParams.assetContract = address(erc721); - _; - } - - modifier whenTokenIsERC1155() { - listingParams.assetContract = address(erc1155); - _; - } - - function test_validateNewListing_whenTokenIsERC721() public whenQuantityIsGtOne { - vm.expectRevert("Marketplace: listing invalid quantity."); - MockValidateListing(marketplace).validateNewListing(listingParams, IDirectListings.TokenType.ERC721); - } - - function test_validateNewListing_whenTokenOwnerDoesntOwnSufficientTokens_1() - public - whenQuantityIsGtOne - whenTokenIsERC1155 - { - vm.startPrank(seller); - erc1155.setApprovalForAll(marketplace, true); - erc1155.burn(seller, listingParams.tokenId, 100); - vm.stopPrank(); - - vm.prank(seller); - vm.expectRevert("Marketplace: not owner or approved tokens."); - MockValidateListing(marketplace).validateNewListing(listingParams, IDirectListings.TokenType.ERC1155); - } - - modifier whenTokenOwnerOwnsSufficientTokens() { - _; - } - - function test_validateNewListing_whenTokensNotApprovedForTransfer_1() - public - whenQuantityIsGtOne - whenTokenIsERC1155 - whenTokenOwnerOwnsSufficientTokens - { - vm.prank(seller); - erc1155.setApprovalForAll(marketplace, false); - - vm.prank(seller); - vm.expectRevert("Marketplace: not owner or approved tokens."); - MockValidateListing(marketplace).validateNewListing(listingParams, IDirectListings.TokenType.ERC1155); - } - - modifier whenTokensApprovedForTransfer(IDirectListings.TokenType tokenType) { - vm.prank(seller); - if (tokenType == IDirectListings.TokenType.ERC721) { - erc721.setApprovalForAll(marketplace, true); - } else { - erc1155.setApprovalForAll(marketplace, true); - } - _; - } - - function test_validateNewListing_whenTokensOwnedAndApproved_1() - public - whenQuantityIsGtOne - whenTokenIsERC1155 - whenTokenOwnerOwnsSufficientTokens - whenTokensApprovedForTransfer(IDirectListings.TokenType.ERC1155) - { - vm.prank(seller); - assertEq( - MockValidateListing(marketplace).validateNewListing(listingParams, IDirectListings.TokenType.ERC1155), - true - ); - } - - function test_validateNewListing_whenTokenOwnerDoesntOwnSufficientTokens_2a() - public - whenQuantityIsOne - whenTokenIsERC1155 - { - vm.startPrank(seller); - erc1155.setApprovalForAll(marketplace, true); - erc1155.burn(seller, listingParams.tokenId, 100); - vm.stopPrank(); - - vm.prank(seller); - vm.expectRevert("Marketplace: not owner or approved tokens."); - MockValidateListing(marketplace).validateNewListing(listingParams, IDirectListings.TokenType.ERC1155); - } - - function test_validateNewListing_whenTokenOwnerDoesntOwnSufficientTokens_2b() - public - whenQuantityIsOne - whenTokenIsERC721 - { - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - erc721.burn(listingParams.tokenId); - vm.stopPrank(); - - vm.prank(seller); - vm.expectRevert("Marketplace: not owner or approved tokens."); - MockValidateListing(marketplace).validateNewListing(listingParams, IDirectListings.TokenType.ERC721); - } - - function test_validateNewListing_whenTokensNotApprovedForTransfer_2a() - public - whenQuantityIsOne - whenTokenIsERC721 - whenTokenOwnerOwnsSufficientTokens - { - vm.prank(seller); - erc721.setApprovalForAll(marketplace, false); - - vm.prank(seller); - vm.expectRevert("Marketplace: not owner or approved tokens."); - MockValidateListing(marketplace).validateNewListing(listingParams, IDirectListings.TokenType.ERC721); - } - - function test_validateNewListing_whenTokensNotApprovedForTransfer_2b() - public - whenQuantityIsOne - whenTokenIsERC1155 - whenTokenOwnerOwnsSufficientTokens - { - vm.prank(seller); - erc1155.setApprovalForAll(marketplace, false); - - vm.prank(seller); - vm.expectRevert("Marketplace: not owner or approved tokens."); - MockValidateListing(marketplace).validateNewListing(listingParams, IDirectListings.TokenType.ERC1155); - } - - function test_validateNewListing_whenTokensOwnedAndApproved_2a() - public - whenQuantityIsOne - whenTokenIsERC1155 - whenTokenOwnerOwnsSufficientTokens - whenTokensApprovedForTransfer(IDirectListings.TokenType.ERC1155) - { - vm.prank(seller); - assertEq( - MockValidateListing(marketplace).validateNewListing(listingParams, IDirectListings.TokenType.ERC1155), - true - ); - } - - function test_validateNewListing_whenTokensOwnedAndApproved_2b() - public - whenQuantityIsOne - whenTokenIsERC721 - whenTokenOwnerOwnsSufficientTokens - whenTokensApprovedForTransfer(IDirectListings.TokenType.ERC721) - { - vm.prank(seller); - assertEq( - MockValidateListing(marketplace).validateNewListing(listingParams, IDirectListings.TokenType.ERC721), - true - ); - } -} diff --git a/src/test/marketplace/direct-listings/_validateNewListing/_validateNewListing.tree b/src/test/marketplace/direct-listings/_validateNewListing/_validateNewListing.tree deleted file mode 100644 index a2520cf27..000000000 --- a/src/test/marketplace/direct-listings/_validateNewListing/_validateNewListing.tree +++ /dev/null @@ -1,23 +0,0 @@ -function _validateNewListing(ListingParameters memory _params, TokenType _tokenType) -├── when quantity is zero -│ └── it should revert ✅ -└── when quantity is non zero - ├── when quantity is greater than one - │ ├── when token type is ERC721 - │ │ └── it should revert ✅ - │ └── when the token type is ERC1155 - │ ├── when the token owner owns less than quantity to list - │ │ └── it should revert ✅ - │ └── when the token owner owns sufficient quantity - │ ├── when the marketplace is not approved to transfer tokens - │ │ └── it should revert ✅ - │ └── when the marketplace is approved to transfer tokens - │ └── it should return ✅ - └── when the quantity is one - ├── when the token owner owns less than quantity to list - │ └── it should revert ✅ - └── when the token owner owns sufficient quantity - ├── when the marketplace is not approved to transfer tokens - │ └── it should revert ✅ - └── when the marketplace is approved to transfer tokens - └── it should return ✅ \ No newline at end of file diff --git a/src/test/marketplace/direct-listings/_validateOwnershipAndApproval/_validateOwnershipAndApproval.t.sol b/src/test/marketplace/direct-listings/_validateOwnershipAndApproval/_validateOwnershipAndApproval.t.sol deleted file mode 100644 index 5436d89f7..000000000 --- a/src/test/marketplace/direct-listings/_validateOwnershipAndApproval/_validateOwnershipAndApproval.t.sol +++ /dev/null @@ -1,254 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { MarketplaceV3 } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { DirectListingsLogic } from "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol"; -import { IDirectListings } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -contract MockValidateOwnershipAndApproval is DirectListingsLogic { - constructor(address _nativeTokenWrapper) DirectListingsLogic(_nativeTokenWrapper) {} - - function validateOwnershipAndApproval( - address _tokenOwner, - address _assetContract, - uint256 _tokenId, - uint256 _quantity, - TokenType _tokenType - ) external view returns (bool) { - return _validateOwnershipAndApproval(_tokenOwner, _assetContract, _tokenId, _quantity, _tokenType); - } -} - -contract ValidateOwnershipAndApprovalTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - - // Default listing parameters - IDirectListings.ListingParameters internal listingParams; - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - - vm.stopPrank(); - - // Setup listing params - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Mint 1 ERC721 NFT to seller - erc721.mint(seller, listingParams.quantity); - // Mint 100 ERC1155 NFT to seller - erc1155.mint(seller, listingParams.tokenId, 100); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `MockValidateListing` - address directListings = address(new MockValidateOwnershipAndApproval(address(weth))); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "MockValidateOwnershipAndApproval", - metadataURI: "ipfs://MockValidateOwnershipAndApproval", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](1); - extension_directListings.functions[0] = ExtensionFunction( - MockValidateOwnershipAndApproval.validateOwnershipAndApproval.selector, - "validateOwnershipAndApproval(address,address,uint256,uint256,uint8)" - ); - extensions[0] = extension_directListings; - } - - modifier whenTokenIsERC1155() { - listingParams.assetContract = address(erc1155); - listingParams.quantity = 100; - _; - } - - modifier whenTokenIsERC721() { - listingParams.assetContract = address(erc721); - listingParams.quantity = 1; - _; - } - - function test_validateOwnershipAndApproval_whenInsufficientTokensOwned_erc1155() public whenTokenIsERC1155 { - vm.prank(seller); - erc1155.setApprovalForAll(marketplace, true); - - vm.prank(seller); - erc1155.burn(seller, listingParams.tokenId, 100); - - bool result = MockValidateOwnershipAndApproval(marketplace).validateOwnershipAndApproval( - seller, - listingParams.assetContract, - listingParams.tokenId, - listingParams.quantity, - IDirectListings.TokenType.ERC1155 - ); - assertEq(result, false); - } - - function test_validateOwnershipAndApproval_whenInsufficientTokensOwned_erc721() public whenTokenIsERC721 { - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - vm.prank(seller); - erc721.burn(listingParams.tokenId); - - bool result = MockValidateOwnershipAndApproval(marketplace).validateOwnershipAndApproval( - seller, - listingParams.assetContract, - listingParams.tokenId, - listingParams.quantity, - IDirectListings.TokenType.ERC721 - ); - assertEq(result, false); - } - - modifier whenSufficientTokensOwned() { - _; - } - - function test_validateOwnershipAndApproval_whenTokensNotApprovedToTransfer_erc1155() - public - whenTokenIsERC1155 - whenSufficientTokensOwned - { - assertEq(erc1155.balanceOf(seller, listingParams.tokenId), listingParams.quantity); - - vm.prank(seller); - erc1155.setApprovalForAll(marketplace, false); - - bool result = MockValidateOwnershipAndApproval(marketplace).validateOwnershipAndApproval( - seller, - listingParams.assetContract, - listingParams.tokenId, - listingParams.quantity, - IDirectListings.TokenType.ERC1155 - ); - assertEq(result, false); - } - - function test_validateOwnershipAndApproval_whenTokensNotApprovedToTransfer_erc721() - public - whenTokenIsERC721 - whenSufficientTokensOwned - { - assertEq(erc721.ownerOf(listingParams.tokenId), seller); - - vm.prank(seller); - erc721.setApprovalForAll(marketplace, false); - - bool result = MockValidateOwnershipAndApproval(marketplace).validateOwnershipAndApproval( - seller, - listingParams.assetContract, - listingParams.tokenId, - listingParams.quantity, - IDirectListings.TokenType.ERC721 - ); - assertEq(result, false); - } - - modifier whenTokensApprovedForTransfer(IDirectListings.TokenType tokenType) { - vm.prank(seller); - if (tokenType == IDirectListings.TokenType.ERC1155) { - erc1155.setApprovalForAll(marketplace, true); - } else { - erc721.setApprovalForAll(marketplace, true); - } - _; - } - - function test_validateOwnershipAndApproval_whenTokensOwnedAndApproved_erc1155() - public - whenTokenIsERC1155 - whenSufficientTokensOwned - whenTokensApprovedForTransfer(IDirectListings.TokenType.ERC1155) - { - bool result = MockValidateOwnershipAndApproval(marketplace).validateOwnershipAndApproval( - seller, - listingParams.assetContract, - listingParams.tokenId, - listingParams.quantity, - IDirectListings.TokenType.ERC1155 - ); - assertEq(result, true); - } - - function test_validateOwnershipAndApproval_whenTokensOwnedAndApproved_erc721() - public - whenTokenIsERC721 - whenSufficientTokensOwned - whenTokensApprovedForTransfer(IDirectListings.TokenType.ERC721) - { - bool result = MockValidateOwnershipAndApproval(marketplace).validateOwnershipAndApproval( - seller, - listingParams.assetContract, - listingParams.tokenId, - listingParams.quantity, - IDirectListings.TokenType.ERC721 - ); - assertEq(result, true); - } -} diff --git a/src/test/marketplace/direct-listings/_validateOwnershipAndApproval/_validateOwnershipAndApproval.tree b/src/test/marketplace/direct-listings/_validateOwnershipAndApproval/_validateOwnershipAndApproval.tree deleted file mode 100644 index 2ee82b493..000000000 --- a/src/test/marketplace/direct-listings/_validateOwnershipAndApproval/_validateOwnershipAndApproval.tree +++ /dev/null @@ -1,21 +0,0 @@ -function _validateOwnershipAndApproval( - address _tokenOwner, - address _assetContract, - uint256 _tokenId, - uint256 _quantity, - TokenType _tokenType -) -├── when token type is ERC1155 -│ ├── when token balance of owner is less than expected quantity -│ │ └── it should return false ✅ -│ ├── when marketplace is not approved to transfer tokens -│ │ └── it should return false ✅ -│ └── when token balance of owner is gte expected quantity and marketplace is approved to transfer tokens -│ └── it should return true ✅ -└── when token type is ERC721 - ├── when token owner is not the expected owner of the token - │ └── it should return false ✅ - ├── when marketplace is not approved to transfer tokens - │ └── it should return false ✅ - └── when token owner is the expected owner of the token and marketplace is approved to transfer tokens - └── it should return true ✅ diff --git a/src/test/marketplace/direct-listings/approveBuyerForListing/approveBuyerForListing.t.sol b/src/test/marketplace/direct-listings/approveBuyerForListing/approveBuyerForListing.t.sol deleted file mode 100644 index bdd8dbae8..000000000 --- a/src/test/marketplace/direct-listings/approveBuyerForListing/approveBuyerForListing.t.sol +++ /dev/null @@ -1,222 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { MarketplaceV3 } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { DirectListingsLogic } from "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol"; -import { IDirectListings } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -contract ApproveBuyerForListingTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - // Default listing parameters - IDirectListings.ListingParameters internal listingParams; - uint256 internal listingId = 0; - - // Events to test - - /// @notice Emitted when a buyer is approved to buy from a reserved listing. - event BuyerApprovedForListing(uint256 indexed listingId, address indexed buyer, bool approved); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - - vm.stopPrank(); - - // Setup listing params - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100 minutes; - uint128 endTimestamp = 200 minutes; - bool reserved = false; - - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Mint 1 ERC721 NFT to seller - erc721.mint(seller, listingParams.quantity); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `DirectListings` - address directListings = address(new DirectListingsLogic(address(weth))); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "DirectListingsLogic", - metadataURI: "ipfs://DirectListings", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](13); - extension_directListings.functions[0] = ExtensionFunction( - DirectListingsLogic.totalListings.selector, - "totalListings()" - ); - extension_directListings.functions[1] = ExtensionFunction( - DirectListingsLogic.isBuyerApprovedForListing.selector, - "isBuyerApprovedForListing(uint256,address)" - ); - extension_directListings.functions[2] = ExtensionFunction( - DirectListingsLogic.isCurrencyApprovedForListing.selector, - "isCurrencyApprovedForListing(uint256,address)" - ); - extension_directListings.functions[3] = ExtensionFunction( - DirectListingsLogic.currencyPriceForListing.selector, - "currencyPriceForListing(uint256,address)" - ); - extension_directListings.functions[4] = ExtensionFunction( - DirectListingsLogic.createListing.selector, - "createListing((address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[5] = ExtensionFunction( - DirectListingsLogic.updateListing.selector, - "updateListing(uint256,(address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[6] = ExtensionFunction( - DirectListingsLogic.cancelListing.selector, - "cancelListing(uint256)" - ); - extension_directListings.functions[7] = ExtensionFunction( - DirectListingsLogic.approveBuyerForListing.selector, - "approveBuyerForListing(uint256,address,bool)" - ); - extension_directListings.functions[8] = ExtensionFunction( - DirectListingsLogic.approveCurrencyForListing.selector, - "approveCurrencyForListing(uint256,address,uint256)" - ); - extension_directListings.functions[9] = ExtensionFunction( - DirectListingsLogic.buyFromListing.selector, - "buyFromListing(uint256,address,uint256,address,uint256)" - ); - extension_directListings.functions[10] = ExtensionFunction( - DirectListingsLogic.getAllListings.selector, - "getAllListings(uint256,uint256)" - ); - extension_directListings.functions[11] = ExtensionFunction( - DirectListingsLogic.getAllValidListings.selector, - "getAllValidListings(uint256,uint256)" - ); - extension_directListings.functions[12] = ExtensionFunction( - DirectListingsLogic.getListing.selector, - "getListing(uint256)" - ); - - extensions[0] = extension_directListings; - } - - function test_approveBuyerForListing_listingDoesntExist() public { - vm.prank(seller); - vm.expectRevert("Marketplace: invalid listing."); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - } - - modifier whenListingExists() { - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - vm.stopPrank(); - - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - listingId = DirectListingsLogic(marketplace).createListing(listingParams); - vm.stopPrank(); - _; - } - - function test_approveBuyerForListing_whenCallerNotListingCreator() public whenListingExists { - vm.prank(address(0x4353)); - vm.expectRevert("Marketplace: not listing creator."); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - } - - modifier whenCallerIsListingCreator() { - _; - } - - function test_approveBuyerForListing_whenListingNotReserved() public whenListingExists whenCallerIsListingCreator { - vm.prank(seller); - vm.expectRevert("Marketplace: listing not reserved."); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - } - - modifier whenListingIsReserved() { - listingParams.reserved = true; - - vm.prank(seller); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - _; - } - - function test_approveBuyerForListing_whenListingIsReserved() - public - whenListingExists - whenCallerIsListingCreator - whenListingIsReserved - { - assertEq(DirectListingsLogic(marketplace).isBuyerApprovedForListing(listingId, buyer), false); - - vm.prank(seller); - vm.expectEmit(true, true, true, false); - emit BuyerApprovedForListing(listingId, buyer, true); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - - assertEq(DirectListingsLogic(marketplace).isBuyerApprovedForListing(listingId, buyer), true); - } -} diff --git a/src/test/marketplace/direct-listings/approveBuyerForListing/approveBuyerForListing.tree b/src/test/marketplace/direct-listings/approveBuyerForListing/approveBuyerForListing.tree deleted file mode 100644 index 5b7aeff2a..000000000 --- a/src/test/marketplace/direct-listings/approveBuyerForListing/approveBuyerForListing.tree +++ /dev/null @@ -1,15 +0,0 @@ -function approveBuyerForListing( - uint256 _listingId, - address _buyer, - bool _toApprove -) -├── when the lisitng does not exist -│ └── it should revert ✅ -└── when the listing exists - ├── when the caller is not listing creator - │ └── it should revert ✅ - └── when the caller is listing creator - ├── when the listing is not reserved - │ └── it should revert ✅ - └── when the listing is reserved - └── it should set the intended approval status for buyer ✅ \ No newline at end of file diff --git a/src/test/marketplace/direct-listings/approveCurrencyForListing/approveCurrencyForListing.t.sol b/src/test/marketplace/direct-listings/approveCurrencyForListing/approveCurrencyForListing.t.sol deleted file mode 100644 index e4e70007d..000000000 --- a/src/test/marketplace/direct-listings/approveCurrencyForListing/approveCurrencyForListing.t.sol +++ /dev/null @@ -1,237 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { MarketplaceV3 } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { DirectListingsLogic } from "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol"; -import { IDirectListings } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -contract ApproveCurrencyForListingTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - - // Default listing parameters - IDirectListings.ListingParameters internal listingParams; - uint256 internal listingId = 0; - - // Events to test - - /// @notice Emitted when a currency is approved as a form of payment for the listing. - event CurrencyApprovedForListing(uint256 indexed listingId, address indexed currency, uint256 pricePerToken); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - - vm.stopPrank(); - - // Setup listing params - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100 minutes; - uint128 endTimestamp = 200 minutes; - bool reserved = true; - - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Mint 1 ERC721 NFT to seller - erc721.mint(seller, listingParams.quantity); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `DirectListings` - address directListings = address(new DirectListingsLogic(address(weth))); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "DirectListingsLogic", - metadataURI: "ipfs://DirectListings", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](13); - extension_directListings.functions[0] = ExtensionFunction( - DirectListingsLogic.totalListings.selector, - "totalListings()" - ); - extension_directListings.functions[1] = ExtensionFunction( - DirectListingsLogic.isBuyerApprovedForListing.selector, - "isBuyerApprovedForListing(uint256,address)" - ); - extension_directListings.functions[2] = ExtensionFunction( - DirectListingsLogic.isCurrencyApprovedForListing.selector, - "isCurrencyApprovedForListing(uint256,address)" - ); - extension_directListings.functions[3] = ExtensionFunction( - DirectListingsLogic.currencyPriceForListing.selector, - "currencyPriceForListing(uint256,address)" - ); - extension_directListings.functions[4] = ExtensionFunction( - DirectListingsLogic.createListing.selector, - "createListing((address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[5] = ExtensionFunction( - DirectListingsLogic.updateListing.selector, - "updateListing(uint256,(address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[6] = ExtensionFunction( - DirectListingsLogic.cancelListing.selector, - "cancelListing(uint256)" - ); - extension_directListings.functions[7] = ExtensionFunction( - DirectListingsLogic.approveBuyerForListing.selector, - "approveBuyerForListing(uint256,address,bool)" - ); - extension_directListings.functions[8] = ExtensionFunction( - DirectListingsLogic.approveCurrencyForListing.selector, - "approveCurrencyForListing(uint256,address,uint256)" - ); - extension_directListings.functions[9] = ExtensionFunction( - DirectListingsLogic.buyFromListing.selector, - "buyFromListing(uint256,address,uint256,address,uint256)" - ); - extension_directListings.functions[10] = ExtensionFunction( - DirectListingsLogic.getAllListings.selector, - "getAllListings(uint256,uint256)" - ); - extension_directListings.functions[11] = ExtensionFunction( - DirectListingsLogic.getAllValidListings.selector, - "getAllValidListings(uint256,uint256)" - ); - extension_directListings.functions[12] = ExtensionFunction( - DirectListingsLogic.getListing.selector, - "getListing(uint256)" - ); - - extensions[0] = extension_directListings; - } - - function test_approveCurrencyForListing_listingDoesntExist() public { - vm.prank(seller); - vm.expectRevert("Marketplace: invalid listing."); - DirectListingsLogic(marketplace).approveCurrencyForListing(listingId, address(weth), 1 ether); - } - - modifier whenListingExists() { - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - vm.stopPrank(); - - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - listingId = DirectListingsLogic(marketplace).createListing(listingParams); - erc721.setApprovalForAll(marketplace, false); - vm.stopPrank(); - - vm.prank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(erc721)); - _; - } - - function test_approveCurrencyForListing_whenCallerNotListingCreator() public whenListingExists { - vm.prank(address(0x4353)); - vm.expectRevert("Marketplace: not listing creator."); - DirectListingsLogic(marketplace).approveCurrencyForListing(listingId, address(weth), 1 ether); - } - - modifier whenCallerIsListingCreator() { - _; - } - - function test_approveCurrencyForListing_whenApprovingDifferentPriceForListedCurrency() - public - whenListingExists - whenCallerIsListingCreator - { - vm.prank(seller); - vm.expectRevert("Marketplace: approving listing currency with different price."); - DirectListingsLogic(marketplace).approveCurrencyForListing( - listingId, - listingParams.currency, - listingParams.pricePerToken + 1 - ); - } - - function test_approveCurrencyForListing_whenPriceToApproveIsAlreadyApproved() - public - whenListingExists - whenCallerIsListingCreator - { - vm.prank(seller); - DirectListingsLogic(marketplace).approveCurrencyForListing(listingId, address(weth), 1 ether); - - vm.prank(seller); - vm.expectRevert("Marketplace: price unchanged."); - DirectListingsLogic(marketplace).approveCurrencyForListing(listingId, address(weth), 1 ether); - } - - function test_approveCurrencyForListing_whenApprovedPriceForCurrencyIsDifferentThanIncumbent() - public - whenListingExists - whenCallerIsListingCreator - { - vm.expectRevert("Currency not approved for listing"); - DirectListingsLogic(marketplace).currencyPriceForListing(listingId, address(weth)); - - vm.prank(seller); - vm.expectEmit(true, true, true, true); - emit CurrencyApprovedForListing(listingId, address(weth), 1 ether); - DirectListingsLogic(marketplace).approveCurrencyForListing(listingId, address(weth), 1 ether); - - assertEq(DirectListingsLogic(marketplace).currencyPriceForListing(listingId, address(weth)), 1 ether); - } -} diff --git a/src/test/marketplace/direct-listings/approveCurrencyForListing/approveCurrencyForListing.tree b/src/test/marketplace/direct-listings/approveCurrencyForListing/approveCurrencyForListing.tree deleted file mode 100644 index 1c7912edc..000000000 --- a/src/test/marketplace/direct-listings/approveCurrencyForListing/approveCurrencyForListing.tree +++ /dev/null @@ -1,19 +0,0 @@ -function approveCurrencyForListing( - uint256 _listingId, - address _currency, - uint256 _pricePerTokenInCurrency -) -├── when listing does not exist -│ └── it should revert ✅ -└── when the listing exists - ├── when the caller is not listing creator - │ └── it should revert ✅ - └── when the caller is listing creator - ├── when approving different price for listed currency - │ └── it should revert ✅ - └── when not approving different price for listed currency - ├── when prive to approve for currency is already approved - │ └── it should revert ✅ - └── when approving a new price for currency ✅ - ├── it should update the approved price for currency - └── it should emit CurrencyApprovedForListing event with the listing ID, currency and approved price \ No newline at end of file diff --git a/src/test/marketplace/direct-listings/buyFromListing/buyFromListing.t.sol b/src/test/marketplace/direct-listings/buyFromListing/buyFromListing.t.sol deleted file mode 100644 index 8c11e6540..000000000 --- a/src/test/marketplace/direct-listings/buyFromListing/buyFromListing.t.sol +++ /dev/null @@ -1,636 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { PlatformFee } from "contracts/extension/PlatformFee.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { MarketplaceV3 } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { DirectListingsLogic } from "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol"; -import { IDirectListings } from "contracts/prebuilts/marketplace/IMarketplace.sol"; -import { MockRoyaltyEngineV1 } from "../../../mocks/MockRoyaltyEngineV1.sol"; -import { ERC1155Holder } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; - -contract ReentrantRecipient is ERC1155Holder { - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes memory data - ) public virtual override returns (bytes4) { - DirectListingsLogic(msg.sender).buyFromListing(0, address(this), 1, address(0), 0); - return super.onERC1155Received(operator, from, id, value, data); - } -} - -contract BuyFromListingTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - // Default listing parameters - IDirectListings.ListingParameters internal listingParams; - - uint256 internal listingId = type(uint256).max; - uint256 internal listingId_native_noSpecialPrice = 0; - uint256 internal listingId_native_specialPrice = 1; - uint256 internal listingId_erc20_noSpecialPrice = 2; - uint256 internal listingId_erc20_specialPrice = 3; - - // Events to test - - /// @notice Emitted when NFTs are bought from a listing. - event NewSale( - address indexed listingCreator, - uint256 indexed listingId, - address indexed assetContract, - uint256 tokenId, - address buyer, - uint256 quantityBought, - uint256 totalPricePaid - ); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), platformFeeRecipient, uint16(platformFeeBps)) - ) - ) - ); - - // Setup listing params - address assetContract = address(erc1155); - uint256 tokenId = 0; - uint256 quantity = 10; - address currency = NATIVE_TOKEN; - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100 minutes; - uint128 endTimestamp = 200 minutes; - bool reserved = false; - - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Mint currency to buyer - vm.deal(buyer, 100 ether); - erc20.mint(buyer, 100 ether); - - // Mint an ERC721 NFTs to seller - erc1155.mint(seller, 0, 100); - vm.prank(seller); - erc1155.setApprovalForAll(marketplace, true); - - // Create 4 listings - vm.startPrank(seller); - - // 1. Native token, no special price - listingParams.currency = NATIVE_TOKEN; - listingId_native_noSpecialPrice = DirectListingsLogic(marketplace).createListing(listingParams); - - // 2. Native token, special price - listingParams.currency = address(erc20); - listingId_native_specialPrice = DirectListingsLogic(marketplace).createListing(listingParams); - DirectListingsLogic(marketplace).approveCurrencyForListing( - listingId_native_specialPrice, - NATIVE_TOKEN, - 2 ether - ); - - // 3. ERC20 token, no special price - listingParams.currency = address(erc20); - listingId_erc20_noSpecialPrice = DirectListingsLogic(marketplace).createListing(listingParams); - - // 4. ERC20 token, special price - listingParams.currency = NATIVE_TOKEN; - listingId_erc20_specialPrice = DirectListingsLogic(marketplace).createListing(listingParams); - DirectListingsLogic(marketplace).approveCurrencyForListing( - listingId_erc20_specialPrice, - address(erc20), - 2 ether - ); - - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `DirectListings` - address directListings = address(new DirectListingsLogic(address(weth))); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "DirectListingsLogic", - metadataURI: "ipfs://DirectListings", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](13); - extension_directListings.functions[0] = ExtensionFunction( - DirectListingsLogic.totalListings.selector, - "totalListings()" - ); - extension_directListings.functions[1] = ExtensionFunction( - DirectListingsLogic.isBuyerApprovedForListing.selector, - "isBuyerApprovedForListing(uint256,address)" - ); - extension_directListings.functions[2] = ExtensionFunction( - DirectListingsLogic.isCurrencyApprovedForListing.selector, - "isCurrencyApprovedForListing(uint256,address)" - ); - extension_directListings.functions[3] = ExtensionFunction( - DirectListingsLogic.currencyPriceForListing.selector, - "currencyPriceForListing(uint256,address)" - ); - extension_directListings.functions[4] = ExtensionFunction( - DirectListingsLogic.createListing.selector, - "createListing((address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[5] = ExtensionFunction( - DirectListingsLogic.updateListing.selector, - "updateListing(uint256,(address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[6] = ExtensionFunction( - DirectListingsLogic.cancelListing.selector, - "cancelListing(uint256)" - ); - extension_directListings.functions[7] = ExtensionFunction( - DirectListingsLogic.approveBuyerForListing.selector, - "approveBuyerForListing(uint256,address,bool)" - ); - extension_directListings.functions[8] = ExtensionFunction( - DirectListingsLogic.approveCurrencyForListing.selector, - "approveCurrencyForListing(uint256,address,uint256)" - ); - extension_directListings.functions[9] = ExtensionFunction( - DirectListingsLogic.buyFromListing.selector, - "buyFromListing(uint256,address,uint256,address,uint256)" - ); - extension_directListings.functions[10] = ExtensionFunction( - DirectListingsLogic.getAllListings.selector, - "getAllListings(uint256,uint256)" - ); - extension_directListings.functions[11] = ExtensionFunction( - DirectListingsLogic.getAllValidListings.selector, - "getAllValidListings(uint256,uint256)" - ); - extension_directListings.functions[12] = ExtensionFunction( - DirectListingsLogic.getListing.selector, - "getListing(uint256)" - ); - - extensions[0] = extension_directListings; - } - - modifier whenListingCurrencyIsNativeToken() { - listingId = listingId_native_noSpecialPrice; - listingParams.currency = NATIVE_TOKEN; - _; - } - - modifier whenListingHasSpecialPriceNativeToken() { - listingId = listingId_native_specialPrice; - _; - } - - modifier whenListingCurrencyIsERC20Token() { - listingId = listingId_erc20_noSpecialPrice; - _; - } - - modifier whenListingHasSpecialPriceERC20Token() { - listingId = listingId_erc20_specialPrice; - _; - } - - //////////// ASSUME NATIVE_TOKEN && SPECIAL_PRICE //////////// - - function test_buyFromListing_whenCallIsReentrant() public whenListingHasSpecialPriceNativeToken { - vm.warp(listingParams.startTimestamp); - address reentrantRecipient = address(new ReentrantRecipient()); - - vm.prank(buyer); - vm.expectRevert(); - DirectListingsLogic(marketplace).buyFromListing{ value: 2 ether }( - listingId, - reentrantRecipient, - 1, - NATIVE_TOKEN, - 2 ether - ); - } - - modifier whenCallIsNotReentrant() { - _; - } - - function test_buyFromListing_whenListingDoesNotExist() - public - whenListingHasSpecialPriceNativeToken - whenCallIsNotReentrant - { - vm.warp(listingParams.startTimestamp); - vm.prank(buyer); - vm.expectRevert("Marketplace: invalid listing."); - DirectListingsLogic(marketplace).buyFromListing{ value: 2 ether }(100, buyer, 1, NATIVE_TOKEN, 2 ether); - } - - modifier whenListingExists() { - _; - } - - function test_buyFromListing_whenBuyerIsNotApprovedForListing() - public - whenListingHasSpecialPriceNativeToken - whenCallIsNotReentrant - whenListingExists - { - listingParams.reserved = true; - listingParams.currency = address(erc20); - - vm.prank(seller); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - - vm.warp(listingParams.startTimestamp); - - vm.prank(buyer); - vm.expectRevert("buyer not approved"); - DirectListingsLogic(marketplace).buyFromListing{ value: 2 ether }(listingId, buyer, 1, NATIVE_TOKEN, 2 ether); - } - - modifier whenBuyerIsApprovedForListing(address _currency) { - listingParams.reserved = true; - listingParams.currency = _currency; - - vm.prank(seller); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - - vm.prank(seller); - DirectListingsLogic(marketplace).approveBuyerForListing(listingId, buyer, true); - _; - } - - function test_buyFromListing_whenQuantityToBuyIsInvalid() - public - whenListingHasSpecialPriceNativeToken - whenCallIsNotReentrant - whenListingExists - whenBuyerIsApprovedForListing(address(erc20)) - { - vm.warp(listingParams.startTimestamp); - - vm.prank(buyer); - vm.expectRevert("Buying invalid quantity"); - DirectListingsLogic(marketplace).buyFromListing{ value: 2 ether }(listingId, buyer, 0, NATIVE_TOKEN, 2 ether); - } - - modifier whenQuantityToBuyIsValid() { - _; - } - - function test_buyFromListing_whenListingIsInactive() - public - whenListingHasSpecialPriceNativeToken - whenCallIsNotReentrant - whenListingExists - whenBuyerIsApprovedForListing(address(erc20)) - whenQuantityToBuyIsValid - { - vm.prank(buyer); - vm.expectRevert("not within sale window."); - DirectListingsLogic(marketplace).buyFromListing{ value: 2 ether }(listingId, buyer, 1, NATIVE_TOKEN, 2 ether); - } - - modifier whenListingIsActive() { - _; - } - - function test_buyFromListing_whenListedAssetNotOwnedOrApprovedToTransfer() - public - whenListingHasSpecialPriceNativeToken - whenCallIsNotReentrant - whenListingExists - whenBuyerIsApprovedForListing(address(erc20)) - whenQuantityToBuyIsValid - whenListingIsActive - { - vm.prank(seller); - erc1155.setApprovalForAll(marketplace, false); - - vm.warp(listingParams.startTimestamp); - - vm.prank(buyer); - vm.expectRevert("Marketplace: not owner or approved tokens."); - DirectListingsLogic(marketplace).buyFromListing{ value: 2 ether }(listingId, buyer, 1, NATIVE_TOKEN, 2 ether); - } - - modifier whenListedAssetOwnedAndApproved() { - _; - } - - function test_buyFromListing_whenExpectedPriceNotActualPrice() - public - whenListingHasSpecialPriceNativeToken - whenCallIsNotReentrant - whenListingExists - whenBuyerIsApprovedForListing(address(erc20)) - whenQuantityToBuyIsValid - whenListingIsActive - whenListedAssetOwnedAndApproved - { - vm.warp(listingParams.startTimestamp); - - vm.prank(buyer); - vm.expectRevert("Unexpected total price"); - DirectListingsLogic(marketplace).buyFromListing{ value: 2 ether }(listingId, buyer, 1, NATIVE_TOKEN, 1 ether); - } - - modifier whenExpectedPriceIsActualPrice() { - _; - } - - function test_buyFromListing_whenMsgValueNotEqTotalPrice() - public - whenListingHasSpecialPriceNativeToken - whenCallIsNotReentrant - whenListingExists - whenBuyerIsApprovedForListing(address(erc20)) - whenQuantityToBuyIsValid - whenListingIsActive - whenListedAssetOwnedAndApproved - whenExpectedPriceIsActualPrice - { - vm.warp(listingParams.startTimestamp); - - vm.prank(buyer); - vm.expectRevert("Marketplace: msg.value must exactly be the total price."); - DirectListingsLogic(marketplace).buyFromListing{ value: 1 ether }(listingId, buyer, 1, NATIVE_TOKEN, 2 ether); - } - - modifier whenMsgValueEqTotalPrice() { - _; - } - - function test_buyFromListing_whenAllRemainingQtyIsBought_nativeToken() - public - whenListingHasSpecialPriceNativeToken - whenCallIsNotReentrant - whenListingExists - whenBuyerIsApprovedForListing(address(erc20)) - whenQuantityToBuyIsValid - whenListingIsActive - whenListedAssetOwnedAndApproved - whenExpectedPriceIsActualPrice - whenMsgValueEqTotalPrice - { - assertEq(erc1155.balanceOf(seller, listingParams.tokenId), 100); - assertEq(erc1155.balanceOf(buyer, listingParams.tokenId), 0); - assertEq( - uint8(DirectListingsLogic(marketplace).getListing(listingId).status), - uint8(IDirectListings.Status.CREATED) - ); - - vm.warp(listingParams.startTimestamp); - vm.prank(buyer); - DirectListingsLogic(marketplace).buyFromListing{ value: 2 ether * listingParams.quantity }( - listingId, - buyer, - listingParams.quantity, - NATIVE_TOKEN, - 2 ether * listingParams.quantity - ); - - assertEq(erc1155.balanceOf(seller, listingParams.tokenId), 100 - listingParams.quantity); - assertEq(erc1155.balanceOf(buyer, listingParams.tokenId), listingParams.quantity); - assertEq( - uint8(DirectListingsLogic(marketplace).getListing(listingId).status), - uint8(IDirectListings.Status.COMPLETED) - ); - } - - function test_buyFromListing_whenSomeRemainingQtyIsBought_nativeToken() - public - whenListingHasSpecialPriceNativeToken - whenCallIsNotReentrant - whenListingExists - whenBuyerIsApprovedForListing(address(erc20)) - whenQuantityToBuyIsValid - whenListingIsActive - whenListedAssetOwnedAndApproved - whenExpectedPriceIsActualPrice - whenMsgValueEqTotalPrice - { - assertEq(erc1155.balanceOf(seller, listingParams.tokenId), 100); - assertEq(erc1155.balanceOf(buyer, listingParams.tokenId), 0); - assertEq( - uint8(DirectListingsLogic(marketplace).getListing(listingId).status), - uint8(IDirectListings.Status.CREATED) - ); - - vm.warp(listingParams.startTimestamp); - vm.prank(buyer); - DirectListingsLogic(marketplace).buyFromListing{ value: 2 ether }(listingId, buyer, 1, NATIVE_TOKEN, 2 ether); - - assertEq(erc1155.balanceOf(seller, listingParams.tokenId), 99); - assertEq(erc1155.balanceOf(buyer, listingParams.tokenId), 1); - assertEq( - uint8(DirectListingsLogic(marketplace).getListing(listingId).status), - uint8(IDirectListings.Status.CREATED) - ); - } - - //////////// ASSUME NATIVE_TOKEN && NO_SPECIAL_PRICE //////////// - - function test_buyFromListing_whenCurrencyToUseNotListedCurrency() - public - whenListingCurrencyIsNativeToken - whenCallIsNotReentrant - whenListingExists - whenBuyerIsApprovedForListing(NATIVE_TOKEN) - whenQuantityToBuyIsValid - whenListingIsActive - whenListedAssetOwnedAndApproved - { - vm.warp(listingParams.startTimestamp); - - vm.prank(buyer); - vm.expectRevert("Paying in invalid currency."); - DirectListingsLogic(marketplace).buyFromListing{ value: 2 ether * listingParams.quantity }( - listingId, - buyer, - listingParams.quantity, - address(erc20), - 2 ether * listingParams.quantity - ); - } - - //////////// ASSUME ERC20 && NO_SPECIAL_PRICE //////////// - - function test_buyFromListing_whenInsufficientTokenBalanceOrAllowance() - public - whenListingCurrencyIsERC20Token - whenCallIsNotReentrant - whenListingExists - whenBuyerIsApprovedForListing(address(erc20)) - whenQuantityToBuyIsValid - whenListingIsActive - whenListedAssetOwnedAndApproved - whenExpectedPriceIsActualPrice - { - vm.warp(listingParams.startTimestamp); - - vm.prank(buyer); - vm.expectRevert("!BAL20"); - DirectListingsLogic(marketplace).buyFromListing( - listingId, - buyer, - listingParams.quantity, - address(erc20), - 1 ether * listingParams.quantity - ); - } - - modifier whenSufficientTokenBalanceOrAllowance() { - vm.prank(buyer); - erc20.approve(marketplace, 100 ether); - _; - } - - function test_buyFromListing_whenMsgValueNotZero() - public - whenListingCurrencyIsERC20Token - whenCallIsNotReentrant - whenListingExists - whenBuyerIsApprovedForListing(address(erc20)) - whenQuantityToBuyIsValid - whenListingIsActive - whenListedAssetOwnedAndApproved - whenExpectedPriceIsActualPrice - whenSufficientTokenBalanceOrAllowance - { - vm.warp(listingParams.startTimestamp); - - vm.prank(buyer); - vm.expectRevert("Marketplace: invalid native tokens sent."); - DirectListingsLogic(marketplace).buyFromListing{ value: 1 ether }( - listingId, - buyer, - listingParams.quantity, - address(erc20), - 1 ether * listingParams.quantity - ); - } - - modifier whenMsgValueIsZero() { - _; - } - - function test_buyFromListing_whenAllRemainingQtyIsBought_erc20() - public - whenListingCurrencyIsERC20Token - whenCallIsNotReentrant - whenListingExists - whenBuyerIsApprovedForListing(address(erc20)) - whenQuantityToBuyIsValid - whenListingIsActive - whenListedAssetOwnedAndApproved - whenExpectedPriceIsActualPrice - whenSufficientTokenBalanceOrAllowance - whenMsgValueIsZero - { - assertEq(erc1155.balanceOf(seller, listingParams.tokenId), 100); - assertEq(erc1155.balanceOf(buyer, listingParams.tokenId), 0); - assertEq( - uint8(DirectListingsLogic(marketplace).getListing(listingId).status), - uint8(IDirectListings.Status.CREATED) - ); - - vm.warp(listingParams.startTimestamp); - vm.prank(buyer); - DirectListingsLogic(marketplace).buyFromListing( - listingId, - buyer, - listingParams.quantity, - address(erc20), - 1 ether * listingParams.quantity - ); - - assertEq(erc1155.balanceOf(seller, listingParams.tokenId), 100 - listingParams.quantity); - assertEq(erc1155.balanceOf(buyer, listingParams.tokenId), listingParams.quantity); - assertEq( - uint8(DirectListingsLogic(marketplace).getListing(listingId).status), - uint8(IDirectListings.Status.COMPLETED) - ); - } - - function test_buyFromListing_whenSomeRemainingQtyIsBought_erc20() - public - whenListingCurrencyIsERC20Token - whenCallIsNotReentrant - whenListingExists - whenBuyerIsApprovedForListing(address(erc20)) - whenQuantityToBuyIsValid - whenListingIsActive - whenListedAssetOwnedAndApproved - whenExpectedPriceIsActualPrice - whenSufficientTokenBalanceOrAllowance - whenMsgValueIsZero - { - assertEq(erc1155.balanceOf(seller, listingParams.tokenId), 100); - assertEq(erc1155.balanceOf(buyer, listingParams.tokenId), 0); - assertEq( - uint8(DirectListingsLogic(marketplace).getListing(listingId).status), - uint8(IDirectListings.Status.CREATED) - ); - - vm.warp(listingParams.startTimestamp); - vm.prank(buyer); - DirectListingsLogic(marketplace).buyFromListing(listingId, buyer, 1, address(erc20), 1 ether); - - assertEq(erc1155.balanceOf(seller, listingParams.tokenId), 99); - assertEq(erc1155.balanceOf(buyer, listingParams.tokenId), 1); - assertEq( - uint8(DirectListingsLogic(marketplace).getListing(listingId).status), - uint8(IDirectListings.Status.CREATED) - ); - } -} diff --git a/src/test/marketplace/direct-listings/buyFromListing/buyFromListing.tree b/src/test/marketplace/direct-listings/buyFromListing/buyFromListing.tree deleted file mode 100644 index 0b7ae81fa..000000000 --- a/src/test/marketplace/direct-listings/buyFromListing/buyFromListing.tree +++ /dev/null @@ -1,172 +0,0 @@ -function buyFromListing( - uint256 _listingId, - address _buyFor, - uint256 _quantity, - address _currency, - uint256 _expectedTotalPrice -) - -// ASSUME NATIVE_TOKEN && SPECIAL_PRICE -. -├── when the call is reentrant -│ └── it should revert -└── when the call is not reentrant - ├── when no listing with the given listing ID exists - │ └── it should revert - └── when listing with the given listing ID exists - ├── when the listing is reserved and caller is not approved for listing - │ └── it should revert - └── when the listing is not reserved OR caller is approved for listing - ├── when quantity to buy is invalid i.e. zero OR exceeds the listing quantity - │ └── it should revert - └── when quantity to buy is valid i.e. non-zero and does not exceed the listing quantity - ├── when the listing is inactive - │ └── it should revert - └── when the listing is active - ├── when the asset is not owned by listing creator or marketplace is not approved for transfer - │ └── it should revert - └── when the asset is owned by listing creator and marketplace is approved for transfer - ├── when the calculated total price is not equal to the expected total price - │ └── it should revert - └── when the calculated total price is equal to the expected total price - ├── when msg.value is not equal to the calculated total price - │ └── it should revert - └── when msg.value is equal to the calculated total price - ├── when the quantity bought is the total remaining listing quantity - │ ├── it should set the status of the lisitng as complete - │ ├── it should subtract the quantity to buy from the listing quantity - │ ├── it should payout platform fees and royalty fees to respective recipients - │ ├── it should transfer the bought tokens from the listing creator to the appropriate recipient - │ └── it should emit NewSale event with the correct total price to paid - └── when the quantity bought is not the total remaining listing quantity - ├── it should subtract the quantity to buy from the listing quantity - ├── it should payout platform fees and royalty fees to respective recipients - ├── it should transfer the bought tokens from the listing creator to the appropriate recipient - └── it should emit NewSale event with the correct total price to paid - -// ASSUME NATIVE_TOKEN && NO_SPECIAL_PRICE -. -├── when the call is reentrant -│ └── it should revert -└── when the call is not reentrant - ├── when no listing with the given listing ID exists - │ └── it should revert - └── when listing with the given listing ID exists - ├── when the listing is reserved and caller is not approved for listing - │ └── it should revert - └── when the listing is not reserved OR caller is approved for listing - ├── when quantity to buy is invalid i.e. zero OR exceeds the listing quantity - │ └── it should revert - └── when quantity to buy is valid i.e. non-zero and does not exceed the listing quantity - ├── when the listing is inactive - │ └── it should revert - └── when the listing is active - ├── when the asset is not owned by listing creator or marketplace is not approved for transfer - │ └── it should revert - └── when the asset is owned by listing creator and marketplace is approved for transfer - ├── when the currency to pay in is not the listing's accepted currency - │ └── it should revert - └── when the currency to pay in is the listing's accepted currency - ├── when the calculated total price is not equal to the expected total price - │ └── it should revert - └── when the calculated total price is equal to the expected total price - ├── when the msg.value is not equal to the calculated total price - │ └── it should revert - └── when the msg.value is equal to the calculated total price - ├── when the quantity bought is the total remaining listing quantity - │ ├── it should set the status of the lisitng as complete - │ ├── it should subtract the quantity to buy from the listing quantity - │ ├── it should payout platform fees and royalty fees to respective recipients - │ ├── it should transfer the bought tokens from the listing creator to the appropriate recipient - │ └── it should emit NewSale event with the correct total price to paid - └── when the quantity bought is not the total remaining listing quantity - ├── it should subtract the quantity to buy from the listing quantity - ├── it should payout platform fees and royalty fees to respective recipients - ├── it should transfer the bought tokens from the listing creator to the appropriate recipient - └── it should emit NewSale event with the correct total price to paid - -// ASSUME ERC20 && NO_SPECIAL_PRICE -. -├── when the call is reentrant -│ └── it should revert -└── when the call is not reentrant - ├── when no listing with the given listing ID exists - │ └── it should revert - └── when listing with the given listing ID exists - ├── when the listing is reserved and caller is not approved for listing - │ └── it should revert - └── when the listing is not reserved OR caller is approved for listing - ├── when quantity to buy is invalid i.e. zero OR exceeds the listing quantity - │ └── it should revert - └── when quantity to buy is valid i.e. non-zero and does not exceed the listing quantity - ├── when the listing is inactive - │ └── it should revert - └── when the listing is active - ├── when the asset is not owned by listing creator or marketplace is not approved for transfer - │ └── it should revert - └── when the asset is owned by listing creator and marketplace is approved for transfer - ├── when the currency to pay in is not the listing's accepted currency - │ └── it should revert - └── when the currency to pay in is the listing's accepted currency - ├── when the calculated total price is not equal to the expected total price - │ └── it should revert - └── when the calculated total price is equal to the expected total price - └── when ERC20 balance and allowance is invalid - ├── it should revert - └── when ERC20 balance and allowance is valid - ├── when msg.value is not zero - │ └── it should revert - └── when msg.value is zero - ├── when the quantity bought is the total remaining listing quantity - │ ├── it should set the status of the lisitng as complete - │ ├── it should subtract the quantity to buy from the listing quantity - │ ├── it should payout platform fees and royalty fees to respective recipients - │ ├── it should transfer the bought tokens from the listing creator to the appropriate recipient - │ └── it should emit NewSale event with the correct total price to paid - └── when the quantity bought is not the total remaining listing quantity - ├── it should subtract the quantity to buy from the listing quantity - ├── it should payout platform fees and royalty fees to respective recipients - ├── it should transfer the bought tokens from the listing creator to the appropriate recipient - └── it should emit NewSale event with the correct total price to paid - - -// ASSUME ERC20 && SPECIAL_PRICE -. -├── when the call is reentrant -│ └── it should revert -└── when the call is not reentrant - ├── when no listing with the given listing ID exists - │ └── it should revert - └── when listing with the given listing ID exists - ├── when the listing is reserved and caller is not approved for listing - │ └── it should revert - └── when the listing is not reserved OR caller is approved for listing - ├── when quantity to buy is invalid i.e. zero OR exceeds the listing quantity - │ └── it should revert - └── when quantity to buy is valid i.e. non-zero and does not exceed the listing quantity - ├── when the listing is inactive - │ └── it should revert - └── when the listing is active - ├── when the asset is not owned by listing creator or marketplace is not approved for transfer - │ └── it should revert - └── when the asset is owned by listing creator and marketplace is approved for transfer - ├── when the calculated total price is not equal to the expected total price - │ └── it should revert - └── when the calculated total price is equal to the expected total price - ├── when ERC20 balance and allowance is invalid - │ └── it should revert - └── when ERC20 balance and allowance is valid - ├── when msg.value is not zero - │ └── it should revert - └── when msg.value is zero - ├── when the quantity bought is the total remaining listing quantity - │ ├── it should set the status of the lisitng as complete - │ ├── it should subtract the quantity to buy from the listing quantity - │ ├── it should payout platform fees and royalty fees to respective recipients - │ ├── it should transfer the bought tokens from the listing creator to the appropriate recipient - │ └── it should emit NewSale event with the correct total price to paid - └── when the quantity bought is not the total remaining listing quantity - ├── it should subtract the quantity to buy from the listing quantity - ├── it should payout platform fees and royalty fees to respective recipients - ├── it should transfer the bought tokens from the listing creator to the appropriate recipient - └── it should emit NewSale event with the correct total price to paid diff --git a/src/test/marketplace/direct-listings/cancelListing/cancelListing.t.sol b/src/test/marketplace/direct-listings/cancelListing/cancelListing.t.sol deleted file mode 100644 index 6e77c4fe0..000000000 --- a/src/test/marketplace/direct-listings/cancelListing/cancelListing.t.sol +++ /dev/null @@ -1,207 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { MarketplaceV3 } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { DirectListingsLogic } from "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol"; -import { IDirectListings } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -contract CancelListingTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - - // Default listing parameters - IDirectListings.ListingParameters internal listingParams; - uint256 internal listingId = 0; - - // Events to test - - /// @notice Emitted when a listing is updated. - event CancelledListing(address indexed listingCreator, uint256 indexed listingId); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - - vm.stopPrank(); - - // Setup listing params - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100 minutes; - uint128 endTimestamp = 200 minutes; - bool reserved = true; - - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Mint 1 ERC721 NFT to seller - erc721.mint(seller, listingParams.quantity); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `DirectListings` - address directListings = address(new DirectListingsLogic(address(weth))); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "DirectListingsLogic", - metadataURI: "ipfs://DirectListings", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](13); - extension_directListings.functions[0] = ExtensionFunction( - DirectListingsLogic.totalListings.selector, - "totalListings()" - ); - extension_directListings.functions[1] = ExtensionFunction( - DirectListingsLogic.isBuyerApprovedForListing.selector, - "isBuyerApprovedForListing(uint256,address)" - ); - extension_directListings.functions[2] = ExtensionFunction( - DirectListingsLogic.isCurrencyApprovedForListing.selector, - "isCurrencyApprovedForListing(uint256,address)" - ); - extension_directListings.functions[3] = ExtensionFunction( - DirectListingsLogic.currencyPriceForListing.selector, - "currencyPriceForListing(uint256,address)" - ); - extension_directListings.functions[4] = ExtensionFunction( - DirectListingsLogic.createListing.selector, - "createListing((address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[5] = ExtensionFunction( - DirectListingsLogic.updateListing.selector, - "updateListing(uint256,(address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[6] = ExtensionFunction( - DirectListingsLogic.cancelListing.selector, - "cancelListing(uint256)" - ); - extension_directListings.functions[7] = ExtensionFunction( - DirectListingsLogic.approveBuyerForListing.selector, - "approveBuyerForListing(uint256,address,bool)" - ); - extension_directListings.functions[8] = ExtensionFunction( - DirectListingsLogic.approveCurrencyForListing.selector, - "approveCurrencyForListing(uint256,address,uint256)" - ); - extension_directListings.functions[9] = ExtensionFunction( - DirectListingsLogic.buyFromListing.selector, - "buyFromListing(uint256,address,uint256,address,uint256)" - ); - extension_directListings.functions[10] = ExtensionFunction( - DirectListingsLogic.getAllListings.selector, - "getAllListings(uint256,uint256)" - ); - extension_directListings.functions[11] = ExtensionFunction( - DirectListingsLogic.getAllValidListings.selector, - "getAllValidListings(uint256,uint256)" - ); - extension_directListings.functions[12] = ExtensionFunction( - DirectListingsLogic.getListing.selector, - "getListing(uint256)" - ); - - extensions[0] = extension_directListings; - } - - function test_cancelListing_whenListingDoesntExist() public { - vm.prank(seller); - vm.expectRevert("Marketplace: invalid listing."); - DirectListingsLogic(marketplace).cancelListing(listingId); - } - - modifier whenListingExists() { - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - vm.stopPrank(); - - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - listingId = DirectListingsLogic(marketplace).createListing(listingParams); - erc721.setApprovalForAll(marketplace, false); - vm.stopPrank(); - - vm.prank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(erc721)); - _; - } - - function test_cancelListing_whenCallerNotListingCreator() public whenListingExists { - vm.prank(address(0x4567)); - vm.expectRevert("Marketplace: not listing creator."); - DirectListingsLogic(marketplace).cancelListing(listingId); - } - - modifier whenCallerIsListingCreator() { - _; - } - - function test_cancelListing_success() public whenListingExists whenCallerIsListingCreator { - vm.warp(listingParams.startTimestamp + 1); - - assertEq(uint8(DirectListingsLogic(marketplace).getListing(listingId).status), uint8(1)); // CREATED - - vm.prank(seller); - vm.expectEmit(true, true, true, true); - emit CancelledListing(seller, listingId); - DirectListingsLogic(marketplace).cancelListing(listingId); - - assertEq(uint8(DirectListingsLogic(marketplace).getListing(listingId).status), uint8(3)); // CANCELLED - } -} diff --git a/src/test/marketplace/direct-listings/cancelListing/cancelListing.tree b/src/test/marketplace/direct-listings/cancelListing/cancelListing.tree deleted file mode 100644 index dd34eb23d..000000000 --- a/src/test/marketplace/direct-listings/cancelListing/cancelListing.tree +++ /dev/null @@ -1,9 +0,0 @@ -function cancelListing(uint256 _listingId) -├── when no listing with the given listing ID exists -│ └── it should revert ✅ -└── when listing with the given listing ID exists - ├── when the caller is not listing creator - │ └── it should revert ✅ - └── when the caller is listing creator ✅ - ├── it should set status of listing as cancelled - └── it should emit CancelledListing event \ No newline at end of file diff --git a/src/test/marketplace/direct-listings/createListing/createListing.t.sol b/src/test/marketplace/direct-listings/createListing/createListing.t.sol deleted file mode 100644 index 415a1b71d..000000000 --- a/src/test/marketplace/direct-listings/createListing/createListing.t.sol +++ /dev/null @@ -1,363 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { MarketplaceV3 } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { DirectListingsLogic } from "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol"; -import { IDirectListings } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -contract CreateListingTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - - // Default listing parameters - IDirectListings.ListingParameters internal listingParams; - - // Events to test - - /// @notice Emitted when a new listing is created. - event NewListing( - address indexed listingCreator, - uint256 indexed listingId, - address indexed assetContract, - IDirectListings.Listing listing - ); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - - vm.stopPrank(); - - // Setup listing params - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100; - uint128 endTimestamp = 200; - bool reserved = true; - - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Mint 1 ERC721 NFT to seller - erc721.mint(seller, listingParams.quantity); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `DirectListings` - address directListings = address(new DirectListingsLogic(address(weth))); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "DirectListingsLogic", - metadataURI: "ipfs://DirectListings", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](13); - extension_directListings.functions[0] = ExtensionFunction( - DirectListingsLogic.totalListings.selector, - "totalListings()" - ); - extension_directListings.functions[1] = ExtensionFunction( - DirectListingsLogic.isBuyerApprovedForListing.selector, - "isBuyerApprovedForListing(uint256,address)" - ); - extension_directListings.functions[2] = ExtensionFunction( - DirectListingsLogic.isCurrencyApprovedForListing.selector, - "isCurrencyApprovedForListing(uint256,address)" - ); - extension_directListings.functions[3] = ExtensionFunction( - DirectListingsLogic.currencyPriceForListing.selector, - "currencyPriceForListing(uint256,address)" - ); - extension_directListings.functions[4] = ExtensionFunction( - DirectListingsLogic.createListing.selector, - "createListing((address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[5] = ExtensionFunction( - DirectListingsLogic.updateListing.selector, - "updateListing(uint256,(address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[6] = ExtensionFunction( - DirectListingsLogic.cancelListing.selector, - "cancelListing(uint256)" - ); - extension_directListings.functions[7] = ExtensionFunction( - DirectListingsLogic.approveBuyerForListing.selector, - "approveBuyerForListing(uint256,address,bool)" - ); - extension_directListings.functions[8] = ExtensionFunction( - DirectListingsLogic.approveCurrencyForListing.selector, - "approveCurrencyForListing(uint256,address,uint256)" - ); - extension_directListings.functions[9] = ExtensionFunction( - DirectListingsLogic.buyFromListing.selector, - "buyFromListing(uint256,address,uint256,address,uint256)" - ); - extension_directListings.functions[10] = ExtensionFunction( - DirectListingsLogic.getAllListings.selector, - "getAllListings(uint256,uint256)" - ); - extension_directListings.functions[11] = ExtensionFunction( - DirectListingsLogic.getAllValidListings.selector, - "getAllValidListings(uint256,uint256)" - ); - extension_directListings.functions[12] = ExtensionFunction( - DirectListingsLogic.getListing.selector, - "getListing(uint256)" - ); - - extensions[0] = extension_directListings; - } - - function test_createListing_whenCallerDoesNotHaveListerRole() public { - bytes32 role = keccak256("LISTER_ROLE"); - assertEq(Permissions(marketplace).hasRole(role, seller), false); - - vm.prank(seller); - vm.expectRevert("!LISTER_ROLE"); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - modifier whenCallerHasListerRole() { - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - _; - } - - function test_createListing_whenAssetDoesNotHaveAssetRole() public whenCallerHasListerRole { - bytes32 role = keccak256("ASSET_ROLE"); - assertEq(Permissions(marketplace).hasRole(role, listingParams.assetContract), false); - - vm.prank(seller); - vm.expectRevert("!ASSET_ROLE"); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - modifier whenAssetHasAssetRole() { - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), listingParams.assetContract); - _; - } - - function test_createListing_startTimeGteEndTime() public whenCallerHasListerRole whenAssetHasAssetRole { - listingParams.startTimestamp = 200; - listingParams.endTimestamp = 100; - - vm.prank(seller); - vm.expectRevert("Marketplace: endTimestamp not greater than startTimestamp."); - DirectListingsLogic(marketplace).createListing(listingParams); - - listingParams.endTimestamp = 200; - - vm.prank(seller); - vm.expectRevert("Marketplace: endTimestamp not greater than startTimestamp."); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - modifier whenStartTimeLtEndTime() { - listingParams.startTimestamp = 100; - listingParams.endTimestamp = 200; - _; - } - - modifier whenStartTimeLtBlockTimestamp() { - // This warp has no effect on subsequent tests since they include a vm.warp in their own test body. - vm.warp(listingParams.startTimestamp + 1); - _; - } - - function test_createListing_whenStartTimeMoreThanHourBeforeBlockTimestamp() - public - whenCallerHasListerRole - whenAssetHasAssetRole - whenStartTimeLtEndTime - whenStartTimeLtBlockTimestamp - { - vm.warp(listingParams.startTimestamp + (60 minutes + 1)); - - vm.prank(seller); - vm.expectRevert("Marketplace: invalid startTimestamp."); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - modifier whenStartTimeWithinHourOfBlockTimestamp() { - vm.warp(listingParams.startTimestamp + 59 minutes); - _; - } - - function test_createListing_whenListingParamsAreInvalid_1() - public - whenCallerHasListerRole - whenAssetHasAssetRole - whenStartTimeLtEndTime - whenStartTimeLtBlockTimestamp - whenStartTimeWithinHourOfBlockTimestamp - { - // This is one of the ways in which params are considered invalid. - // We've written separate BTT tests for `_validateNewListing` - listingParams.quantity = 0; - - vm.prank(seller); - vm.expectRevert("Marketplace: listing zero quantity."); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - modifier whenListingParamsAreValid() { - // Approve marketplace to transfer tokens -- else listing params are considered invalid. - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - _; - } - - function test_createListing_whenListingParamsAreValid_1() - public - whenCallerHasListerRole - whenAssetHasAssetRole - whenStartTimeLtEndTime - whenStartTimeLtBlockTimestamp - whenStartTimeWithinHourOfBlockTimestamp - whenListingParamsAreValid - { - uint256 expectedListingId = 0; - - assertEq(DirectListingsLogic(marketplace).totalListings(), 0); - assertEq(DirectListingsLogic(marketplace).getListing(expectedListingId).assetContract, address(0)); - - IDirectListings.Listing memory listing; - - vm.prank(seller); - vm.expectEmit(true, true, true, false); - emit NewListing(seller, expectedListingId, listingParams.assetContract, listing); - DirectListingsLogic(marketplace).createListing(listingParams); - - listing = DirectListingsLogic(marketplace).getListing(expectedListingId); - assertEq(listing.assetContract, listingParams.assetContract); - assertEq(listing.tokenId, listingParams.tokenId); - assertEq(listing.quantity, listingParams.quantity); - assertEq(listing.currency, listingParams.currency); - assertEq(listing.pricePerToken, listingParams.pricePerToken); - assertEq(listing.endTimestamp, block.timestamp + (listingParams.endTimestamp - listingParams.startTimestamp)); - assertEq(listing.startTimestamp, block.timestamp); - assertEq(listing.listingCreator, seller); - assertEq(listing.reserved, true); - assertEq(uint256(listing.status), 1); // Status.CREATED - assertEq(uint256(listing.tokenType), 0); // TokenType.ERC721 - - assertEq(DirectListingsLogic(marketplace).totalListings(), 1); - assertEq(DirectListingsLogic(marketplace).getAllListings(0, 0).length, 1); - assertEq(DirectListingsLogic(marketplace).getAllValidListings(0, 0).length, 1); - } - - modifier whenStartTimeGteBlockTimestamp() { - vm.warp(listingParams.startTimestamp - 1 minutes); - _; - } - - function test_createListing_whenListingParamsAreInvalid_2() - public - whenCallerHasListerRole - whenAssetHasAssetRole - whenStartTimeLtEndTime - whenStartTimeGteBlockTimestamp - { - // This is one of the ways in which params are considered invalid. - // We've written separate BTT tests for `_validateNewListing` - listingParams.quantity = 0; - - vm.prank(seller); - vm.expectRevert("Marketplace: listing zero quantity."); - DirectListingsLogic(marketplace).createListing(listingParams); - } - - function test_createListing_whenListingParamsAreValid_2() - public - whenCallerHasListerRole - whenAssetHasAssetRole - whenStartTimeLtEndTime - whenStartTimeGteBlockTimestamp - whenListingParamsAreValid - { - uint256 expectedListingId = 0; - - assertEq(DirectListingsLogic(marketplace).totalListings(), 0); - assertEq(DirectListingsLogic(marketplace).getListing(expectedListingId).assetContract, address(0)); - - IDirectListings.Listing memory listing; - - vm.prank(seller); - vm.expectEmit(true, true, true, false); - emit NewListing(seller, expectedListingId, listingParams.assetContract, listing); - DirectListingsLogic(marketplace).createListing(listingParams); - - listing = DirectListingsLogic(marketplace).getListing(expectedListingId); - assertEq(listing.assetContract, listingParams.assetContract); - assertEq(listing.tokenId, listingParams.tokenId); - assertEq(listing.quantity, listingParams.quantity); - assertEq(listing.currency, listingParams.currency); - assertEq(listing.pricePerToken, listingParams.pricePerToken); - assertEq(listing.endTimestamp, listingParams.endTimestamp); - assertEq(listing.startTimestamp, listingParams.startTimestamp); - assertEq(listing.listingCreator, seller); - assertEq(listing.reserved, true); - assertEq(uint256(listing.status), 1); // Status.CREATED - assertEq(uint256(listing.tokenType), 0); // TokenType.ERC721 - - assertEq(DirectListingsLogic(marketplace).totalListings(), 1); - assertEq(DirectListingsLogic(marketplace).getAllListings(0, 0).length, 1); - assertEq(DirectListingsLogic(marketplace).getAllValidListings(0, 0).length, 0); - } -} diff --git a/src/test/marketplace/direct-listings/createListing/createListing.tree b/src/test/marketplace/direct-listings/createListing/createListing.tree deleted file mode 100644 index 8964c7798..000000000 --- a/src/test/marketplace/direct-listings/createListing/createListing.tree +++ /dev/null @@ -1,27 +0,0 @@ -function createListing(ListingParameters calldata _params) -├── when caller does not have LISTER_ROLE -│ └── it should revert -└── when the caller has lister LISTER_ROLE - ├── when the asset to list does not have ASSET_ROLE - │ └── it should revert - └── when the asset to list has ASSET_ROLE - ├── when the start time is greater i.e. after the end time - │ └── it should revert - └── when the start time is less than i.e. before the end time - ├── when the start time is less than i.e. before block timestamp - │ ├── when the start time is more than 60 minutes before block timestamp - │ │ └── it should revert - │ └── when the start time is less than or equal to 60 minutes before block timestamp - │ ├── when the listing params are invalid - │ │ └── it should revert - │ └── when the listing params are valid - │ ├── it should store the listing at a new listing ID - │ ├── it should return the listing ID - │ └── it should emit NewListing event with listing creator, listing ID, and listing data - └── when the start time is greater than i.e. after, or equal to block timestamp - ├── when the listing params are invalid - │ └── it should revert - └── when the listing params are valid - ├── it should store the listing at a new listing ID - ├── it should return the listing ID - └── it should emit NewListing event with listing creator, listing ID, and listing data \ No newline at end of file diff --git a/src/test/marketplace/direct-listings/updateListing/updateListing.t.sol b/src/test/marketplace/direct-listings/updateListing/updateListing.t.sol deleted file mode 100644 index 6b35615ea..000000000 --- a/src/test/marketplace/direct-listings/updateListing/updateListing.t.sol +++ /dev/null @@ -1,502 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { MarketplaceV3 } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { DirectListingsLogic } from "contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol"; -import { IDirectListings } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -contract UpdateListingTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - - // Default listing parameters - IDirectListings.ListingParameters internal listingParams; - uint256 internal listingId = 0; - - // Events to test - - /// @notice Emitted when a listing is updated. - event UpdatedListing( - address indexed listingCreator, - uint256 indexed listingId, - address indexed assetContract, - IDirectListings.Listing listing - ); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - - vm.stopPrank(); - - // Setup listing params - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 pricePerToken = 1 ether; - uint128 startTimestamp = 100 minutes; - uint128 endTimestamp = 200 minutes; - bool reserved = true; - - listingParams = IDirectListings.ListingParameters( - assetContract, - tokenId, - quantity, - currency, - pricePerToken, - startTimestamp, - endTimestamp, - reserved - ); - - // Mint 1 ERC721 NFT to seller - erc721.mint(seller, listingParams.quantity); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `DirectListings` - address directListings = address(new DirectListingsLogic(address(weth))); - vm.label(directListings, "DirectListings_Extension"); - - // Extension: DirectListingsLogic - Extension memory extension_directListings; - extension_directListings.metadata = ExtensionMetadata({ - name: "DirectListingsLogic", - metadataURI: "ipfs://DirectListings", - implementation: directListings - }); - - extension_directListings.functions = new ExtensionFunction[](13); - extension_directListings.functions[0] = ExtensionFunction( - DirectListingsLogic.totalListings.selector, - "totalListings()" - ); - extension_directListings.functions[1] = ExtensionFunction( - DirectListingsLogic.isBuyerApprovedForListing.selector, - "isBuyerApprovedForListing(uint256,address)" - ); - extension_directListings.functions[2] = ExtensionFunction( - DirectListingsLogic.isCurrencyApprovedForListing.selector, - "isCurrencyApprovedForListing(uint256,address)" - ); - extension_directListings.functions[3] = ExtensionFunction( - DirectListingsLogic.currencyPriceForListing.selector, - "currencyPriceForListing(uint256,address)" - ); - extension_directListings.functions[4] = ExtensionFunction( - DirectListingsLogic.createListing.selector, - "createListing((address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[5] = ExtensionFunction( - DirectListingsLogic.updateListing.selector, - "updateListing(uint256,(address,uint256,uint256,address,uint256,uint128,uint128,bool))" - ); - extension_directListings.functions[6] = ExtensionFunction( - DirectListingsLogic.cancelListing.selector, - "cancelListing(uint256)" - ); - extension_directListings.functions[7] = ExtensionFunction( - DirectListingsLogic.approveBuyerForListing.selector, - "approveBuyerForListing(uint256,address,bool)" - ); - extension_directListings.functions[8] = ExtensionFunction( - DirectListingsLogic.approveCurrencyForListing.selector, - "approveCurrencyForListing(uint256,address,uint256)" - ); - extension_directListings.functions[9] = ExtensionFunction( - DirectListingsLogic.buyFromListing.selector, - "buyFromListing(uint256,address,uint256,address,uint256)" - ); - extension_directListings.functions[10] = ExtensionFunction( - DirectListingsLogic.getAllListings.selector, - "getAllListings(uint256,uint256)" - ); - extension_directListings.functions[11] = ExtensionFunction( - DirectListingsLogic.getAllValidListings.selector, - "getAllValidListings(uint256,uint256)" - ); - extension_directListings.functions[12] = ExtensionFunction( - DirectListingsLogic.getListing.selector, - "getListing(uint256)" - ); - - extensions[0] = extension_directListings; - } - - function test_updateListing_whenListingDoesNotExist() public { - vm.prank(seller); - vm.expectRevert("Marketplace: invalid listing."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - modifier whenListingExists() { - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - vm.stopPrank(); - - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - listingId = DirectListingsLogic(marketplace).createListing(listingParams); - erc721.setApprovalForAll(marketplace, false); - vm.stopPrank(); - - vm.prank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(erc721)); - _; - } - - function test_updateListing_whenAssetDoesntHaveAssetRole() public whenListingExists { - vm.prank(seller); - vm.expectRevert("!ASSET_ROLE"); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - modifier whenAssetHasAssetRole() { - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - _; - } - - function test_updateListing_whenCallerIsNotListingCreator() public whenListingExists whenAssetHasAssetRole { - vm.prank(address(0x4567)); - vm.expectRevert("Marketplace: not listing creator."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - modifier whenCallerIsListingCreator() { - _; - } - - function test_updateListing_whenListingHasExpired() - public - whenListingExists - whenAssetHasAssetRole - whenCallerIsListingCreator - { - vm.warp(listingParams.endTimestamp + 1); - - vm.prank(seller); - vm.expectRevert("Marketplace: listing expired."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - modifier whenListingNotExpired() { - vm.warp(0); - _; - } - - function test_updateListing_whenUpdatedAssetIsDifferent() - public - whenListingExists - whenAssetHasAssetRole - whenCallerIsListingCreator - whenListingNotExpired - { - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - listingParams.assetContract = address(erc1155); - - vm.prank(seller); - vm.expectRevert("Marketplace: cannot update what token is listed."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - - listingParams.assetContract = address(erc721); - listingParams.tokenId = 10; - - vm.prank(seller); - vm.expectRevert("Marketplace: cannot update what token is listed."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - modifier whenUpdatedAssetIsSame() { - _; - } - - function test_updateListing_whenUpdatedStartTimeGteEndTime() - public - whenListingExists - whenAssetHasAssetRole - whenCallerIsListingCreator - whenListingNotExpired - whenUpdatedAssetIsSame - { - listingParams.startTimestamp = 200; - listingParams.endTimestamp = 100; - - vm.prank(seller); - vm.expectRevert("Marketplace: endTimestamp not greater than startTimestamp."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - modifier whenUpdatedStartTimeLtUpdatedEndTime() { - _; - } - - function test_updateListing_whenUpdateMakesActiveListingInactive() - public - whenListingExists - whenAssetHasAssetRole - whenCallerIsListingCreator - whenListingNotExpired - whenUpdatedAssetIsSame - whenUpdatedStartTimeLtUpdatedEndTime - { - vm.warp(listingParams.startTimestamp + 1); - - listingParams.startTimestamp += 50; - - vm.prank(seller); - vm.expectRevert("Marketplace: listing already active."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - modifier whenUpdateDoesntMakeActiveListingInactive() { - _; - } - - modifier whenUpdatedStartIsDiffAndInPast() { - vm.warp(listingParams.startTimestamp - 1 minutes); - listingParams.startTimestamp -= 2 minutes; - _; - } - - function test_updateListing_whenUpdatedStartIsMoreThanHourInPast() - public - whenListingExists - whenAssetHasAssetRole - whenCallerIsListingCreator - whenListingNotExpired - whenUpdatedAssetIsSame - whenUpdatedStartTimeLtUpdatedEndTime - whenUpdateDoesntMakeActiveListingInactive - whenUpdatedStartIsDiffAndInPast - { - listingParams.startTimestamp = 30 minutes; - - vm.prank(seller); - vm.expectRevert("Marketplace: invalid startTimestamp."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - modifier whenUpdatedStartIsWithinPastHour() { - listingParams.startTimestamp = 90 minutes; - _; - } - - function test_updateListing_whenUpdatedPriceIsDifferentFromApprovedPrice_1() - public - whenListingExists - whenAssetHasAssetRole - whenCallerIsListingCreator - whenListingNotExpired - whenUpdatedAssetIsSame - whenUpdatedStartTimeLtUpdatedEndTime - whenUpdateDoesntMakeActiveListingInactive - whenUpdatedStartIsDiffAndInPast - whenUpdatedStartIsWithinPastHour - { - vm.prank(seller); - DirectListingsLogic(marketplace).approveCurrencyForListing(listingId, address(weth), 2 ether); - - listingParams.currency = address(weth); - - vm.prank(seller); - vm.expectRevert("Marketplace: price different from approved price"); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - modifier whenUpdatedPriceIsSameAsApprovedPrice() { - _; - } - - function test_updateListing_whenListingParamsAreInvalid_1() - public - whenListingExists - whenAssetHasAssetRole - whenCallerIsListingCreator - whenListingNotExpired - whenUpdatedAssetIsSame - whenUpdatedStartTimeLtUpdatedEndTime - whenUpdateDoesntMakeActiveListingInactive - whenUpdatedStartIsDiffAndInPast - whenUpdatedStartIsWithinPastHour - whenUpdatedPriceIsSameAsApprovedPrice - { - // This is one of the ways in which params can be invalid. - // Separate tests for `_validateNewListingParams` - listingParams.quantity = 0; - - vm.prank(seller); - vm.expectRevert("Marketplace: listing zero quantity."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - modifier whenListingParamsAreValid() { - _; - } - - function test_updateListing_whenListingParamsAreValid_1() - public - whenListingExists - whenAssetHasAssetRole - whenCallerIsListingCreator - whenListingNotExpired - whenUpdatedAssetIsSame - whenUpdatedStartTimeLtUpdatedEndTime - whenUpdateDoesntMakeActiveListingInactive - whenUpdatedStartIsDiffAndInPast - whenUpdatedStartIsWithinPastHour - whenUpdatedPriceIsSameAsApprovedPrice - whenListingParamsAreValid - { - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - IDirectListings.Listing memory listing; - - vm.prank(seller); - vm.expectEmit(true, true, true, false); - emit UpdatedListing(seller, listingId, listingParams.assetContract, listing); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - - IDirectListings.Listing memory updatedListing = DirectListingsLogic(marketplace).getListing(listingId); - - assertEq(updatedListing.assetContract, listingParams.assetContract); - assertEq(updatedListing.tokenId, listingParams.tokenId); - assertEq(updatedListing.quantity, listingParams.quantity); - assertEq(updatedListing.currency, listingParams.currency); - assertEq(updatedListing.pricePerToken, listingParams.pricePerToken); - assertEq(updatedListing.endTimestamp, listingParams.endTimestamp); - assertEq(updatedListing.startTimestamp, block.timestamp); - assertEq(updatedListing.listingCreator, seller); - assertEq(updatedListing.reserved, true); - assertEq(uint256(updatedListing.status), 1); // Status.CREATED - assertEq(uint256(updatedListing.tokenType), 0); // TokenType.ERC721 - } - - modifier whenUpdatedStartIsSameAsCurrentStart() { - _; - } - - function test_updateListing_whenUpdatedPriceIsDifferentFromApprovedPrice_2() - public - whenListingExists - whenAssetHasAssetRole - whenCallerIsListingCreator - whenListingNotExpired - whenUpdatedAssetIsSame - whenUpdatedStartTimeLtUpdatedEndTime - whenUpdateDoesntMakeActiveListingInactive - whenUpdatedStartIsSameAsCurrentStart - { - vm.prank(seller); - DirectListingsLogic(marketplace).approveCurrencyForListing(listingId, address(weth), 2 ether); - - listingParams.currency = address(weth); - - vm.prank(seller); - vm.expectRevert("Marketplace: price different from approved price"); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - function test_updateListing_whenListingParamsAreInvalid_2() - public - whenListingExists - whenAssetHasAssetRole - whenCallerIsListingCreator - whenListingNotExpired - whenUpdatedAssetIsSame - whenUpdatedStartTimeLtUpdatedEndTime - whenUpdateDoesntMakeActiveListingInactive - whenUpdatedStartIsSameAsCurrentStart - whenUpdatedPriceIsSameAsApprovedPrice - { - // This is one of the ways in which params can be invalid. - // Separate tests for `_validateNewListingParams` - listingParams.quantity = 0; - - vm.prank(seller); - vm.expectRevert("Marketplace: listing zero quantity."); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - } - - function test_updateListing_whenListingParamsAreValid_2() - public - whenListingExists - whenAssetHasAssetRole - whenCallerIsListingCreator - whenListingNotExpired - whenUpdatedAssetIsSame - whenUpdatedStartTimeLtUpdatedEndTime - whenUpdateDoesntMakeActiveListingInactive - whenUpdatedStartIsSameAsCurrentStart - whenUpdatedPriceIsSameAsApprovedPrice - whenListingParamsAreValid - { - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - IDirectListings.Listing memory listing; - - vm.prank(seller); - vm.expectEmit(true, true, true, false); - emit UpdatedListing(seller, listingId, listingParams.assetContract, listing); - DirectListingsLogic(marketplace).updateListing(listingId, listingParams); - - IDirectListings.Listing memory updatedListing = DirectListingsLogic(marketplace).getListing(listingId); - - assertEq(updatedListing.assetContract, listingParams.assetContract); - assertEq(updatedListing.tokenId, listingParams.tokenId); - assertEq(updatedListing.quantity, listingParams.quantity); - assertEq(updatedListing.currency, listingParams.currency); - assertEq(updatedListing.pricePerToken, listingParams.pricePerToken); - assertEq(updatedListing.endTimestamp, listingParams.endTimestamp); - assertEq(updatedListing.startTimestamp, listingParams.startTimestamp); - assertEq(updatedListing.listingCreator, seller); - assertEq(updatedListing.reserved, true); - assertEq(uint256(updatedListing.status), 1); // Status.CREATED - assertEq(uint256(updatedListing.tokenType), 0); // TokenType.ERC721 - } -} diff --git a/src/test/marketplace/direct-listings/updateListing/updateListing.tree b/src/test/marketplace/direct-listings/updateListing/updateListing.tree deleted file mode 100644 index 982ed1076..000000000 --- a/src/test/marketplace/direct-listings/updateListing/updateListing.tree +++ /dev/null @@ -1,43 +0,0 @@ -function updateListing(uint256 _listingId, ListingParameters memory _params) -├── when the listing does not exist -│ └── it should revert ✅ -└── when listing exists - ├── when asset does not have ASSET_ROLE - │ └── it should revert ✅ - └── when asset has ASSET_ROLE - ├── when caller is not listing creator - │ └── it should revert ✅ - └── when caller is listing creator - ├── when listing has expired - │ └── it should revert ✅ - └── when listing has not expired - ├── when the updated asset is different from the listed asset - │ └── it should revert ✅ - └── when the updated asset is the same as the listed asset - ├── when the updated start time is greater or equal to than the updated end time - │ └── it should revert ✅ - └── when the updated start time is less than the updated end time - ├── when update makes active listing inactive - │ └── it should revert ✅ - └── when update does not make active listing inactive - ├── when the updated start time is in the past and different from the listed start time - │ ├── when the updated start time is more than 60 minutes before block timestamp - │ │ └── it should revert ✅ - │ └── when the updated start time is within 60 minutes past block timestamp - │ ├── when updated price in updated currency different from approved price for updated currency - │ │ └── it should revert ✅ - │ └── when updated price in updated currency is same as approved price for updated currency - │ ├── when updated listing params are invalid - │ │ └── it should revert ✅ - │ └── when updated listing params are valid ✅ - │ ├── it should store updated listing at the same listing ID - │ └── it should emit UpdatedListing event with listing creator, listing ID, updated asset contract and listing data - └── when the updated start time is same as listed start time - ├── when updated price in updated currency different from approved price for updated currency - │ └── it should revert ✅ - └── when updated price in updated currency is same as approved price for updated currency - ├── when updated listing params are invalid - │ └── it should revert ✅ - └── when updated listing params are valid ✅ - ├── it should store updated listing at the same listing ID - └── it should emit UpdatedListing event with listing creator, listing ID, updated asset contract and listing data \ No newline at end of file diff --git a/src/test/marketplace/english-auctions/_payout/_payout.t.sol b/src/test/marketplace/english-auctions/_payout/_payout.t.sol deleted file mode 100644 index ed3f15927..000000000 --- a/src/test/marketplace/english-auctions/_payout/_payout.t.sol +++ /dev/null @@ -1,312 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test helper imports -import "../../../utils/BaseTest.sol"; - -// Test contracts and interfaces -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { MarketplaceV3, IPlatformFee } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { EnglishAuctionsLogic } from "contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC721Base } from "contracts/base/ERC721Base.sol"; -import { MockRoyaltyEngineV1 } from "../../../mocks/MockRoyaltyEngineV1.sol"; -import { PlatformFee } from "contracts/extension/PlatformFee.sol"; - -import { IEnglishAuctions } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -contract ReentrantRecipient is ERC1155Holder { - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes memory data - ) public virtual override returns (bytes4) { - uint256 auctionId = 0; - uint256 bidAmount = 10 ether; - EnglishAuctionsLogic(msg.sender).bidInAuction(auctionId, bidAmount); - return super.onERC1155Received(operator, from, id, value, data); - } -} - -contract EnglishAuctionsPayoutTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - address private defaultFeeRecipient; - - // Auction parameters - uint256 internal auctionId; - uint256 internal bidAmount; - address internal winningBidder = address(0x123); - IEnglishAuctions.AuctionParameters internal auctionParams; - - // Events - event NewBid( - uint256 indexed auctionId, - address indexed bidder, - address indexed assetContract, - uint256 bidAmount, - IEnglishAuctions.Auction auction - ); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), platformFeeRecipient, uint16(platformFeeBps)) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - - // Sample auction parameters. - address assetContract = address(erc1155); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 100 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100 minutes; - uint64 endTimestamp = 200 minutes; - - // Auction tokens. - auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - // Set bidAmount - bidAmount = auctionParams.minimumBidAmount; - - // Mint NFT to seller. - erc721.mint(seller, 1); // to, amount - erc1155.mint(seller, 0, 100); // to, id, amount - - // Create auction - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - erc1155.setApprovalForAll(marketplace, true); - auctionId = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - vm.stopPrank(); - - // Mint currency to bidder. - erc20.mint(buyer, 10_000 ether); - - vm.prank(buyer); - erc20.approve(marketplace, 100 ether); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `EnglishAuctions` - address englishAuctions = address(new EnglishAuctionsLogic(address(weth))); - defaultFeeRecipient = EnglishAuctionsLogic(englishAuctions).DEFAULT_FEE_RECIPIENT(); - vm.label(englishAuctions, "EnglishAuctions_Extension"); - - // Extension: EnglishAuctionsLogic - Extension memory extension_englishAuctions; - extension_englishAuctions.metadata = ExtensionMetadata({ - name: "EnglishAuctionsLogic", - metadataURI: "ipfs://EnglishAuctions", - implementation: englishAuctions - }); - - extension_englishAuctions.functions = new ExtensionFunction[](12); - extension_englishAuctions.functions[0] = ExtensionFunction( - EnglishAuctionsLogic.totalAuctions.selector, - "totalAuctions()" - ); - extension_englishAuctions.functions[1] = ExtensionFunction( - EnglishAuctionsLogic.createAuction.selector, - "createAuction((address,uint256,uint256,address,uint256,uint256,uint64,uint64,uint64,uint64))" - ); - extension_englishAuctions.functions[2] = ExtensionFunction( - EnglishAuctionsLogic.cancelAuction.selector, - "cancelAuction(uint256)" - ); - extension_englishAuctions.functions[3] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionPayout.selector, - "collectAuctionPayout(uint256)" - ); - extension_englishAuctions.functions[4] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionTokens.selector, - "collectAuctionTokens(uint256)" - ); - extension_englishAuctions.functions[5] = ExtensionFunction( - EnglishAuctionsLogic.bidInAuction.selector, - "bidInAuction(uint256,uint256)" - ); - extension_englishAuctions.functions[6] = ExtensionFunction( - EnglishAuctionsLogic.isNewWinningBid.selector, - "isNewWinningBid(uint256,uint256)" - ); - extension_englishAuctions.functions[7] = ExtensionFunction( - EnglishAuctionsLogic.getAuction.selector, - "getAuction(uint256)" - ); - extension_englishAuctions.functions[8] = ExtensionFunction( - EnglishAuctionsLogic.getAllAuctions.selector, - "getAllAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[9] = ExtensionFunction( - EnglishAuctionsLogic.getAllValidAuctions.selector, - "getAllValidAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[10] = ExtensionFunction( - EnglishAuctionsLogic.getWinningBid.selector, - "getWinningBid(uint256)" - ); - extension_englishAuctions.functions[11] = ExtensionFunction( - EnglishAuctionsLogic.isAuctionExpired.selector, - "isAuctionExpired(uint256)" - ); - - extensions[0] = extension_englishAuctions; - } - - address payable[] internal mockRecipients; - uint256[] internal mockAmounts; - MockRoyaltyEngineV1 internal royaltyEngine; - - function _setupRoyaltyEngine() private { - mockRecipients.push(payable(address(0x12345))); - mockRecipients.push(payable(address(0x56789))); - - mockAmounts.push(10 ether); - mockAmounts.push(15 ether); - - royaltyEngine = new MockRoyaltyEngineV1(mockRecipients, mockAmounts); - } - - function test_payout_whenZeroRoyaltyRecipients() public { - vm.warp(auctionParams.startTimestamp); - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, auctionParams.buyoutBidAmount); - - vm.prank(seller); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - - uint256 totalPrice = auctionParams.buyoutBidAmount; - - uint256 platformFees = (totalPrice * platformFeeBps) / 10_000; - - { - uint256 defaultFee = (totalPrice * 100) / 10_000; - - // Platform fee recipient receives correct amount - assertBalERC20Eq(address(erc20), platformFeeRecipient, platformFees); - - // Seller gets total price minus royalty amounts - assertBalERC20Eq(address(erc20), seller, totalPrice - platformFees - defaultFee); - - assertBalERC20Eq(address(erc20), defaultFeeRecipient, defaultFee); - } - } - - modifier whenNonZeroRoyaltyRecipients() { - _setupRoyaltyEngine(); - - // Add RoyaltyEngine to marketplace - vm.prank(marketplaceDeployer); - RoyaltyPaymentsLogic(marketplace).setRoyaltyEngine(address(royaltyEngine)); - - _; - } - - function test_payout_whenInsufficientFundsToPayRoyaltyAfterPlatformFeePayout() public whenNonZeroRoyaltyRecipients { - vm.prank(marketplaceDeployer); - PlatformFee(marketplace).setPlatformFeeInfo(platformFeeRecipient, 9899); // 99.99% fees with 100 bps default; - - // Buy tokens from listing. - vm.warp(auctionParams.startTimestamp); - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, auctionParams.buyoutBidAmount); - - vm.prank(seller); - vm.expectRevert("fees exceed the price"); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - } - - function test_payout_whenSufficientFundsToPayRoyaltyAfterPlatformFeePayout() public whenNonZeroRoyaltyRecipients { - assertEq(RoyaltyPaymentsLogic(marketplace).getRoyaltyEngineAddress(), address(royaltyEngine)); - - vm.warp(auctionParams.startTimestamp); - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, auctionParams.buyoutBidAmount); - - vm.prank(seller); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - - uint256 totalPrice = auctionParams.buyoutBidAmount; - uint256 platformFees = (totalPrice * platformFeeBps) / 10_000; - - { - uint256 defaultFee = (totalPrice * 100) / 10_000; - - // Royalty recipients receive correct amounts - assertBalERC20Eq(address(erc20), mockRecipients[0], mockAmounts[0]); - assertBalERC20Eq(address(erc20), mockRecipients[1], mockAmounts[1]); - - // Platform fee recipient receives correct amount - assertBalERC20Eq(address(erc20), platformFeeRecipient, platformFees); - - // Seller gets total price minus royalty amounts - assertBalERC20Eq( - address(erc20), - seller, - totalPrice - mockAmounts[0] - mockAmounts[1] - platformFees - defaultFee - ); - - assertBalERC20Eq(address(erc20), defaultFeeRecipient, defaultFee); - } - } -} diff --git a/src/test/marketplace/english-auctions/_payout/_payout.tree b/src/test/marketplace/english-auctions/_payout/_payout.tree deleted file mode 100644 index 36b930e11..000000000 --- a/src/test/marketplace/english-auctions/_payout/_payout.tree +++ /dev/null @@ -1,17 +0,0 @@ -function _payout( - address _payer, - address _payee, - address _currencyToUse, - uint256 _totalPayoutAmount, - Auction memory _targetAuction -) -├── when there are zero royalty recipients ✅ -│ ├── it should transfer platform fee from payer to platform fee recipient -│ └── it should transfer remainder of currency from payer to payee -└── when there are non-zero royalty recipients - ├── when the total royalty payout exceeds remainder payout after having paid platform fee - │ └── it should revert ✅ - └── when the total royalty payout does not exceed remainder payout after having paid platform fee ✅ - ├── it should transfer platform fee from payer to platform fee recipient - ├── it should transfer royalty fee from payer to royalty recipients - └── it should transfer remainder of currency from payer to payeew \ No newline at end of file diff --git a/src/test/marketplace/english-auctions/_transferAuctionTokens/_transferAuctionTokens.t.sol b/src/test/marketplace/english-auctions/_transferAuctionTokens/_transferAuctionTokens.t.sol deleted file mode 100644 index 5c36a091d..000000000 --- a/src/test/marketplace/english-auctions/_transferAuctionTokens/_transferAuctionTokens.t.sol +++ /dev/null @@ -1,198 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test helper imports -import "../../../utils/BaseTest.sol"; - -// Test contracts and interfaces -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { MarketplaceV3, IPlatformFee } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { EnglishAuctionsLogic } from "contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC721Base } from "contracts/base/ERC721Base.sol"; -import { MockRoyaltyEngineV1 } from "../../../mocks/MockRoyaltyEngineV1.sol"; -import { PlatformFee } from "contracts/extension/PlatformFee.sol"; - -import { IEnglishAuctions } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -contract MockTransferAuctionTokens is EnglishAuctionsLogic { - constructor(address _nativeTokenWrapper) EnglishAuctionsLogic(_nativeTokenWrapper) {} - - function transferAuctionTokens(address _from, address _to, Auction memory _auction) external { - _transferAuctionTokens(_from, _to, _auction); - } -} - -contract TransferAuctionTokensTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - // Auction parameters - uint256 internal auctionId_erc1155; - uint256 internal auctionId_erc721; - uint256 internal bidAmount; - address internal winningBidder = address(0x123); - IEnglishAuctions.AuctionParameters internal auctionParams; - - // Events - event NewBid( - uint256 indexed auctionId, - address indexed bidder, - address indexed assetContract, - uint256 bidAmount, - IEnglishAuctions.Auction auction - ); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), platformFeeRecipient, uint16(platformFeeBps)) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - - // Sample auction parameters. - address assetContract = address(erc1155); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 100 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100 minutes; - uint64 endTimestamp = 200 minutes; - - // Auction tokens. - auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - // Set bidAmount - bidAmount = auctionParams.minimumBidAmount; - - // Mint NFT to seller. - erc721.mint(seller, 1); // to, amount - erc1155.mint(seller, 0, 100); // to, id, amount - - // Create auction - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - erc1155.setApprovalForAll(marketplace, true); - - auctionId_erc1155 = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - - auctionParams.assetContract = address(erc721); - auctionId_erc721 = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - - vm.stopPrank(); - - // Mint currency to bidder. - erc20.mint(buyer, 10_000 ether); - - vm.prank(buyer); - erc20.approve(marketplace, 100 ether); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `EnglishAuctions` - address englishAuctions = address(new MockTransferAuctionTokens(address(weth))); - vm.label(englishAuctions, "EnglishAuctions_Extension"); - - // Extension: EnglishAuctionsLogic - Extension memory extension_englishAuctions; - extension_englishAuctions.metadata = ExtensionMetadata({ - name: "EnglishAuctionsLogic", - metadataURI: "ipfs://EnglishAuctions", - implementation: englishAuctions - }); - - extension_englishAuctions.functions = new ExtensionFunction[](3); - extension_englishAuctions.functions[0] = ExtensionFunction( - MockTransferAuctionTokens.transferAuctionTokens.selector, - "transferAuctionTokens(address,address,(uint256,uint256,uint256,uint256,uint256,uint64,uint64,uint64,uint64,address,address,address,uint8,uint8))" - ); - extension_englishAuctions.functions[1] = ExtensionFunction( - EnglishAuctionsLogic.createAuction.selector, - "createAuction((address,uint256,uint256,address,uint256,uint256,uint64,uint64,uint64,uint64))" - ); - extension_englishAuctions.functions[2] = ExtensionFunction( - EnglishAuctionsLogic.getAuction.selector, - "getAuction(uint256)" - ); - - extensions[0] = extension_englishAuctions; - } - - function test_transferAuctionTokens_erc1155() public { - IEnglishAuctions.Auction memory auction = EnglishAuctionsLogic(marketplace).getAuction(auctionId_erc1155); - - assertEq(erc1155.balanceOf(address(marketplace), auction.tokenId), 1); - assertEq(erc1155.balanceOf(buyer, auction.tokenId), 0); - - MockTransferAuctionTokens(marketplace).transferAuctionTokens(address(marketplace), buyer, auction); - - assertEq(erc1155.balanceOf(address(marketplace), auction.tokenId), 0); - assertEq(erc1155.balanceOf(buyer, auction.tokenId), 1); - } - - function test_transferAuctionTokens_erc721() public { - IEnglishAuctions.Auction memory auction = EnglishAuctionsLogic(marketplace).getAuction(auctionId_erc721); - - assertEq(erc721.ownerOf(auction.tokenId), address(marketplace)); - - MockTransferAuctionTokens(marketplace).transferAuctionTokens(address(marketplace), buyer, auction); - - assertEq(erc721.ownerOf(auction.tokenId), buyer); - } -} diff --git a/src/test/marketplace/english-auctions/_transferAuctionTokens/_transferAuctionTokens.tree b/src/test/marketplace/english-auctions/_transferAuctionTokens/_transferAuctionTokens.tree deleted file mode 100644 index c44dc8c55..000000000 --- a/src/test/marketplace/english-auctions/_transferAuctionTokens/_transferAuctionTokens.tree +++ /dev/null @@ -1,9 +0,0 @@ -function _transferAuctionTokens( - address _from, - address _to, - Auction memory _auction -) -├── when the token is ERC1155 -│ └── it should transfer ERC1155 tokens from the specified owner to recipient -└── when the token is ERC721 - └── it should transfer ERC721 tokens from the specified owner to recipient \ No newline at end of file diff --git a/src/test/marketplace/english-auctions/_validateNewAuction/_validateNewAuction.t.sol b/src/test/marketplace/english-auctions/_validateNewAuction/_validateNewAuction.t.sol deleted file mode 100644 index 5f716875f..000000000 --- a/src/test/marketplace/english-auctions/_validateNewAuction/_validateNewAuction.t.sol +++ /dev/null @@ -1,304 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test helper imports -import "../../../utils/BaseTest.sol"; - -// Test contracts and interfaces -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { MarketplaceV3, IPlatformFee } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { EnglishAuctionsLogic } from "contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC721Base } from "contracts/base/ERC721Base.sol"; - -import { IEnglishAuctions } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -contract InvalidToken { - function supportsInterface(bytes4) public pure returns (bool) { - return false; - } -} - -contract ValidateNewAuctionTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - // Auction parameters - IEnglishAuctions.AuctionParameters internal auctionParams; - - // Events - /// @dev Emitted when a new auction is created. - event NewAuction( - address indexed auctionCreator, - uint256 indexed auctionId, - address indexed assetContract, - IEnglishAuctions.Auction auction - ); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Auction tokens. - auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - // Mint NFT to seller. - erc721.mint(seller, 1); // to, amount - erc1155.mint(seller, 0, 100); // to, id, amount - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `EnglishAuctions` - address englishAuctions = address(new EnglishAuctionsLogic(address(weth))); - vm.label(englishAuctions, "EnglishAuctions_Extension"); - - // Extension: EnglishAuctionsLogic - Extension memory extension_englishAuctions; - extension_englishAuctions.metadata = ExtensionMetadata({ - name: "EnglishAuctionsLogic", - metadataURI: "ipfs://EnglishAuctions", - implementation: englishAuctions - }); - - extension_englishAuctions.functions = new ExtensionFunction[](12); - extension_englishAuctions.functions[0] = ExtensionFunction( - EnglishAuctionsLogic.totalAuctions.selector, - "totalAuctions()" - ); - extension_englishAuctions.functions[1] = ExtensionFunction( - EnglishAuctionsLogic.createAuction.selector, - "createAuction((address,uint256,uint256,address,uint256,uint256,uint64,uint64,uint64,uint64))" - ); - extension_englishAuctions.functions[2] = ExtensionFunction( - EnglishAuctionsLogic.cancelAuction.selector, - "cancelAuction(uint256)" - ); - extension_englishAuctions.functions[3] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionPayout.selector, - "collectAuctionPayout(uint256)" - ); - extension_englishAuctions.functions[4] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionTokens.selector, - "collectAuctionTokens(uint256)" - ); - extension_englishAuctions.functions[5] = ExtensionFunction( - EnglishAuctionsLogic.bidInAuction.selector, - "bidInAuction(uint256,uint256)" - ); - extension_englishAuctions.functions[6] = ExtensionFunction( - EnglishAuctionsLogic.isNewWinningBid.selector, - "isNewWinningBid(uint256,uint256)" - ); - extension_englishAuctions.functions[7] = ExtensionFunction( - EnglishAuctionsLogic.getAuction.selector, - "getAuction(uint256)" - ); - extension_englishAuctions.functions[8] = ExtensionFunction( - EnglishAuctionsLogic.getAllAuctions.selector, - "getAllAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[9] = ExtensionFunction( - EnglishAuctionsLogic.getAllValidAuctions.selector, - "getAllValidAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[10] = ExtensionFunction( - EnglishAuctionsLogic.getWinningBid.selector, - "getWinningBid(uint256)" - ); - extension_englishAuctions.functions[11] = ExtensionFunction( - EnglishAuctionsLogic.isAuctionExpired.selector, - "isAuctionExpired(uint256)" - ); - - extensions[0] = extension_englishAuctions; - } - - function test_validateNewAuction_whenQuantityIsZero() public { - auctionParams.quantity = 0; - - vm.prank(seller); - vm.expectRevert("Marketplace: auctioning zero quantity."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - modifier whenNonZeroQuantity() { - auctionParams.quantity = 1; - _; - } - - function test_validateNewAuction_whenQuantityGtOneAndAssetERC721() public whenNonZeroQuantity { - auctionParams.quantity = 2; - auctionParams.assetContract = address(erc721); - - vm.prank(seller); - vm.expectRevert("Marketplace: auctioning invalid quantity."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - modifier whenQtyOneOrAssetERC1155() { - auctionParams.quantity = 1; - auctionParams.assetContract = address(erc721); - _; - } - - function test_validateNewAuction_whenTimeBufferIsZero() public whenNonZeroQuantity whenQtyOneOrAssetERC1155 { - auctionParams.timeBufferInSeconds = 0; - - vm.prank(seller); - vm.expectRevert("Marketplace: no time-buffer."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - modifier whenNonZeroTimeBuffer() { - _; - } - - function test_validateNewAuction_whenBidBufferIsZero() - public - whenNonZeroQuantity - whenQtyOneOrAssetERC1155 - whenNonZeroTimeBuffer - { - auctionParams.bidBufferBps = 0; - - vm.prank(seller); - vm.expectRevert("Marketplace: no bid-buffer."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - modifier whenNonZeroBidBuffer() { - _; - } - - function test_validateNewAuction_whenInvalidTimestamps() - public - whenNonZeroQuantity - whenQtyOneOrAssetERC1155 - whenNonZeroTimeBuffer - whenNonZeroBidBuffer - { - vm.warp(auctionParams.startTimestamp + 61 minutes); - - vm.prank(seller); - vm.expectRevert("Marketplace: invalid timestamps."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - - vm.warp(auctionParams.startTimestamp); - - auctionParams.endTimestamp = auctionParams.startTimestamp - 1; - - vm.prank(seller); - vm.expectRevert("Marketplace: invalid timestamps."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - modifier whenValidTimestamps() { - _; - } - - function test_validateNewAuction_whenBuyoutLtMinimumBidAmt() - public - whenNonZeroQuantity - whenQtyOneOrAssetERC1155 - whenNonZeroTimeBuffer - whenNonZeroBidBuffer - whenValidTimestamps - { - auctionParams.buyoutBidAmount = auctionParams.minimumBidAmount - 1; - - vm.prank(seller); - vm.expectRevert("Marketplace: invalid bid amounts."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - modifier whenBuyoutGteMinimumBidAmt() { - _; - } - - function test_validateNewAuction_buyoutGteMinimumBidAmt() - public - whenNonZeroQuantity - whenQtyOneOrAssetERC1155 - whenNonZeroTimeBuffer - whenNonZeroBidBuffer - whenValidTimestamps - whenBuyoutGteMinimumBidAmt - { - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - vm.prank(seller); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - - assertEq(EnglishAuctionsLogic(marketplace).totalAuctions(), 1); - } -} diff --git a/src/test/marketplace/english-auctions/_validateNewAuction/_validateNewAuction.tree b/src/test/marketplace/english-auctions/_validateNewAuction/_validateNewAuction.tree deleted file mode 100644 index 24429d6c5..000000000 --- a/src/test/marketplace/english-auctions/_validateNewAuction/_validateNewAuction.tree +++ /dev/null @@ -1,20 +0,0 @@ -function _validateNewAuction(AuctionParameters memory _params, TokenType _tokenType) internal view -. -├── when quantity is zero -│ └── it should revert ✅ -└── when the quantity is non zero - ├── when the quantity is greater than one and token type is ERC721 - │ └── it should revert ✅ - └── when the quantity is one or token type is ERC1155 - ├── when the time buffer is zero - │ └── it should revert ✅ - └── when the time buffer is non zero - ├── when the bid buffer is zero - │ └── it should revert ✅ - └── when the bid buffer is non zero - ├── when start and end timestamps are invalid - │ └── it should revert ✅ - └── when start and end timestamps are valid - ├── when buyout amount is less than minimum bid amount - │ └── it should revert ✅ - └── when buyout amount is zero or gte minimum bid amount ✅ \ No newline at end of file diff --git a/src/test/marketplace/english-auctions/bidInAuction/bidInAuction.t.sol b/src/test/marketplace/english-auctions/bidInAuction/bidInAuction.t.sol deleted file mode 100644 index ad7041106..000000000 --- a/src/test/marketplace/english-auctions/bidInAuction/bidInAuction.t.sol +++ /dev/null @@ -1,683 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test helper imports -import "../../../utils/BaseTest.sol"; - -// Test contracts and interfaces -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { MarketplaceV3, IPlatformFee } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { EnglishAuctionsLogic } from "contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC721Base } from "contracts/base/ERC721Base.sol"; - -import { IEnglishAuctions } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -contract ReentrantRecipient is ERC1155Holder { - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes memory data - ) public virtual override returns (bytes4) { - uint256 auctionId = 0; - uint256 bidAmount = 10 ether; - EnglishAuctionsLogic(msg.sender).bidInAuction(auctionId, bidAmount); - return super.onERC1155Received(operator, from, id, value, data); - } -} - -contract BidInAuctionTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - // Auction parameters - uint256 internal auctionId; - uint256 internal bidAmount; - address internal winningBidder = address(0x123); - IEnglishAuctions.AuctionParameters internal auctionParams; - - // Events - event NewBid( - uint256 indexed auctionId, - address indexed bidder, - address indexed assetContract, - uint256 bidAmount, - IEnglishAuctions.Auction auction - ); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - - // Sample auction parameters. - address assetContract = address(erc1155); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100 minutes; - uint64 endTimestamp = 200 minutes; - - // Auction tokens. - auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - // Set bidAmount - bidAmount = auctionParams.minimumBidAmount; - - // Mint NFT to seller. - erc721.mint(seller, 1); // to, amount - erc1155.mint(seller, 0, 100); // to, id, amount - - // Create auction - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - erc1155.setApprovalForAll(marketplace, true); - auctionId = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - vm.stopPrank(); - - // Mint currency to bidder. - erc20.mint(buyer, 10_000 ether); - - vm.prank(buyer); - erc20.approve(marketplace, 100 ether); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `EnglishAuctions` - address englishAuctions = address(new EnglishAuctionsLogic(address(weth))); - vm.label(englishAuctions, "EnglishAuctions_Extension"); - - // Extension: EnglishAuctionsLogic - Extension memory extension_englishAuctions; - extension_englishAuctions.metadata = ExtensionMetadata({ - name: "EnglishAuctionsLogic", - metadataURI: "ipfs://EnglishAuctions", - implementation: englishAuctions - }); - - extension_englishAuctions.functions = new ExtensionFunction[](12); - extension_englishAuctions.functions[0] = ExtensionFunction( - EnglishAuctionsLogic.totalAuctions.selector, - "totalAuctions()" - ); - extension_englishAuctions.functions[1] = ExtensionFunction( - EnglishAuctionsLogic.createAuction.selector, - "createAuction((address,uint256,uint256,address,uint256,uint256,uint64,uint64,uint64,uint64))" - ); - extension_englishAuctions.functions[2] = ExtensionFunction( - EnglishAuctionsLogic.cancelAuction.selector, - "cancelAuction(uint256)" - ); - extension_englishAuctions.functions[3] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionPayout.selector, - "collectAuctionPayout(uint256)" - ); - extension_englishAuctions.functions[4] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionTokens.selector, - "collectAuctionTokens(uint256)" - ); - extension_englishAuctions.functions[5] = ExtensionFunction( - EnglishAuctionsLogic.bidInAuction.selector, - "bidInAuction(uint256,uint256)" - ); - extension_englishAuctions.functions[6] = ExtensionFunction( - EnglishAuctionsLogic.isNewWinningBid.selector, - "isNewWinningBid(uint256,uint256)" - ); - extension_englishAuctions.functions[7] = ExtensionFunction( - EnglishAuctionsLogic.getAuction.selector, - "getAuction(uint256)" - ); - extension_englishAuctions.functions[8] = ExtensionFunction( - EnglishAuctionsLogic.getAllAuctions.selector, - "getAllAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[9] = ExtensionFunction( - EnglishAuctionsLogic.getAllValidAuctions.selector, - "getAllValidAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[10] = ExtensionFunction( - EnglishAuctionsLogic.getWinningBid.selector, - "getWinningBid(uint256)" - ); - extension_englishAuctions.functions[11] = ExtensionFunction( - EnglishAuctionsLogic.isAuctionExpired.selector, - "isAuctionExpired(uint256)" - ); - - extensions[0] = extension_englishAuctions; - } - - function test_bidInAuction_callIsReentrant() public { - vm.warp(auctionParams.startTimestamp + 1); - address reentrantRecipient = address(new ReentrantRecipient()); - - erc20.mint(reentrantRecipient, 100 ether); - - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), reentrantRecipient); - - vm.startPrank(reentrantRecipient); - erc20.approve(marketplace, 100 ether); - vm.expectRevert(); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, auctionParams.buyoutBidAmount); - vm.stopPrank(); - } - - modifier whenCallIsNotReentrant() { - _; - } - - function test_bidInAuction_whenAuctionDoesNotExist() public whenCallIsNotReentrant { - vm.prank(buyer); - vm.expectRevert("Marketplace: invalid auction."); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId + 1, bidAmount); - } - - modifier whenAuctionExists() { - _; - } - - function test_bidInAuction_whenAuctionIsNotActive() public whenCallIsNotReentrant whenAuctionExists { - vm.prank(buyer); - vm.expectRevert("Marketplace: inactive auction."); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, bidAmount); - } - - modifier whenAuctionIsActive() { - vm.warp(auctionParams.startTimestamp + 1); - _; - } - - function test_bidInAuction_whenBidAmountIsZero() - public - whenCallIsNotReentrant - whenAuctionExists - whenAuctionIsActive - { - vm.prank(buyer); - vm.expectRevert("Marketplace: Bidding with zero amount."); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, 0); - } - - modifier whenBidAmountIsNotZero() { - bidAmount = auctionParams.minimumBidAmount; - _; - } - - function test_bidInAuction_whenAuctionCurrencyIsERC20AndMsgValueSent() - public - whenCallIsNotReentrant - whenAuctionExists - whenAuctionIsActive - whenBidAmountIsNotZero - { - vm.deal(buyer, 1 ether); - - vm.prank(buyer); - vm.expectRevert("Marketplace: invalid native tokens sent."); - EnglishAuctionsLogic(marketplace).bidInAuction{ value: 1 }(auctionId, auctionParams.buyoutBidAmount); - } - - function test_bidInAuction_whenBidAmountIsGtBuyoutPrice() - public - whenCallIsNotReentrant - whenAuctionExists - whenAuctionIsActive - whenBidAmountIsNotZero - { - vm.prank(buyer); - vm.expectRevert("Marketplace: Bidding above buyout price."); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, auctionParams.buyoutBidAmount + 1); - } - - modifier whenBidAmountLtBuyoutPrice() { - bidAmount = auctionParams.buyoutBidAmount - 1; - _; - } - - modifier whenBidAmountEqBuyoutPrice() { - bidAmount = auctionParams.buyoutBidAmount; - _; - } - - modifier whenCurrentWinningBid() { - // Existing winning bid. - erc20.mint(winningBidder, 100 ether); - - vm.prank(winningBidder); - erc20.approve(marketplace, 100 ether); - - vm.prank(winningBidder); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, auctionParams.minimumBidAmount + 1); - _; - } - - modifier whenNoCurrentWinningBid() { - _; - } - - function test_bidInAuction_buyoutAndExistingWinningBid() - public - whenCallIsNotReentrant - whenAuctionExists - whenAuctionIsActive - whenBidAmountIsNotZero - whenBidAmountEqBuyoutPrice - whenCurrentWinningBid - { - uint256 winningBidderBal = erc20.balanceOf(winningBidder); - - assertEq(erc20.balanceOf(marketplace), auctionParams.minimumBidAmount + 1); - - assertEq(erc1155.balanceOf(marketplace, auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(seller, auctionParams.tokenId), 99); - - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, bidAmount); - - assertEq(erc20.balanceOf(winningBidder), winningBidderBal + auctionParams.minimumBidAmount + 1); - assertEq(erc20.balanceOf(marketplace), bidAmount); - - assertEq(erc1155.balanceOf(marketplace, auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(seller, auctionParams.tokenId), 99); - - // Auction is marked CLOSED in auction state when creator collected payout - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - } - - function test_bidInAuction_buyoutAndNoWinningBid() - public - whenCallIsNotReentrant - whenAuctionExists - whenAuctionIsActive - whenBidAmountIsNotZero - whenBidAmountEqBuyoutPrice - whenNoCurrentWinningBid - { - assertEq(erc20.balanceOf(marketplace), 0); - - assertEq(erc1155.balanceOf(marketplace, auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(seller, auctionParams.tokenId), 99); - - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, bidAmount); - - assertEq(erc20.balanceOf(marketplace), bidAmount); - - assertEq(erc1155.balanceOf(marketplace, auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(seller, auctionParams.tokenId), 99); - - // Auction is marked CLOSED in auction state when creator collected payout - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - } - - function test_bidInAuction_whenBidIsNotNewWinningBid() - public - whenCallIsNotReentrant - whenAuctionExists - whenAuctionIsActive - whenCurrentWinningBid - whenBidAmountIsNotZero - whenBidAmountLtBuyoutPrice - { - assertEq(EnglishAuctionsLogic(marketplace).isNewWinningBid(auctionId, auctionParams.minimumBidAmount), false); - - vm.prank(buyer); - vm.expectRevert("Marketplace: not winning bid."); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, auctionParams.minimumBidAmount); - } - - modifier whenBidIsNewWinningBig() { - bidAmount = auctionParams.buyoutBidAmount - 1; - assertEq(EnglishAuctionsLogic(marketplace).isNewWinningBid(auctionId, bidAmount), true); - _; - } - - modifier whenBidWithinTimeBuffer() { - vm.warp(auctionParams.endTimestamp - auctionParams.timeBufferInSeconds); - _; - } - - function test_bidInAuction_noBuyoutAndExistingWinningBid() - public - whenCallIsNotReentrant - whenAuctionExists - whenAuctionIsActive - whenBidAmountIsNotZero - whenBidAmountLtBuyoutPrice - whenBidIsNewWinningBig - whenCurrentWinningBid - { - uint256 winningBidderBal = erc20.balanceOf(winningBidder); - - assertEq(erc20.balanceOf(marketplace), auctionParams.minimumBidAmount + 1); - - assertEq(erc1155.balanceOf(marketplace, auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(seller, auctionParams.tokenId), 99); - - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - - (address bidderBefore, address currencyBefore, uint256 bidAmountBefore) = EnglishAuctionsLogic(marketplace) - .getWinningBid(auctionId); - - assertEq(bidderBefore, winningBidder); - assertEq(currencyBefore, address(erc20)); - assertEq(bidAmountBefore, auctionParams.minimumBidAmount + 1); - - assertEq(EnglishAuctionsLogic(marketplace).isNewWinningBid(auctionId, bidAmount), true); - - vm.startPrank(buyer); - vm.expectEmit(true, true, true, false); - emit NewBid( - auctionId, - buyer, - address(erc1155), - bidAmount, - EnglishAuctionsLogic(marketplace).getAuction(auctionId) - ); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, bidAmount); - vm.stopPrank(); - - (address bidderAfter, address currencyAfter, uint256 bidAmountAfter) = EnglishAuctionsLogic(marketplace) - .getWinningBid(auctionId); - - assertEq(bidderAfter, buyer); - assertEq(currencyAfter, address(erc20)); - assertEq(bidAmountAfter, bidAmount); - - assertEq(erc20.balanceOf(winningBidder), winningBidderBal + auctionParams.minimumBidAmount + 1); - assertEq(erc20.balanceOf(marketplace), bidAmount); - - assertEq(erc1155.balanceOf(marketplace, auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(seller, auctionParams.tokenId), 99); - - // Auction is marked CLOSED in auction state when creator collected payout - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - } - - function test_bidInAuction_noBuyoutAndNoWinningBid() - public - whenCallIsNotReentrant - whenAuctionExists - whenAuctionIsActive - whenBidAmountIsNotZero - whenBidAmountLtBuyoutPrice - whenBidIsNewWinningBig - whenNoCurrentWinningBid - { - assertEq(erc20.balanceOf(marketplace), 0); - - assertEq(erc1155.balanceOf(marketplace, auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(seller, auctionParams.tokenId), 99); - - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - - (address bidderBefore, address currencyBefore, uint256 bidAmountBefore) = EnglishAuctionsLogic(marketplace) - .getWinningBid(auctionId); - - assertEq(bidderBefore, address(0)); - assertEq(currencyBefore, address(erc20)); - assertEq(bidAmountBefore, 0); - - vm.startPrank(buyer); - vm.expectEmit(true, true, true, false); - emit NewBid( - auctionId, - buyer, - address(erc1155), - bidAmount, - EnglishAuctionsLogic(marketplace).getAuction(auctionId) - ); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, bidAmount); - vm.stopPrank(); - - (address bidderAfter, address currencyAfter, uint256 bidAmountAfter) = EnglishAuctionsLogic(marketplace) - .getWinningBid(auctionId); - - assertEq(bidderAfter, buyer); - assertEq(currencyAfter, address(erc20)); - assertEq(bidAmountAfter, bidAmount); - - assertEq(erc20.balanceOf(marketplace), bidAmount); - - assertEq(erc1155.balanceOf(marketplace, auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(seller, auctionParams.tokenId), 99); - - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - } - - function test_bidInAuction_noBuyoutAndExistingWinningBid_withinTimeBuffer() - public - whenCallIsNotReentrant - whenAuctionExists - whenAuctionIsActive - whenBidAmountIsNotZero - whenBidAmountLtBuyoutPrice - whenBidIsNewWinningBig - whenCurrentWinningBid - whenBidWithinTimeBuffer - { - uint256 winningBidderBal = erc20.balanceOf(winningBidder); - - assertEq(erc20.balanceOf(marketplace), auctionParams.minimumBidAmount + 1); - - assertEq(erc1155.balanceOf(marketplace, auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(seller, auctionParams.tokenId), 99); - - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - - (address bidderBefore, address currencyBefore, uint256 bidAmountBefore) = EnglishAuctionsLogic(marketplace) - .getWinningBid(auctionId); - - assertEq(bidderBefore, winningBidder); - assertEq(currencyBefore, address(erc20)); - assertEq(bidAmountBefore, auctionParams.minimumBidAmount + 1); - - assertEq(EnglishAuctionsLogic(marketplace).getAuction(auctionId).endTimestamp, auctionParams.endTimestamp); - - vm.startPrank(buyer); - vm.expectEmit(true, true, true, false); - emit NewBid( - auctionId, - buyer, - address(erc1155), - bidAmount, - EnglishAuctionsLogic(marketplace).getAuction(auctionId) - ); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, bidAmount); - vm.stopPrank(); - - assertEq( - EnglishAuctionsLogic(marketplace).getAuction(auctionId).endTimestamp, - auctionParams.endTimestamp + auctionParams.timeBufferInSeconds - ); - - (address bidderAfter, address currencyAfter, uint256 bidAmountAfter) = EnglishAuctionsLogic(marketplace) - .getWinningBid(auctionId); - - assertEq(bidderAfter, buyer); - assertEq(currencyAfter, address(erc20)); - assertEq(bidAmountAfter, bidAmount); - - assertEq(erc20.balanceOf(winningBidder), winningBidderBal + auctionParams.minimumBidAmount + 1); - assertEq(erc20.balanceOf(marketplace), bidAmount); - - assertEq(erc1155.balanceOf(marketplace, auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(seller, auctionParams.tokenId), 99); - - // Auction is marked CLOSED in auction state when creator collected payout - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - } - - function test_bidInAuction_noBuyoutAndNoWinningBid_withinTimeBuffer() - public - whenCallIsNotReentrant - whenAuctionExists - whenAuctionIsActive - whenBidAmountIsNotZero - whenBidAmountLtBuyoutPrice - whenBidIsNewWinningBig - whenBidWithinTimeBuffer - whenNoCurrentWinningBid - { - assertEq(erc20.balanceOf(marketplace), 0); - - assertEq(erc1155.balanceOf(marketplace, auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(seller, auctionParams.tokenId), 99); - - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - - (address bidderBefore, address currencyBefore, uint256 bidAmountBefore) = EnglishAuctionsLogic(marketplace) - .getWinningBid(auctionId); - - assertEq(bidderBefore, address(0)); - assertEq(currencyBefore, address(erc20)); - assertEq(bidAmountBefore, 0); - - assertEq(EnglishAuctionsLogic(marketplace).getAuction(auctionId).endTimestamp, auctionParams.endTimestamp); - - vm.startPrank(buyer); - vm.expectEmit(true, true, true, false); - emit NewBid( - auctionId, - buyer, - address(erc1155), - bidAmount, - EnglishAuctionsLogic(marketplace).getAuction(auctionId) - ); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, bidAmount); - vm.stopPrank(); - - assertEq( - EnglishAuctionsLogic(marketplace).getAuction(auctionId).endTimestamp, - auctionParams.endTimestamp + auctionParams.timeBufferInSeconds - ); - - (address bidderAfter, address currencyAfter, uint256 bidAmountAfter) = EnglishAuctionsLogic(marketplace) - .getWinningBid(auctionId); - - assertEq(bidderAfter, buyer); - assertEq(currencyAfter, address(erc20)); - assertEq(bidAmountAfter, bidAmount); - - assertEq(erc20.balanceOf(marketplace), bidAmount); - - assertEq(erc1155.balanceOf(marketplace, auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(seller, auctionParams.tokenId), 99); - - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - } -} diff --git a/src/test/marketplace/english-auctions/bidInAuction/bidInAuction.tree b/src/test/marketplace/english-auctions/bidInAuction/bidInAuction.tree deleted file mode 100644 index bcb182035..000000000 --- a/src/test/marketplace/english-auctions/bidInAuction/bidInAuction.tree +++ /dev/null @@ -1,52 +0,0 @@ -function bidInAuction(uint256 _auctionId, uint256 _bidAmount) -├── when the call is reentrant -│ └── it should revert ✅ -└── when the call is not reentrant - ├── when the auction does not exist - │ └── it should revert ✅ - └── when the auction exists - ├── when the auction is not active - │ └── it should revert ✅ - └── when the auction is active - ├── when the bid amount is zero - │ └── it should revert ✅ - └── when the bid amount is not zero - ├── when the bid amount is greater than buyout price - │ └── it should revert ✅ - └── when the bid amount is less than or equal to buyout price - ├── when the bid amount is equal to buyout price - │ ├── when there is a current winning bid ✅ - │ │ ├── it should transfer previous winning bid back to previous winning bidder - │ │ ├── it should transfer auctioned tokens to bidder - │ │ ├── it should escrow incoming bid - │ │ └── it should emit a NewBid event - │ └── when there is no current winning bid ✅ - │ ├── it should transfer auctioned tokens to bidder - │ ├── it should escrow incoming bid - │ └── it should emit a NewBid event - └── when the bid amount is less than buyout price - ├── when the bid is not a new winning bid - │ └── it should revert ✅ - └── when the bid is a new winning bid - ├── when the remaining auction duration is less than time buffer - │ ├── when there is a current winning bid ✅ - │ │ ├── it should add time buffer to auction duration - │ │ ├── it should transfer previous winning bid back to previous winning bidder - │ │ ├── it should escrow incoming bid - │ │ └── it should emit a NewBid event - │ │ └── it set auction status as completed - │ └── when there is no current winning bid ✅ - │ ├── it should add time buffer to auction duration - │ ├── it should escrow incoming bid - │ └── it should emit a NewBid event - │ └── it set auction status as completed - └── when the remaining auction duration is not less than time buffer - ├── when there is a current winning bid ✅ - │ ├── it should transfer previous winning bid back to previous winning bidder - │ ├── it should escrow incoming bid - │ └── it should emit a NewBid event - │ └── it set auction status as completed - └── when there is no current winning bid ✅ - ├── it should escrow incoming bid - └── it should emit a NewBid event - └── it set auction status as completed \ No newline at end of file diff --git a/src/test/marketplace/english-auctions/cancelAuction/cancelAuction.t.sol b/src/test/marketplace/english-auctions/cancelAuction/cancelAuction.t.sol deleted file mode 100644 index aa69aacb2..000000000 --- a/src/test/marketplace/english-auctions/cancelAuction/cancelAuction.t.sol +++ /dev/null @@ -1,261 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test helper imports -import "../../../utils/BaseTest.sol"; - -// Test contracts and interfaces -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { MarketplaceV3, IPlatformFee } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { EnglishAuctionsLogic } from "contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC721Base } from "contracts/base/ERC721Base.sol"; - -import { IEnglishAuctions } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -contract ReentrantRecipient is ERC1155Holder { - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes memory data - ) public virtual override returns (bytes4) { - uint256 auctionId = 0; - uint256 bidAmount = 10 ether; - EnglishAuctionsLogic(msg.sender).bidInAuction(auctionId, bidAmount); - return super.onERC1155Received(operator, from, id, value, data); - } -} - -contract CancelAuctionTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - // Auction parameters - uint256 internal auctionId; - uint256 internal bidAmount; - address internal winningBidder = address(0x123); - IEnglishAuctions.AuctionParameters internal auctionParams; - - // Events - event CancelledAuction(address indexed auctionCreator, uint256 indexed auctionId); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - - // Sample auction parameters. - address assetContract = address(erc1155); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100 minutes; - uint64 endTimestamp = 200 minutes; - - // Auction tokens. - auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - // Set bidAmount - bidAmount = auctionParams.minimumBidAmount; - - // Mint NFT to seller. - erc721.mint(seller, 1); // to, amount - erc1155.mint(seller, 0, 100); // to, id, amount - - // Create auction - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - erc1155.setApprovalForAll(marketplace, true); - auctionId = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - vm.stopPrank(); - - // Mint currency to bidder. - erc20.mint(buyer, 10_000 ether); - - vm.prank(buyer); - erc20.approve(marketplace, 100 ether); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `EnglishAuctions` - address englishAuctions = address(new EnglishAuctionsLogic(address(weth))); - vm.label(englishAuctions, "EnglishAuctions_Extension"); - - // Extension: EnglishAuctionsLogic - Extension memory extension_englishAuctions; - extension_englishAuctions.metadata = ExtensionMetadata({ - name: "EnglishAuctionsLogic", - metadataURI: "ipfs://EnglishAuctions", - implementation: englishAuctions - }); - - extension_englishAuctions.functions = new ExtensionFunction[](12); - extension_englishAuctions.functions[0] = ExtensionFunction( - EnglishAuctionsLogic.totalAuctions.selector, - "totalAuctions()" - ); - extension_englishAuctions.functions[1] = ExtensionFunction( - EnglishAuctionsLogic.createAuction.selector, - "createAuction((address,uint256,uint256,address,uint256,uint256,uint64,uint64,uint64,uint64))" - ); - extension_englishAuctions.functions[2] = ExtensionFunction( - EnglishAuctionsLogic.cancelAuction.selector, - "cancelAuction(uint256)" - ); - extension_englishAuctions.functions[3] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionPayout.selector, - "collectAuctionPayout(uint256)" - ); - extension_englishAuctions.functions[4] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionTokens.selector, - "collectAuctionTokens(uint256)" - ); - extension_englishAuctions.functions[5] = ExtensionFunction( - EnglishAuctionsLogic.bidInAuction.selector, - "bidInAuction(uint256,uint256)" - ); - extension_englishAuctions.functions[6] = ExtensionFunction( - EnglishAuctionsLogic.isNewWinningBid.selector, - "isNewWinningBid(uint256,uint256)" - ); - extension_englishAuctions.functions[7] = ExtensionFunction( - EnglishAuctionsLogic.getAuction.selector, - "getAuction(uint256)" - ); - extension_englishAuctions.functions[8] = ExtensionFunction( - EnglishAuctionsLogic.getAllAuctions.selector, - "getAllAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[9] = ExtensionFunction( - EnglishAuctionsLogic.getAllValidAuctions.selector, - "getAllValidAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[10] = ExtensionFunction( - EnglishAuctionsLogic.getWinningBid.selector, - "getWinningBid(uint256)" - ); - extension_englishAuctions.functions[11] = ExtensionFunction( - EnglishAuctionsLogic.isAuctionExpired.selector, - "isAuctionExpired(uint256)" - ); - - extensions[0] = extension_englishAuctions; - } - - function test_cancelAuction_whenAuctionDoesntExist() public { - vm.prank(seller); - vm.expectRevert("Marketplace: invalid auction."); - EnglishAuctionsLogic(marketplace).cancelAuction(auctionId + 100); - } - - modifier whenAuctionExists() { - _; - } - - function test_cancelAuction_whenCallerNotCreator() public whenAuctionExists { - vm.prank(buyer); - vm.expectRevert("Marketplace: not auction creator."); - EnglishAuctionsLogic(marketplace).cancelAuction(auctionId); - } - - modifier whenCallerIsCreator() { - _; - } - - function test_cancelAuction_whenWinningBid() public whenAuctionExists whenCallerIsCreator { - vm.warp(auctionParams.startTimestamp + 1); - - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, bidAmount); - - vm.prank(seller); - vm.expectRevert("Marketplace: bids already made."); - EnglishAuctionsLogic(marketplace).cancelAuction(auctionId); - } - - modifier whenNoWinningBid() { - _; - } - - function test_cancelAuction_whenNoWinningBid() public whenAuctionExists whenCallerIsCreator whenNoWinningBid { - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - assertEq(erc1155.balanceOf(address(marketplace), 0), 1); - assertEq(erc1155.balanceOf(seller, 0), 99); - - vm.prank(seller); - vm.expectEmit(true, true, true, true); - emit CancelledAuction(seller, auctionId); - EnglishAuctionsLogic(marketplace).cancelAuction(auctionId); - - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CANCELLED) - ); - - assertEq(erc1155.balanceOf(address(marketplace), 0), 0); - assertEq(erc1155.balanceOf(seller, 0), 100); - } -} diff --git a/src/test/marketplace/english-auctions/cancelAuction/cancelAuction.tree b/src/test/marketplace/english-auctions/cancelAuction/cancelAuction.tree deleted file mode 100644 index 397d6bc6d..000000000 --- a/src/test/marketplace/english-auctions/cancelAuction/cancelAuction.tree +++ /dev/null @@ -1,14 +0,0 @@ -function cancelAuction(uint256 _auctionId) external -. -├── when auction does not exist -│ └── it should revert ✅ -└── when auction exists - ├── when the caller is not auction creator - │ └── it should revert ✅ - └── when the caller is auction creator - ├── when there is a winning bidder - │ └── it should revert ✅ - └── when there is no winning bidder ✅ - ├── it should set auction status as cancelled - ├── it should transfer auction tokens back to creator - └── it should emit CancelledAuction event diff --git a/src/test/marketplace/english-auctions/collectAuctionPayout/collectAuctionPayout.t.sol b/src/test/marketplace/english-auctions/collectAuctionPayout/collectAuctionPayout.t.sol deleted file mode 100644 index b33673657..000000000 --- a/src/test/marketplace/english-auctions/collectAuctionPayout/collectAuctionPayout.t.sol +++ /dev/null @@ -1,304 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test helper imports -import "../../../utils/BaseTest.sol"; - -// Test contracts and interfaces -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { MarketplaceV3, IPlatformFee } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { EnglishAuctionsLogic } from "contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC721Base } from "contracts/base/ERC721Base.sol"; - -import { IEnglishAuctions } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -contract ReentrantRecipient is ERC1155Holder { - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes memory data - ) public virtual override returns (bytes4) { - uint256 auctionId = 0; - uint256 bidAmount = 10 ether; - EnglishAuctionsLogic(msg.sender).bidInAuction(auctionId, bidAmount); - return super.onERC1155Received(operator, from, id, value, data); - } -} - -contract CollectAuctionPayoutTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - address private defaultFeeRecipient; - - // Auction parameters - uint256 internal auctionId; - address internal winningBidder = address(0x123); - IEnglishAuctions.AuctionParameters internal auctionParams; - - // Events - event AuctionClosed( - uint256 indexed auctionId, - address indexed assetContract, - address indexed closer, - uint256 tokenId, - address auctionCreator, - address winningBidder - ); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - - // Sample auction parameters. - address assetContract = address(erc1155); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100 minutes; - uint64 endTimestamp = 200 minutes; - - // Auction tokens. - auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - // Mint NFT to seller. - erc721.mint(seller, 1); // to, amount - erc1155.mint(seller, 0, 100); // to, id, amount - - // Create auction - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - erc1155.setApprovalForAll(marketplace, true); - auctionId = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - vm.stopPrank(); - - // Mint currency to bidder. - erc20.mint(buyer, 10_000 ether); - - vm.prank(buyer); - erc20.approve(marketplace, 100 ether); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `EnglishAuctions` - address englishAuctions = address(new EnglishAuctionsLogic(address(weth))); - defaultFeeRecipient = EnglishAuctionsLogic(englishAuctions).DEFAULT_FEE_RECIPIENT(); - vm.label(englishAuctions, "EnglishAuctions_Extension"); - - // Extension: EnglishAuctionsLogic - Extension memory extension_englishAuctions; - extension_englishAuctions.metadata = ExtensionMetadata({ - name: "EnglishAuctionsLogic", - metadataURI: "ipfs://EnglishAuctions", - implementation: englishAuctions - }); - - extension_englishAuctions.functions = new ExtensionFunction[](12); - extension_englishAuctions.functions[0] = ExtensionFunction( - EnglishAuctionsLogic.totalAuctions.selector, - "totalAuctions()" - ); - extension_englishAuctions.functions[1] = ExtensionFunction( - EnglishAuctionsLogic.createAuction.selector, - "createAuction((address,uint256,uint256,address,uint256,uint256,uint64,uint64,uint64,uint64))" - ); - extension_englishAuctions.functions[2] = ExtensionFunction( - EnglishAuctionsLogic.cancelAuction.selector, - "cancelAuction(uint256)" - ); - extension_englishAuctions.functions[3] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionPayout.selector, - "collectAuctionPayout(uint256)" - ); - extension_englishAuctions.functions[4] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionTokens.selector, - "collectAuctionTokens(uint256)" - ); - extension_englishAuctions.functions[5] = ExtensionFunction( - EnglishAuctionsLogic.bidInAuction.selector, - "bidInAuction(uint256,uint256)" - ); - extension_englishAuctions.functions[6] = ExtensionFunction( - EnglishAuctionsLogic.isNewWinningBid.selector, - "isNewWinningBid(uint256,uint256)" - ); - extension_englishAuctions.functions[7] = ExtensionFunction( - EnglishAuctionsLogic.getAuction.selector, - "getAuction(uint256)" - ); - extension_englishAuctions.functions[8] = ExtensionFunction( - EnglishAuctionsLogic.getAllAuctions.selector, - "getAllAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[9] = ExtensionFunction( - EnglishAuctionsLogic.getAllValidAuctions.selector, - "getAllValidAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[10] = ExtensionFunction( - EnglishAuctionsLogic.getWinningBid.selector, - "getWinningBid(uint256)" - ); - extension_englishAuctions.functions[11] = ExtensionFunction( - EnglishAuctionsLogic.isAuctionExpired.selector, - "isAuctionExpired(uint256)" - ); - - extensions[0] = extension_englishAuctions; - } - - function test_collectAuctionPayout_whenAuctionIsCancelled() public { - vm.prank(seller); - EnglishAuctionsLogic(marketplace).cancelAuction(auctionId); - - vm.prank(seller); - vm.expectRevert("Marketplace: invalid auction."); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - } - - modifier whenAuctionNotCancelled() { - _; - } - - function test_collectAuctionPayout_whenAuctionIsActive() public whenAuctionNotCancelled { - vm.warp(auctionParams.startTimestamp + 1); - - vm.prank(seller); - vm.expectRevert("Marketplace: auction still active."); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - } - - modifier whenAuctionHasEnded() { - vm.warp(auctionParams.endTimestamp + 1); - _; - } - - function test_collectAuctionPayout_whenNoWinningBid() public whenAuctionNotCancelled whenAuctionHasEnded { - vm.warp(auctionParams.endTimestamp + 1); - - vm.prank(seller); - vm.expectRevert("Marketplace: no bids were made."); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - } - - modifier whenAuctionHasWinningBid() { - vm.warp(auctionParams.startTimestamp + 1); - - // Bid in auction - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, auctionParams.minimumBidAmount); - _; - } - - function test_collectAuctionPayout_whenAuctionTokensAlreadyPaidOut() - public - whenAuctionNotCancelled - whenAuctionHasWinningBid - whenAuctionHasEnded - { - vm.prank(seller); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - - vm.prank(seller); - vm.expectRevert("Marketplace: payout already completed."); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - } - - modifier whenAuctionTokensNotPaidOut() { - _; - } - - function test_collectAuctionPayout_whenAuctionTokensNotYetPaidOut() - public - whenAuctionNotCancelled - whenAuctionHasWinningBid - whenAuctionHasEnded - whenAuctionTokensNotPaidOut - { - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - - uint256 marketplaceBal = erc20.balanceOf(address(marketplace)); - assertEq(marketplaceBal, auctionParams.minimumBidAmount); - assertEq(erc20.balanceOf(seller), 0); - - vm.prank(seller); - vm.expectEmit(true, true, true, true); - emit AuctionClosed(auctionId, address(erc1155), seller, auctionParams.tokenId, seller, buyer); - EnglishAuctionsLogic(marketplace).collectAuctionPayout(auctionId); - - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.COMPLETED) - ); - - uint256 defaultFee = (marketplaceBal * 100) / 10_000; - - assertEq(erc20.balanceOf(address(marketplace)), 0); - assertEq(erc20.balanceOf(seller), marketplaceBal - defaultFee); - assertEq(erc20.balanceOf(defaultFeeRecipient), defaultFee); - } -} diff --git a/src/test/marketplace/english-auctions/collectAuctionPayout/collectAuctionPayout.tree b/src/test/marketplace/english-auctions/collectAuctionPayout/collectAuctionPayout.tree deleted file mode 100644 index 571fd4a61..000000000 --- a/src/test/marketplace/english-auctions/collectAuctionPayout/collectAuctionPayout.tree +++ /dev/null @@ -1,17 +0,0 @@ -function collectAuctionPayout(uint256 _auctionId) external -. -├── when auction is cancelled -│ └── it should revert ✅ -└── when auction is not cancelled - ├── when auction has not ended - │ └── it should revert ✅ - └── when auction has ended - ├── when there is no winning bid - │ └── it should revert ✅ - └── when there is a winning bid - ├── when creator already paid out - │ └── it should revert ✅ - └── when creator not already paid out ✅ - ├── it should set auction status to completed - ├── it should pay the auction winning bid to creator - └── it should emit AuctionClosed event diff --git a/src/test/marketplace/english-auctions/collectAuctionTokens/collectAuctionTokens.t.sol b/src/test/marketplace/english-auctions/collectAuctionTokens/collectAuctionTokens.t.sol deleted file mode 100644 index 11b451d67..000000000 --- a/src/test/marketplace/english-auctions/collectAuctionTokens/collectAuctionTokens.t.sol +++ /dev/null @@ -1,298 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test helper imports -import "../../../utils/BaseTest.sol"; - -// Test contracts and interfaces -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { MarketplaceV3, IPlatformFee } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { EnglishAuctionsLogic } from "contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC721Base } from "contracts/base/ERC721Base.sol"; - -import { IEnglishAuctions } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -contract ReentrantRecipient is ERC1155Holder { - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes memory data - ) public virtual override returns (bytes4) { - uint256 auctionId = 0; - uint256 bidAmount = 10 ether; - EnglishAuctionsLogic(msg.sender).bidInAuction(auctionId, bidAmount); - return super.onERC1155Received(operator, from, id, value, data); - } -} - -contract CollectAuctionTokensTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - // Auction parameters - uint256 internal auctionId; - address internal winningBidder = address(0x123); - IEnglishAuctions.AuctionParameters internal auctionParams; - - // Events - event AuctionClosed( - uint256 indexed auctionId, - address indexed assetContract, - address indexed closer, - uint256 tokenId, - address auctionCreator, - address winningBidder - ); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc1155)); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - - // Sample auction parameters. - address assetContract = address(erc1155); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100 minutes; - uint64 endTimestamp = 200 minutes; - - // Auction tokens. - auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - // Mint NFT to seller. - erc721.mint(seller, 1); // to, amount - erc1155.mint(seller, 0, 100); // to, id, amount - - // Create auction - vm.startPrank(seller); - erc721.setApprovalForAll(marketplace, true); - erc1155.setApprovalForAll(marketplace, true); - auctionId = EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - vm.stopPrank(); - - // Mint currency to bidder. - erc20.mint(buyer, 10_000 ether); - - vm.prank(buyer); - erc20.approve(marketplace, 100 ether); - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `EnglishAuctions` - address englishAuctions = address(new EnglishAuctionsLogic(address(weth))); - vm.label(englishAuctions, "EnglishAuctions_Extension"); - - // Extension: EnglishAuctionsLogic - Extension memory extension_englishAuctions; - extension_englishAuctions.metadata = ExtensionMetadata({ - name: "EnglishAuctionsLogic", - metadataURI: "ipfs://EnglishAuctions", - implementation: englishAuctions - }); - - extension_englishAuctions.functions = new ExtensionFunction[](12); - extension_englishAuctions.functions[0] = ExtensionFunction( - EnglishAuctionsLogic.totalAuctions.selector, - "totalAuctions()" - ); - extension_englishAuctions.functions[1] = ExtensionFunction( - EnglishAuctionsLogic.createAuction.selector, - "createAuction((address,uint256,uint256,address,uint256,uint256,uint64,uint64,uint64,uint64))" - ); - extension_englishAuctions.functions[2] = ExtensionFunction( - EnglishAuctionsLogic.cancelAuction.selector, - "cancelAuction(uint256)" - ); - extension_englishAuctions.functions[3] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionPayout.selector, - "collectAuctionPayout(uint256)" - ); - extension_englishAuctions.functions[4] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionTokens.selector, - "collectAuctionTokens(uint256)" - ); - extension_englishAuctions.functions[5] = ExtensionFunction( - EnglishAuctionsLogic.bidInAuction.selector, - "bidInAuction(uint256,uint256)" - ); - extension_englishAuctions.functions[6] = ExtensionFunction( - EnglishAuctionsLogic.isNewWinningBid.selector, - "isNewWinningBid(uint256,uint256)" - ); - extension_englishAuctions.functions[7] = ExtensionFunction( - EnglishAuctionsLogic.getAuction.selector, - "getAuction(uint256)" - ); - extension_englishAuctions.functions[8] = ExtensionFunction( - EnglishAuctionsLogic.getAllAuctions.selector, - "getAllAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[9] = ExtensionFunction( - EnglishAuctionsLogic.getAllValidAuctions.selector, - "getAllValidAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[10] = ExtensionFunction( - EnglishAuctionsLogic.getWinningBid.selector, - "getWinningBid(uint256)" - ); - extension_englishAuctions.functions[11] = ExtensionFunction( - EnglishAuctionsLogic.isAuctionExpired.selector, - "isAuctionExpired(uint256)" - ); - - extensions[0] = extension_englishAuctions; - } - - function test_collectAuctionTokens_whenAuctionIsCancelled() public { - vm.prank(seller); - EnglishAuctionsLogic(marketplace).cancelAuction(auctionId); - - vm.prank(buyer); - vm.expectRevert("Marketplace: invalid auction."); - EnglishAuctionsLogic(marketplace).collectAuctionTokens(auctionId); - } - - modifier whenAuctionNotCancelled() { - _; - } - - function test_collectAuctionTokens_whenAuctionIsActive() public whenAuctionNotCancelled { - vm.warp(auctionParams.startTimestamp + 1); - - vm.prank(buyer); - vm.expectRevert("Marketplace: auction still active."); - EnglishAuctionsLogic(marketplace).collectAuctionTokens(auctionId); - } - - modifier whenAuctionHasEnded() { - vm.warp(auctionParams.endTimestamp + 1); - _; - } - - function test_collectAuctionTokens_whenNoWinningBid() public whenAuctionNotCancelled whenAuctionHasEnded { - vm.warp(auctionParams.endTimestamp + 1); - - vm.prank(buyer); - vm.expectRevert("Marketplace: no bids were made."); - EnglishAuctionsLogic(marketplace).collectAuctionTokens(auctionId); - } - - modifier whenAuctionHasWinningBid() { - vm.warp(auctionParams.startTimestamp + 1); - - // Bid in auction - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).bidInAuction(auctionId, auctionParams.minimumBidAmount); - _; - } - - function test_collectAuctionTokens_whenAuctionTokensAlreadyPaidOut() - public - whenAuctionNotCancelled - whenAuctionHasWinningBid - whenAuctionHasEnded - { - vm.prank(buyer); - EnglishAuctionsLogic(marketplace).collectAuctionTokens(auctionId); - - vm.prank(buyer); - vm.expectRevert("Marketplace: payout already completed."); - EnglishAuctionsLogic(marketplace).collectAuctionTokens(auctionId); - } - - modifier whenAuctionTokensNotPaidOut() { - _; - } - - function test_collectAuctionTokens_whenAuctionTokensNotYetPaidOut() - public - whenAuctionNotCancelled - whenAuctionHasWinningBid - whenAuctionHasEnded - whenAuctionTokensNotPaidOut - { - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - - assertEq(erc1155.balanceOf(address(marketplace), auctionParams.tokenId), 1); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 0); - - vm.prank(buyer); - vm.expectEmit(true, true, true, true); - emit AuctionClosed(auctionId, address(erc1155), buyer, auctionParams.tokenId, seller, buyer); - EnglishAuctionsLogic(marketplace).collectAuctionTokens(auctionId); - - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(auctionId).status), - uint256(IEnglishAuctions.Status.COMPLETED) - ); - assertEq(EnglishAuctionsLogic(marketplace).getAuction(auctionId).endTimestamp, uint64(block.timestamp)); - - assertEq(erc1155.balanceOf(address(marketplace), auctionParams.tokenId), 0); - assertEq(erc1155.balanceOf(buyer, auctionParams.tokenId), 1); - } -} diff --git a/src/test/marketplace/english-auctions/collectAuctionTokens/collectAuctionTokens.tree b/src/test/marketplace/english-auctions/collectAuctionTokens/collectAuctionTokens.tree deleted file mode 100644 index d54bd1ba3..000000000 --- a/src/test/marketplace/english-auctions/collectAuctionTokens/collectAuctionTokens.tree +++ /dev/null @@ -1,18 +0,0 @@ -function collectAuctionTokens(uint256 _auctionId) external -. -├── when the auction cancelled -│ └── it reverts ✅ -└── when the auction is not cancelled - ├── when the auction is still active - │ └── it should reverts ✅ - └── when the auction is not active - ├── when the auction has no wining bid - │ └── it should reverts ✅ - └── when the auction has a wining bid - ├── when auction bidder has already been paid out tokens - │ └── it should reverts ✅ - └── when auction creator has not been paid out tokens ✅ - ├── it should set auction timestamp to block timestamp - ├── it should set auction state to completed - ├── it should transfer auction tokens to bidder - └── it should emit AuctionClosed event with auction ID, asset contract, caller, tokenId, creator, bidder \ No newline at end of file diff --git a/src/test/marketplace/english-auctions/createAuction/createAuction.t.sol b/src/test/marketplace/english-auctions/createAuction/createAuction.t.sol deleted file mode 100644 index 8bc759b31..000000000 --- a/src/test/marketplace/english-auctions/createAuction/createAuction.t.sol +++ /dev/null @@ -1,318 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test helper imports -import "../../../utils/BaseTest.sol"; - -// Test contracts and interfaces -import { RoyaltyPaymentsLogic } from "contracts/extension/plugin/RoyaltyPayments.sol"; -import { MarketplaceV3, IPlatformFee } from "contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol"; -import { EnglishAuctionsLogic } from "contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC721Base } from "contracts/base/ERC721Base.sol"; - -import { IEnglishAuctions } from "contracts/prebuilts/marketplace/IMarketplace.sol"; - -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; - -contract InvalidToken { - function supportsInterface(bytes4) public pure returns (bool) { - return false; - } -} - -contract CreateAuctionTest is BaseTest, IExtension { - // Target contract - address public marketplace; - - // Participants - address public marketplaceDeployer; - address public seller; - address public buyer; - - // Auction parameters - IEnglishAuctions.AuctionParameters internal auctionParams; - - // Events - /// @dev Emitted when a new auction is created. - event NewAuction( - address indexed auctionCreator, - uint256 indexed auctionId, - address indexed assetContract, - IEnglishAuctions.Auction auction - ); - - function setUp() public override { - super.setUp(); - - marketplaceDeployer = getActor(1); - seller = getActor(2); - buyer = getActor(3); - - // Deploy implementation. - Extension[] memory extensions = _setupExtensions(); - address impl = address( - new MarketplaceV3(MarketplaceV3.MarketplaceConstructorParams(extensions, address(0), address(weth))) - ); - - vm.prank(marketplaceDeployer); - marketplace = address( - new TWProxy( - impl, - abi.encodeCall( - MarketplaceV3.initialize, - (marketplaceDeployer, "", new address[](0), marketplaceDeployer, 0) - ) - ) - ); - - // Setup roles for seller and assets - vm.startPrank(marketplaceDeployer); - Permissions(marketplace).revokeRole(keccak256("ASSET_ROLE"), address(0)); - Permissions(marketplace).revokeRole(keccak256("LISTER_ROLE"), address(0)); - - vm.stopPrank(); - - vm.label(impl, "MarketplaceV3_Impl"); - vm.label(marketplace, "Marketplace"); - vm.label(seller, "Seller"); - vm.label(buyer, "Buyer"); - vm.label(address(erc721), "ERC721_Token"); - vm.label(address(erc1155), "ERC1155_Token"); - - // Sample auction parameters. - address assetContract = address(erc721); - uint256 tokenId = 0; - uint256 quantity = 1; - address currency = address(erc20); - uint256 minimumBidAmount = 1 ether; - uint256 buyoutBidAmount = 10 ether; - uint64 timeBufferInSeconds = 10 seconds; - uint64 bidBufferBps = 1000; - uint64 startTimestamp = 100; - uint64 endTimestamp = 200; - - // Auction tokens. - auctionParams = IEnglishAuctions.AuctionParameters( - assetContract, - tokenId, - quantity, - currency, - minimumBidAmount, - buyoutBidAmount, - timeBufferInSeconds, - bidBufferBps, - startTimestamp, - endTimestamp - ); - - // Mint NFT to seller. - erc721.mint(seller, 1); // to, amount - erc1155.mint(seller, 0, 100); // to, id, amount - } - - function _setupExtensions() internal returns (Extension[] memory extensions) { - extensions = new Extension[](1); - - // Deploy `EnglishAuctions` - address englishAuctions = address(new EnglishAuctionsLogic(address(weth))); - vm.label(englishAuctions, "EnglishAuctions_Extension"); - - // Extension: EnglishAuctionsLogic - Extension memory extension_englishAuctions; - extension_englishAuctions.metadata = ExtensionMetadata({ - name: "EnglishAuctionsLogic", - metadataURI: "ipfs://EnglishAuctions", - implementation: englishAuctions - }); - - extension_englishAuctions.functions = new ExtensionFunction[](12); - extension_englishAuctions.functions[0] = ExtensionFunction( - EnglishAuctionsLogic.totalAuctions.selector, - "totalAuctions()" - ); - extension_englishAuctions.functions[1] = ExtensionFunction( - EnglishAuctionsLogic.createAuction.selector, - "createAuction((address,uint256,uint256,address,uint256,uint256,uint64,uint64,uint64,uint64))" - ); - extension_englishAuctions.functions[2] = ExtensionFunction( - EnglishAuctionsLogic.cancelAuction.selector, - "cancelAuction(uint256)" - ); - extension_englishAuctions.functions[3] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionPayout.selector, - "collectAuctionPayout(uint256)" - ); - extension_englishAuctions.functions[4] = ExtensionFunction( - EnglishAuctionsLogic.collectAuctionTokens.selector, - "collectAuctionTokens(uint256)" - ); - extension_englishAuctions.functions[5] = ExtensionFunction( - EnglishAuctionsLogic.bidInAuction.selector, - "bidInAuction(uint256,uint256)" - ); - extension_englishAuctions.functions[6] = ExtensionFunction( - EnglishAuctionsLogic.isNewWinningBid.selector, - "isNewWinningBid(uint256,uint256)" - ); - extension_englishAuctions.functions[7] = ExtensionFunction( - EnglishAuctionsLogic.getAuction.selector, - "getAuction(uint256)" - ); - extension_englishAuctions.functions[8] = ExtensionFunction( - EnglishAuctionsLogic.getAllAuctions.selector, - "getAllAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[9] = ExtensionFunction( - EnglishAuctionsLogic.getAllValidAuctions.selector, - "getAllValidAuctions(uint256,uint256)" - ); - extension_englishAuctions.functions[10] = ExtensionFunction( - EnglishAuctionsLogic.getWinningBid.selector, - "getWinningBid(uint256)" - ); - extension_englishAuctions.functions[11] = ExtensionFunction( - EnglishAuctionsLogic.isAuctionExpired.selector, - "isAuctionExpired(uint256)" - ); - - extensions[0] = extension_englishAuctions; - } - - function test_createAuction_whenCallerDoesntHaveListerRole() public { - assertEq(Permissions(marketplace).hasRole(keccak256("LISTER_ROLE"), seller), false); - - vm.prank(seller); - vm.expectRevert("!LISTER_ROLE"); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - modifier whenCallerHasListerRole() { - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("LISTER_ROLE"), seller); - _; - } - - function test_createAuction_whenAssetDoesnHaveAssetRole() public whenCallerHasListerRole { - assertEq(Permissions(marketplace).hasRole(keccak256("ASSET_ROLE"), address(erc721)), false); - - vm.prank(seller); - vm.expectRevert("!ASSET_ROLE"); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - modifier whenAssetHasAssetRole() { - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), address(erc721)); - _; - } - - function test_createAuction_whenTokenIsInvalid() public whenCallerHasListerRole whenAssetHasAssetRole { - address newToken = address(new InvalidToken()); - - vm.prank(marketplaceDeployer); - Permissions(marketplace).grantRole(keccak256("ASSET_ROLE"), newToken); - - auctionParams.assetContract = newToken; - - vm.prank(seller); - vm.expectRevert("Marketplace: auctioned token must be ERC1155 or ERC721."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - modifier whenTokenIsValid() { - _; - } - - function test_createAuction_whenAuctionParamsAreInvalid() - public - whenCallerHasListerRole - whenAssetHasAssetRole - whenTokenIsValid - { - // This is one way for params to be invalid. `_validateNewAuction` has its own tests. - auctionParams.quantity = 0; - - vm.prank(seller); - vm.expectRevert("Marketplace: auctioning zero quantity."); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - } - - modifier whenAuctionParamsAreValid() { - _; - } - - function test_createAuction_whenAuctionParamsAreValid() - public - whenCallerHasListerRole - whenAssetHasAssetRole - whenTokenIsValid - whenAuctionParamsAreValid - { - uint256 expectedAuctionId = 0; - - assertEq(EnglishAuctionsLogic(marketplace).totalAuctions(), 0); - assertEq(erc721.ownerOf(0), seller); - assertEq(EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).assetContract, address(0)); - - vm.prank(seller); - erc721.setApprovalForAll(marketplace, true); - - IEnglishAuctions.Auction memory dummyAuction; - - vm.prank(seller); - vm.expectEmit(true, true, true, false); - emit NewAuction(seller, expectedAuctionId, auctionParams.assetContract, dummyAuction); - EnglishAuctionsLogic(marketplace).createAuction(auctionParams); - - assertEq(EnglishAuctionsLogic(marketplace).totalAuctions(), 1); - assertEq(erc721.ownerOf(0), marketplace); - assertEq(EnglishAuctionsLogic(marketplace).getAllAuctions(0, 0).length, 1); - - assertEq(EnglishAuctionsLogic(marketplace).getAllValidAuctions(0, 0).length, 0); - vm.warp(auctionParams.startTimestamp); - assertEq(EnglishAuctionsLogic(marketplace).getAllValidAuctions(0, 0).length, 1); - - assertEq(EnglishAuctionsLogic(marketplace).getAllAuctions(0, 0).length, 1); - assertEq(EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).auctionId, expectedAuctionId); - assertEq( - EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).assetContract, - auctionParams.assetContract - ); - assertEq(EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).tokenId, auctionParams.tokenId); - assertEq( - EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).minimumBidAmount, - auctionParams.minimumBidAmount - ); - assertEq( - EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).buyoutBidAmount, - auctionParams.buyoutBidAmount - ); - assertEq( - EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).timeBufferInSeconds, - auctionParams.timeBufferInSeconds - ); - assertEq( - EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).bidBufferBps, - auctionParams.bidBufferBps - ); - assertEq( - EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).startTimestamp, - auctionParams.startTimestamp - ); - assertEq( - EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).endTimestamp, - auctionParams.endTimestamp - ); - assertEq(EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).auctionCreator, seller); - assertEq(EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).currency, auctionParams.currency); - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).tokenType), - uint256(IEnglishAuctions.TokenType.ERC721) - ); - assertEq( - uint256(EnglishAuctionsLogic(marketplace).getAuction(expectedAuctionId).status), - uint256(IEnglishAuctions.Status.CREATED) - ); - } -} diff --git a/src/test/marketplace/english-auctions/createAuction/createAuction.tree b/src/test/marketplace/english-auctions/createAuction/createAuction.tree deleted file mode 100644 index dcfa71e4c..000000000 --- a/src/test/marketplace/english-auctions/createAuction/createAuction.tree +++ /dev/null @@ -1,13 +0,0 @@ -function createAuction(AuctionParameters calldata _params) -├── when the caller does not have LISTER_ROLE -│ └── it should revert ✅ -└── when the caller has LISTER_ROLE - ├── when the asset does not have ASSET_ROLE - │ └── it should revert ✅ - └── when the asset has ASSET_ROLE - ├── when the auction params are invalid - │ └── it should revert ✅ - └── when the auction params are valid ✅ - ├── it should create the intended auction - ├── it should escrow asset to auction - └── it should emit an AuctionCreated event with auction creator, auction ID, asset contract, auction data \ No newline at end of file diff --git a/src/test/minimal-factory/MinimalFactory.t.sol b/src/test/minimal-factory/MinimalFactory.t.sol deleted file mode 100644 index a819ee55b..000000000 --- a/src/test/minimal-factory/MinimalFactory.t.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "contracts/infra/TWMinimalFactory.sol"; -import "contracts/infra/TWProxy.sol"; - -import "@openzeppelin/contracts/proxy/Clones.sol"; - -import "../utils/BaseTest.sol"; - -contract DummyUpgradeable { - uint256 public number; - - constructor() {} - - function initialize(uint256 _num) public { - number = _num; - } -} - -contract TWNotMinimalFactory { - /// @dev Deploys a proxy that points to the given implementation. - function deployProxyByImplementation(address _implementation, bytes memory _data, bytes32 _salt) public { - address deployedProxy = Clones.cloneDeterministic(_implementation, _salt); - - if (_data.length > 0) { - // slither-disable-next-line unused-return - Address.functionCall(deployedProxy, _data); - } - } -} - -contract MinimalFactoryTest is BaseTest { - address internal implementation; - bytes32 internal salt; - bytes internal data; - address admin; - - TWNotMinimalFactory notMinimal; - - function setUp() public override { - super.setUp(); - admin = getActor(5000); - vm.startPrank(admin); - implementation = getContract("TokenERC20"); - salt = keccak256("yooo"); - data = abi.encodeWithSelector( - TokenERC20.initialize.selector, - admin, - "MinimalToken", - "MT", - "ipfs://notCentralized", - new address[](0), - admin, - admin, - 50 - ); - - notMinimal = new TWNotMinimalFactory(); - } - - // gas: Baseline + 140k - function test_gas_twProxy() public { - new TWProxy(implementation, data); - } - - // gas: Baseline + 41.5k - function test_gas_notMinimalFactory() public { - notMinimal.deployProxyByImplementation(implementation, data, salt); - } - - // gas: Baseline - function test_gas_minimal() public { - new TWMinimalFactory(implementation, data, salt); - } - - function test_verify_deployedProxy() public { - vm.stopPrank(); - vm.prank(address(0x123456)); - address minimalFactory = address(new TWMinimalFactory(implementation, data, salt)); - bytes32 salthash = keccak256(abi.encodePacked(address(0x123456), salt)); - address deployedProxy = Clones.predictDeterministicAddress(implementation, salthash, minimalFactory); - - bytes32 contractType = TokenERC20(deployedProxy).contractType(); - assertEq(contractType, bytes32("TokenERC20")); - } -} diff --git a/src/test/mocks/Mock.sol b/src/test/mocks/Mock.sol deleted file mode 100644 index 6948d05fb..000000000 --- a/src/test/mocks/Mock.sol +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -/// @author thirdweb - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "contracts/eip/interface/IERC721.sol"; -import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; - -/* - * @dev Mock contract for typechain types generation purposes :) - */ -contract Mock { - IERC20 public erc20; - IERC721 public erc721; - IERC1155 public erc1155; -} - -contract MockContract { - bytes32 private name; - uint8 private version; - - constructor(bytes32 _name, uint8 _version) { - name = _name; - version = _version; - } - - /// @dev Returns the module type of the contract. - function contractType() external view returns (bytes32) { - return name; - } - - /// @dev Returns the version of the contract. - function contractVersion() external view returns (uint8) { - return version; - } -} diff --git a/src/test/mocks/MockContractPublisher.sol b/src/test/mocks/MockContractPublisher.sol deleted file mode 100644 index 3ce6a09f4..000000000 --- a/src/test/mocks/MockContractPublisher.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -/// @author thirdweb - -import "contracts/infra/interface/IContractPublisher.sol"; - -// solhint-disable const-name-snakecase -contract MockContractPublisher is IContractPublisher { - function getAllPublishedContracts( - address - ) external pure override returns (CustomContractInstance[] memory published) { - CustomContractInstance[] memory mocks = new CustomContractInstance[](1); - mocks[0] = CustomContractInstance( - "MockContract", - 123, - "ipfs://mock", - 0x0000000000000000000000000000000000000000000000000000000000000001, - address(0x0000000000000000000000000000000000000000) - ); - return mocks; - } - - function getPublishedContractVersions( - address, - string memory - ) external pure returns (CustomContractInstance[] memory published) { - return new CustomContractInstance[](0); - } - - function getPublishedContract( - address, - string memory - ) external pure returns (CustomContractInstance memory published) { - return CustomContractInstance("", 0, "", "", address(0)); - } - - function publishContract( - address publisher, - string memory contractId, - string memory publishMetadataUri, - string memory compilerMetadataUri, - bytes32 bytecodeHash, - address implementation - ) external {} - - function unpublishContract(address publisher, string memory contractId) external {} - - function setPublisherProfileUri(address, string memory) external {} - - function getPublisherProfileUri(address) external pure returns (string memory uri) { - return ""; - } - - function getPublishedUriFromCompilerUri( - string memory - ) external pure returns (string[] memory publishedMetadataUris) { - return new string[](0); - } -} diff --git a/src/test/mocks/MockERC1155.sol b/src/test/mocks/MockERC1155.sol deleted file mode 100644 index c1af0dc5a..000000000 --- a/src/test/mocks/MockERC1155.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/token/ERC1155/presets/ERC1155PresetMinterPauser.sol"; - -contract MockERC1155 is ERC1155PresetMinterPauser { - constructor() ERC1155PresetMinterPauser("ipfs://BaseURI") {} - - function mint(address to, uint256 id, uint256 amount) public virtual { - _mint(to, id, amount, ""); - } - - function hasRole(bytes32, address) public pure override(AccessControl, IAccessControl) returns (bool) { - return true; - } - - function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts) public virtual { - require(hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint"); - - _mintBatch(to, ids, amounts, ""); - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return super.supportsInterface(interfaceId); - } -} diff --git a/src/test/mocks/MockERC1155NonBurnable.sol b/src/test/mocks/MockERC1155NonBurnable.sol deleted file mode 100644 index dff355c7f..000000000 --- a/src/test/mocks/MockERC1155NonBurnable.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; - -contract MockERC1155NonBurnable is ERC1155 { - constructor() ERC1155("ipfs://BaseURI") {} - - function mint(address to, uint256 id, uint256 amount) public virtual { - _mint(to, id, amount, ""); - } - - function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts) public virtual { - _mintBatch(to, ids, amounts, ""); - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return super.supportsInterface(interfaceId); - } -} diff --git a/src/test/mocks/MockERC20.sol b/src/test/mocks/MockERC20.sol deleted file mode 100644 index 6b3a5bb8a..000000000 --- a/src/test/mocks/MockERC20.sol +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol"; - -contract MockERC20 is ERC20PresetMinterPauser, ERC20Permit { - bool internal taxActive; - - constructor() ERC20PresetMinterPauser("Mock Coin", "MOCK") ERC20Permit("Mock Coin") {} - - function mint(address to, uint256 amount) public override(ERC20PresetMinterPauser) { - _mint(to, amount); - } - - function toggleTax() external { - taxActive = !taxActive; - } - - function _transfer(address from, address to, uint256 amount) internal override { - if (taxActive) { - uint256 tax = (amount * 10) / 100; - amount -= tax; - super._transfer(from, address(this), tax); - } - super._transfer(from, to, amount); - } - - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal override(ERC20PresetMinterPauser, ERC20) { - super._beforeTokenTransfer(from, to, amount); - } -} diff --git a/src/test/mocks/MockERC20CustomDecimals.sol b/src/test/mocks/MockERC20CustomDecimals.sol deleted file mode 100644 index 65ad6a20a..000000000 --- a/src/test/mocks/MockERC20CustomDecimals.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol"; - -contract MockERC20CustomDecimals is ERC20PresetMinterPauser, ERC20Permit { - uint8 private immutable _decimals; - - constructor(uint8 decimals_) ERC20PresetMinterPauser("Mock Coin", "MOCK") ERC20Permit("Mock Coin") { - _decimals = decimals_; - } - - function mint(address to, uint256 amount) public override(ERC20PresetMinterPauser) { - _mint(to, amount); - } - - function decimals() public view override returns (uint8) { - return _decimals; - } - - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal override(ERC20PresetMinterPauser, ERC20) { - super._beforeTokenTransfer(from, to, amount); - } -} diff --git a/src/test/mocks/MockERC20NonCompliant.sol b/src/test/mocks/MockERC20NonCompliant.sol deleted file mode 100644 index 0e938b1de..000000000 --- a/src/test/mocks/MockERC20NonCompliant.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -contract MockERC20NonCompliant { - mapping(address => uint256) private _balances; - mapping(address => mapping(address => uint256)) private _allowances; - uint256 private _totalSupply; - - constructor() {} - - function decimals() public view virtual returns (uint8) { - return 18; - } - - function totalSupply() public view virtual returns (uint256) { - return _totalSupply; - } - - function balanceOf(address account) public view virtual returns (uint256) { - return _balances[account]; - } - - function allowance(address owner, address spender) public view virtual returns (uint256) { - return _allowances[owner][spender]; - } - - function approve(address spender, uint256 amount) public virtual returns (bool) { - address owner = msg.sender; - _approve(owner, spender, amount); - return true; - } - - // non-compliant ERC20 as transfer doesn't return a bool - function transfer(address to, uint256 amount) public virtual { - address owner = msg.sender; - _transfer(owner, to, amount); - } - - // non-compliant ERC20 as transferFrom doesn't return a bool - function transferFrom(address from, address to, uint256 amount) public { - address spender = msg.sender; - _spendAllowance(from, spender, amount); - _transfer(from, to, amount); - } - - function _transfer(address from, address to, uint256 amount) internal virtual { - require(from != address(0), "ERC20: transfer from the zero address"); - require(to != address(0), "ERC20: transfer to the zero address"); - - uint256 fromBalance = _balances[from]; - require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); - unchecked { - _balances[from] = fromBalance - amount; - } - _balances[to] += amount; - } - - function _mint(address account, uint256 amount) internal virtual { - require(account != address(0), "ERC20: mint to the zero address"); - - _totalSupply += amount; - unchecked { - // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. - _balances[account] += amount; - } - } - - function mint(address to, uint256 amount) public { - _mint(to, amount); - } - - function _approve(address owner, address spender, uint256 amount) internal virtual { - require(owner != address(0), "ERC20: approve from the zero address"); - require(spender != address(0), "ERC20: approve to the zero address"); - - _allowances[owner][spender] = amount; - } - - function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { - uint256 currentAllowance = allowance(owner, spender); - if (currentAllowance != type(uint256).max) { - require(currentAllowance >= amount, "ERC20: insufficient allowance"); - unchecked { - _approve(owner, spender, currentAllowance - amount); - } - } - } -} diff --git a/src/test/mocks/MockERC721.sol b/src/test/mocks/MockERC721.sol deleted file mode 100644 index 68cf3d14c..000000000 --- a/src/test/mocks/MockERC721.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; - -contract MockERC721 is ERC721Burnable { - uint256 public nextTokenIdToMint; - - constructor() ERC721("MockERC721", "MOCK") {} - - function mint(address _receiver, uint256 _amount) external { - uint256 tokenId = nextTokenIdToMint; - nextTokenIdToMint += _amount; - - for (uint256 i = 0; i < _amount; i += 1) { - _mint(_receiver, tokenId); - tokenId += 1; - } - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return super.supportsInterface(interfaceId); - } -} diff --git a/src/test/mocks/MockERC721NonBurnable.sol b/src/test/mocks/MockERC721NonBurnable.sol deleted file mode 100644 index 4668d2e75..000000000 --- a/src/test/mocks/MockERC721NonBurnable.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; - -contract MockERC721NonBurnable is ERC721 { - uint256 public nextTokenIdToMint; - - constructor() ERC721("MockERC721", "MOCK") {} - - function mint(address _receiver, uint256 _amount) external { - uint256 tokenId = nextTokenIdToMint; - nextTokenIdToMint += _amount; - - for (uint256 i = 0; i < _amount; i += 1) { - _mint(_receiver, tokenId); - tokenId += 1; - } - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return super.supportsInterface(interfaceId); - } -} diff --git a/src/test/mocks/MockRoyaltyEngineV1.sol b/src/test/mocks/MockRoyaltyEngineV1.sol deleted file mode 100644 index 429c86f41..000000000 --- a/src/test/mocks/MockRoyaltyEngineV1.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "contracts/extension/interface/IRoyaltyEngineV1.sol"; -import { IERC2981 } from "contracts/eip/interface/IERC2981.sol"; -import { ERC165 } from "contracts/eip/ERC165.sol"; - -contract MockRoyaltyEngineV1 is ERC165, IRoyaltyEngineV1 { - address payable[] public mockRecipients; - uint256[] public mockAmounts; - - constructor(address payable[] memory _mockRecipients, uint256[] memory _mockAmounts) { - mockRecipients = _mockRecipients; - mockAmounts = _mockAmounts; - } - - function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { - return interfaceId == type(IRoyaltyEngineV1).interfaceId || super.supportsInterface(interfaceId); - } - - function getRoyalty( - address tokenAddress, - uint256 tokenId, - uint256 value - ) public view override returns (address payable[] memory recipients, uint256[] memory amounts) { - try IERC2981(tokenAddress).royaltyInfo(tokenId, value) returns (address recipient, uint256 amount) { - // Supports EIP2981. Return amounts - recipients = new address payable[](1); - amounts = new uint256[](1); - recipients[0] = payable(recipient); - amounts[0] = amount; - return (recipients, amounts); - } catch {} - - // Non ERC2981. Return mock recipients/amounts. - recipients = mockRecipients; - amounts = mockAmounts; - return (recipients, amounts); - } - - function getRoyaltyView( - address tokenAddress, - uint256 tokenId, - uint256 value - ) public view override returns (address payable[] memory recipients, uint256[] memory amounts) {} -} diff --git a/src/test/mocks/MockThirdwebContract.sol b/src/test/mocks/MockThirdwebContract.sol deleted file mode 100644 index 5cf31f1a7..000000000 --- a/src/test/mocks/MockThirdwebContract.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "contracts/infra/interface/IThirdwebContract.sol"; - -// solhint-disable const-name-snakecase -contract MockThirdwebContract is IThirdwebContract { - string public contractURI; - bytes32 public constant contractType = bytes32("MOCK"); - uint8 public constant contractVersion = 1; - - function setContractURI(string calldata _uri) external { - contractURI = _uri; - } -} - -contract MockThirdwebContractV2 is IThirdwebContract { - string public contractURI; - bytes32 public constant contractType = bytes32("MOCK"); - uint8 public constant contractVersion = 2; - - function setContractURI(string calldata _uri) external { - contractURI = _uri; - } -} diff --git a/src/test/mocks/TestOracle2.sol b/src/test/mocks/TestOracle2.sol deleted file mode 100644 index 77c180c5b..000000000 --- a/src/test/mocks/TestOracle2.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -// source: https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/test/TestOracle2.sol - -interface IOracle { - function decimals() external view returns (uint8); - function latestRoundData() - external - view - returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); -} - -contract TestOracle2 is IOracle { - int256 public price; - uint8 private _decimals_; - - constructor(int256 _price, uint8 _decimals) { - price = _price; - _decimals_ = _decimals; - } - - function setPrice(int256 _price) external { - price = _price; - } - - function setDecimals(uint8 _decimals) external { - _decimals_ = _decimals; - } - - function decimals() external view override returns (uint8) { - return _decimals_; - } - - function latestRoundData() - external - view - override - returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) - { - // solhint-disable-next-line not-rely-on-time - return (73786976294838215802, price, 1680509051, block.timestamp, 73786976294838215802); - } -} diff --git a/src/test/mocks/TestUniswap.sol b/src/test/mocks/TestUniswap.sol deleted file mode 100644 index 4edfef65a..000000000 --- a/src/test/mocks/TestUniswap.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.0; - -// source: https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/test/TestUniswap.sol - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; - -import "./WETH9.sol"; - -/// @notice Very basic simulation of what Uniswap does with the swaps for the unit tests on the TokenPaymaster -/// @dev Do not use to test any actual Uniswap interaction logic as this is way too simplistic -contract TestUniswap { - WETH9 public weth; - - constructor(WETH9 _weth) { - weth = _weth; - } - - event StubUniswapExchangeEvent(uint256 amountIn, uint256 amountOut, address tokenIn, address tokenOut); - - function exactOutputSingle(ISwapRouter.ExactOutputSingleParams calldata params) external returns (uint256) { - uint256 amountIn = params.amountInMaximum - 5; - emit StubUniswapExchangeEvent(amountIn, params.amountOut, params.tokenIn, params.tokenOut); - IERC20(params.tokenIn).transferFrom(msg.sender, address(this), amountIn); - IERC20(params.tokenOut).transfer(params.recipient, params.amountOut); - return amountIn; - } - - function exactInputSingle(ISwapRouter.ExactInputSingleParams calldata params) external returns (uint256) { - uint256 amountOut = params.amountOutMinimum + 5; - emit StubUniswapExchangeEvent(params.amountIn, amountOut, params.tokenIn, params.tokenOut); - IERC20(params.tokenIn).transferFrom(msg.sender, address(this), params.amountIn); - IERC20(params.tokenOut).transfer(params.recipient, amountOut); - return amountOut; - } - - /// @notice Simplified code copied from here: - /// https://github.com/Uniswap/v3-periphery/blob/main/contracts/base/PeripheryPayments.sol#L19 - function unwrapWETH9(uint256 amountMinimum, address recipient) public payable { - uint256 balanceWETH9 = weth.balanceOf(address(this)); - require(balanceWETH9 >= amountMinimum, "Insufficient WETH9"); - - if (balanceWETH9 > 0) { - weth.withdraw(balanceWETH9); - payable(recipient).transfer(balanceWETH9); - } - } - - // solhint-disable-next-line no-empty-blocks - receive() external payable {} -} diff --git a/src/test/mocks/WETH9.sol b/src/test/mocks/WETH9.sol deleted file mode 100644 index 357229903..000000000 --- a/src/test/mocks/WETH9.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -contract WETH9 is ERC20 { - event Deposit(address indexed sender, uint256 amount); - event Withdrawal(address indexed sender, uint256 amount); - - constructor() ERC20("Wrapped Ether", "WETH") {} - - receive() external payable virtual { - deposit(); - } - - function deposit() public payable { - _mint(msg.sender, msg.value); - emit Deposit(msg.sender, msg.value); - } - - function withdraw(uint256 amount) public { - _burn(msg.sender, amount); - payable(msg.sender).transfer(amount); - emit Withdrawal(msg.sender, amount); - } - - function totalSupply() public view override returns (uint256) { - return address(this).balance; - } -} diff --git a/src/test/open-edition-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.t.sol b/src/test/open-edition-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.t.sol deleted file mode 100644 index 4ea304d13..000000000 --- a/src/test/open-edition-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.t.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { OpenEditionERC721FlatFee } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "src/test/utils/BaseTest.sol"; - -contract OpenEditionERC721FlatFeeHarness is OpenEditionERC721FlatFee { - function beforeTokenTransfers(address from, address to, uint256 startTokenId_, uint256 quantity) public { - _beforeTokenTransfers(from, to, startTokenId_, quantity); - } -} - -contract OpenEditionERC721FlatFeeTest_beforeTokenTransfers is BaseTest { - OpenEditionERC721FlatFeeHarness public openEdition; - - address private openEditionImpl; - - function setUp() public override { - super.setUp(); - openEditionImpl = address(new OpenEditionERC721FlatFeeHarness()); - vm.prank(deployer); - openEdition = OpenEditionERC721FlatFeeHarness( - address( - new TWProxy( - openEditionImpl, - abi.encodeCall( - OpenEditionERC721FlatFee.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc - //////////////////////////////////////////////////////////////*/ - - function test_revert_transfersRestricted() public { - address from = address(0x1); - address to = address(0x2); - bytes32 role = keccak256("TRANSFER_ROLE"); - vm.prank(deployer); - openEdition.revokeRole(role, address(0)); - - vm.expectRevert(bytes("!T")); - openEdition.beforeTokenTransfers(from, to, 0, 1); - } -} diff --git a/src/test/open-edition-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.tree b/src/test/open-edition-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.tree deleted file mode 100644 index 078bd6a79..000000000 --- a/src/test/open-edition-flat-fee/_beforeTokenTransfers/_beforeTokenTransfers.tree +++ /dev/null @@ -1,12 +0,0 @@ -function _beforeTokenTransfers( - address from, - address to, - uint256 startTokenId_, - uint256 quantity -) -└── when address(0) does not have the transfer role - └── when from does not equal address(0) - └── when to does not equal address(0) - └── when from does not have the transfer role - └── when to does not have the transfer role - └── it should revert ✅ diff --git a/src/test/open-edition-flat-fee/_canSetFunctions/_canSetFunctions.t.sol b/src/test/open-edition-flat-fee/_canSetFunctions/_canSetFunctions.t.sol deleted file mode 100644 index c45ca2514..000000000 --- a/src/test/open-edition-flat-fee/_canSetFunctions/_canSetFunctions.t.sol +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { OpenEditionERC721FlatFee } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "src/test/utils/BaseTest.sol"; - -contract OpenEditionERC721FlatFeeHarness is OpenEditionERC721FlatFee { - function canSetPrimarySaleRecipient() external view returns (bool) { - return _canSetPrimarySaleRecipient(); - } - - function canSetOwner() external view returns (bool) { - return _canSetOwner(); - } - - /// @dev Checks whether royalty info can be set in the given execution context. - function canSetRoyaltyInfo() external view returns (bool) { - return _canSetRoyaltyInfo(); - } - - /// @dev Checks whether contract metadata can be set in the given execution context. - function canSetContractURI() external view returns (bool) { - return _canSetContractURI(); - } - - /// @dev Checks whether platform fee info can be set in the given execution context. - function canSetClaimConditions() external view returns (bool) { - return _canSetClaimConditions(); - } - - /// @dev Returns whether the shared metadata of tokens can be set in the given execution context. - function canSetSharedMetadata() external view virtual returns (bool) { - return _canSetSharedMetadata(); - } -} - -contract OpenEditionERC721FlatFeeTest_canSetFunctions is BaseTest { - OpenEditionERC721FlatFeeHarness public openEdition; - - address private openEditionImpl; - - function setUp() public override { - super.setUp(); - openEditionImpl = address(new OpenEditionERC721FlatFeeHarness()); - vm.prank(deployer); - openEdition = OpenEditionERC721FlatFeeHarness( - address( - new TWProxy( - openEditionImpl, - abi.encodeCall( - OpenEditionERC721FlatFee.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc - //////////////////////////////////////////////////////////////*/ - - function test_canSetPrimarySaleRecipient_returnTrue() public { - vm.prank(deployer); - assertTrue(openEdition.canSetPrimarySaleRecipient()); - } - - function test_canSetPrimarySaleRecipient_returnFalse() public { - assertFalse(openEdition.canSetPrimarySaleRecipient()); - } - - function test_canSetOwner_returnTrue() public { - vm.prank(deployer); - assertTrue(openEdition.canSetOwner()); - } - - function test_canSetOwner_returnFalse() public { - assertFalse(openEdition.canSetOwner()); - } - - function test_canSetRoyaltyInfo_returnTrue() public { - vm.prank(deployer); - assertTrue(openEdition.canSetRoyaltyInfo()); - } - - function test_canSetRoyaltyInfo_returnFalse() public { - assertFalse(openEdition.canSetRoyaltyInfo()); - } - - function test_canSetContractURI_returnTrue() public { - vm.prank(deployer); - assertTrue(openEdition.canSetContractURI()); - } - - function test_canSetContractURI_returnFalse() public { - assertFalse(openEdition.canSetContractURI()); - } - - function test_canSetClaimConditions_returnTrue() public { - vm.prank(deployer); - assertTrue(openEdition.canSetClaimConditions()); - } - - function test_canSetClaimConditions_returnFalse() public { - assertFalse(openEdition.canSetClaimConditions()); - } - - function test_canSetSharedMetadata_returnTrue() public { - vm.prank(deployer); - assertTrue(openEdition.canSetSharedMetadata()); - } - - function test_canSetSharedMetadata_returnFalse() public { - assertFalse(openEdition.canSetSharedMetadata()); - } -} diff --git a/src/test/open-edition-flat-fee/_canSetFunctions/_canSetFunctions.tree b/src/test/open-edition-flat-fee/_canSetFunctions/_canSetFunctions.tree deleted file mode 100644 index 1ccb478fd..000000000 --- a/src/test/open-edition-flat-fee/_canSetFunctions/_canSetFunctions.tree +++ /dev/null @@ -1,39 +0,0 @@ -function _canSetPrimarySaleRecipient() -├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetOwner() -├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - - -function _canSetRoyaltyInfo() -├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - - -function _canSetContractURI() -├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - - -function _canSetClaimConditions() -├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - - -function _canSetSharedMetadata() -├── when _msgSender has minter role -│ └── it should return true ✅ -└── when _msgSender does not have minter role - └── it should return false ✅ diff --git a/src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.t.sol b/src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.t.sol deleted file mode 100644 index 2b9f5d609..000000000 --- a/src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.t.sol +++ /dev/null @@ -1,228 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { OpenEditionERC721FlatFee } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "src/test/utils/BaseTest.sol"; - -contract OpenEditionERC721FlatFeeHarness is OpenEditionERC721FlatFee { - function collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) external payable { - _collectPriceOnClaim(_primarySaleRecipient, _quantityToClaim, _currency, _pricePerToken); - } -} - -contract OpenEditionERC721FlatFeeTest_collectPrice is BaseTest { - OpenEditionERC721FlatFeeHarness public openEdition; - - address private openEditionImpl; - - address private currency; - address private primarySaleRecipient; - uint256 private msgValue; - uint256 private pricePerToken; - uint256 private qty = 1; - - address private defaultFeeRecipient; - - function setUp() public override { - super.setUp(); - openEditionImpl = address(new OpenEditionERC721FlatFeeHarness()); - vm.prank(deployer); - openEdition = OpenEditionERC721FlatFeeHarness( - address( - new TWProxy( - openEditionImpl, - abi.encodeCall( - OpenEditionERC721FlatFee.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ); - defaultFeeRecipient = openEdition.DEFAULT_FEE_RECIPIENT(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc - //////////////////////////////////////////////////////////////*/ - - modifier pricePerTokenZero() { - _; - } - - modifier pricePerTokenNotZero() { - pricePerToken = 1 ether; - _; - } - - modifier msgValueZero() { - _; - } - - modifier msgValueNotZero() { - msgValue = 1 ether; - _; - } - - modifier valuePriceMismatch() { - msgValue = 1 ether; - pricePerToken = 2 ether; - _; - } - - modifier primarySaleRecipientZeroAddress() { - primarySaleRecipient = address(0); - _; - } - - modifier primarySaleRecipientNotZeroAddress() { - primarySaleRecipient = address(0x0999); - _; - } - - modifier currencyNativeToken() { - currency = NATIVE_TOKEN; - _; - } - - modifier currencyNotNativeToken() { - currency = address(erc20); - _; - } - - function test_revert_pricePerTokenZeroMsgValueNotZero() public pricePerTokenZero msgValueNotZero { - vm.expectRevert("!Value"); - openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); - } - - function test_revert_nativeCurrencyValuePriceMismatch() public currencyNativeToken valuePriceMismatch { - vm.expectRevert(bytes("!V")); - openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); - } - - function test_revert_erc20ValuePriceMismatch() public currencyNotNativeToken valuePriceMismatch { - vm.expectRevert(bytes("!V")); - openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); - } - - function test_state_nativeCurrency() - public - currencyNativeToken - pricePerTokenNotZero - msgValueNotZero - primarySaleRecipientNotZeroAddress - { - uint256 beforeBalancePrimarySaleRecipient = address(primarySaleRecipient).balance; - uint256 defaultFeeRecipientBefore = address(defaultFeeRecipient).balance; - - openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); - - uint256 afterBalancePrimarySaleRecipient = address(primarySaleRecipient).balance; - uint256 defaultFeeRecipientAfter = address(defaultFeeRecipient).balance; - - uint256 defaultFee = (msgValue * 100) / 10_000; - uint256 platformFeeVal = (msgValue * platformFeeBps) / 10_000; - uint256 primarySaleRecipientVal = msgValue - platformFeeVal - defaultFee; - - assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, defaultFee); - } - - function test_revert_erc20_msgValueNotZero() - public - currencyNotNativeToken - msgValueNotZero - primarySaleRecipientNotZeroAddress - { - vm.expectRevert("!Value"); - openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); - } - - function test_state_erc20() public currencyNotNativeToken pricePerTokenNotZero primarySaleRecipientNotZeroAddress { - erc20.mint(address(this), pricePerToken); - ERC20(erc20).approve(address(openEdition), pricePerToken); - uint256 beforeBalancePrimarySaleRecipient = erc20.balanceOf(primarySaleRecipient); - uint256 defaultFeeRecipientBefore = erc20.balanceOf(defaultFeeRecipient); - - openEdition.collectPriceOnClaim(primarySaleRecipient, qty, currency, pricePerToken); - - uint256 afterBalancePrimarySaleRecipient = erc20.balanceOf(primarySaleRecipient); - uint256 defaultFeeRecipientAfter = erc20.balanceOf(defaultFeeRecipient); - - uint256 defaultFee = (1 ether * 100) / 10_000; - uint256 platformFeeVal = (1 ether * platformFeeBps) / 10_000; - uint256 primarySaleRecipientVal = 1 ether - platformFeeVal - defaultFee; - - assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, defaultFee); - } - - function test_state_erc20StoredPrimarySaleRecipient() - public - currencyNotNativeToken - pricePerTokenNotZero - primarySaleRecipientZeroAddress - { - address storedPrimarySaleRecipient = openEdition.primarySaleRecipient(); - - erc20.mint(address(this), pricePerToken); - ERC20(erc20).approve(address(openEdition), pricePerToken); - uint256 beforeBalancePrimarySaleRecipient = erc20.balanceOf(storedPrimarySaleRecipient); - uint256 defaultFeeRecipientBefore = erc20.balanceOf(defaultFeeRecipient); - - openEdition.collectPriceOnClaim(primarySaleRecipient, qty, currency, pricePerToken); - - uint256 afterBalancePrimarySaleRecipient = erc20.balanceOf(storedPrimarySaleRecipient); - uint256 defaultFeeRecipientAfter = erc20.balanceOf(defaultFeeRecipient); - - uint256 defaultFee = (1 ether * 100) / 10_000; - uint256 platformFeeVal = (1 ether * platformFeeBps) / 10_000; - uint256 primarySaleRecipientVal = 1 ether - platformFeeVal - defaultFee; - - assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, defaultFee); - } - - function test_state_nativeCurrencyStoredPrimarySaleRecipient() - public - currencyNativeToken - pricePerTokenNotZero - primarySaleRecipientZeroAddress - msgValueNotZero - { - address storedPrimarySaleRecipient = openEdition.primarySaleRecipient(); - - uint256 beforeBalancePrimarySaleRecipient = address(storedPrimarySaleRecipient).balance; - uint256 defaultFeeRecipientBefore = address(defaultFeeRecipient).balance; - - openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); - - uint256 afterBalancePrimarySaleRecipient = address(storedPrimarySaleRecipient).balance; - uint256 defaultFeeRecipientAfter = address(defaultFeeRecipient).balance; - - uint256 defaultFee = (msgValue * 100) / 10_000; - uint256 platformFeeVal = (msgValue * platformFeeBps) / 10_000; - uint256 primarySaleRecipientVal = msgValue - platformFeeVal - defaultFee; - - assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); - assertEq(defaultFeeRecipientAfter - defaultFeeRecipientBefore, defaultFee); - } -} diff --git a/src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.tree b/src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.tree deleted file mode 100644 index 2054cf049..000000000 --- a/src/test/open-edition-flat-fee/_collectPriceOnClaim/_collectPriceOnClaim.tree +++ /dev/null @@ -1,37 +0,0 @@ -function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken -) -├── when _pricePerToken is equal to zero -│ ├── when msg.value does not equal to zero -│ │ └── it should revert ✅ -│ └── when msg.value is equal to zero -│ └── it should return ✅ -└── when _pricePerToken is not equal to zero - ├── when _primarySaleRecipient is equal to address(0) - │ ├── when saleRecipient for _tokenId is equal to address(0) - │ │ ├── when currency is native token - │ │ │ ├── when msg.value does not equal totalPrice - │ │ │ │ └── it should revert ✅ - │ │ │ └── when msg.value does equal totalPrice - │ │ │ └── it should transfer totalPrice to primarySaleRecipient in native token ✅ - │ │ └── when currency is not native token - │ │ └── it should transfer totalPrice to primarySaleRecipient in _currency token ✅ - │ └── when salerecipient for _tokenId is not equal to address(0) - │ ├── when currency is native token - │ │ ├── when msg.value does not equal totalPrice - │ │ │ └── it should revert ✅ - │ │ └── when msg.value does equal totalPrice - │ │ └── it should transfer totalPrice to saleRecipient for _tokenId in native token ✅ - │ └── when currency is not native token - │ └── it should transfer totalPrice to saleRecipient for _tokenId in _currency token ✅ - └── when _primarySaleRecipient is not equal to address(0) - ├── when currency is native token - │ ├── when msg.value does not equal totalPrice - │ │ └── it should revert ✅ - │ └── when msg.value does equal totalPrice - │ └── it should transfer totalPrice to _primarySaleRecipient in native token ✅ - └── when currency is not native token - └── it should transfer totalPrice to _primarySaleRecipient in _currency token ✅ \ No newline at end of file diff --git a/src/test/open-edition-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.t.sol b/src/test/open-edition-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.t.sol deleted file mode 100644 index 681ca6caa..000000000 --- a/src/test/open-edition-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.t.sol +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { OpenEditionERC721FlatFee, IERC721AUpgradeable } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "src/test/utils/BaseTest.sol"; - -contract OpenEditionERC721FlatFeeHarness is OpenEditionERC721FlatFee { - function transferTokensOnClaim(address _to, uint256 quantityBeingClaimed) public { - _transferTokensOnClaim(_to, quantityBeingClaimed); - } -} - -contract MockERC721Receiver { - function onERC721Received(address, address, uint256, bytes memory) external pure returns (bytes4) { - return this.onERC721Received.selector; - } -} - -contract MockERC721NotReceiver {} - -contract OpenEditionERC721FlatFeeTest_transferTokensOnClaim is BaseTest { - OpenEditionERC721FlatFeeHarness public openEdition; - - MockERC721NotReceiver private notReceiver; - MockERC721Receiver private receiver; - - address private openEditionImpl; - - function setUp() public override { - super.setUp(); - openEditionImpl = address(new OpenEditionERC721FlatFeeHarness()); - vm.prank(deployer); - openEdition = OpenEditionERC721FlatFeeHarness( - address( - new TWProxy( - openEditionImpl, - abi.encodeCall( - OpenEditionERC721FlatFee.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ); - - receiver = new MockERC721Receiver(); - notReceiver = new MockERC721NotReceiver(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc - //////////////////////////////////////////////////////////////*/ - - function test_revert_TransferToNonReceiverContract() public { - vm.expectRevert(IERC721AUpgradeable.TransferToNonERC721ReceiverImplementer.selector); - openEdition.transferTokensOnClaim(address(notReceiver), 1); - } - - function test_state_transferToReceiverContract() public { - uint256 receiverBalanceBefore = openEdition.balanceOf(address(receiver)); - uint256 nextTokenToMintBefore = openEdition.nextTokenIdToMint(); - - openEdition.transferTokensOnClaim(address(receiver), 1); - - uint256 receiverBalanceAfter = openEdition.balanceOf(address(receiver)); - uint256 nextTokenToMintAfter = openEdition.nextTokenIdToMint(); - - assertEq(receiverBalanceAfter, receiverBalanceBefore + 1); - assertEq(nextTokenToMintAfter, nextTokenToMintBefore + 1); - } - - function test_state_transferToEOA() public { - address to = address(0x01); - uint256 receiverBalanceBefore = openEdition.balanceOf(to); - uint256 nextTokenToMintBefore = openEdition.nextTokenIdToMint(); - - openEdition.transferTokensOnClaim(to, 1); - - uint256 receiverBalanceAfter = openEdition.balanceOf(to); - uint256 nextTokenToMintAfter = openEdition.nextTokenIdToMint(); - - assertEq(receiverBalanceAfter, receiverBalanceBefore + 1); - assertEq(nextTokenToMintAfter, nextTokenToMintBefore + 1); - } -} diff --git a/src/test/open-edition-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.tree b/src/test/open-edition-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.tree deleted file mode 100644 index bddcf87f6..000000000 --- a/src/test/open-edition-flat-fee/_transferTokensOnClaim/_transferTokensOnClaim.tree +++ /dev/null @@ -1,8 +0,0 @@ -function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) -├── when _to is a smart contract -│ ├── when _to has not implemented ERC721Receiver -│ │ └── it should revert ✅ -│ └── when _to has implemented ERC721Receiver -│ └── it should mint _quantityBeingClaimed tokens to _to ✅ -└── when _to is an EOA - └── it should mint _quantityBeingClaimed tokens to _to ✅ \ No newline at end of file diff --git a/src/test/open-edition-flat-fee/initialize/initialize.t.sol b/src/test/open-edition-flat-fee/initialize/initialize.t.sol deleted file mode 100644 index 7e60c0903..000000000 --- a/src/test/open-edition-flat-fee/initialize/initialize.t.sol +++ /dev/null @@ -1,255 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { OpenEditionERC721FlatFee, Royalty } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "src/test/utils/BaseTest.sol"; - -contract OpenEditionERC721FlatFeeTest_initialize is BaseTest { - event ContractURIUpdated(string prevURI, string newURI); - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - event PrimarySaleRecipientUpdated(address indexed recipient); - - OpenEditionERC721FlatFee public openEdition; - - address private openEditionImpl; - - function deployOpenEdition( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _saleRecipient, - address _royaltyRecipient, - uint128 _royaltyBps, - uint128 _platformFeeBps, - address _platformFeeRecipient, - address _imp - ) public { - vm.prank(deployer); - openEdition = OpenEditionERC721FlatFee( - address( - new TWProxy( - _imp, - abi.encodeCall( - OpenEditionERC721FlatFee.initialize, - ( - _defaultAdmin, - _name, - _symbol, - _contractURI, - _trustedForwarders, - _saleRecipient, - _royaltyRecipient, - _royaltyBps, - _platformFeeBps, - _platformFeeRecipient - ) - ) - ) - ) - ); - } - - function setUp() public override { - super.setUp(); - openEditionImpl = address(new OpenEditionERC721FlatFee()); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: initialize - //////////////////////////////////////////////////////////////*/ - - function test_state() public { - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient, - openEditionImpl - ); - - address _saleRecipient = openEdition.primarySaleRecipient(); - (address _royaltyRecipient, uint16 _royaltyBps) = openEdition.getDefaultRoyaltyInfo(); - string memory _name = openEdition.name(); - string memory _symbol = openEdition.symbol(); - string memory _contractURI = openEdition.contractURI(); - address _owner = openEdition.owner(); - - assertEq(_name, NAME); - assertEq(_symbol, SYMBOL); - assertEq(_contractURI, CONTRACT_URI); - assertEq(_saleRecipient, saleRecipient); - assertEq(_royaltyRecipient, royaltyRecipient); - assertEq(_royaltyBps, royaltyBps); - assertEq(_owner, deployer); - - for (uint256 i = 0; i < forwarders().length; i++) { - assertEq(openEdition.isTrustedForwarder(forwarders()[i]), true); - } - - assertTrue(openEdition.hasRole(openEdition.DEFAULT_ADMIN_ROLE(), deployer)); - assertTrue(openEdition.hasRole(keccak256("TRANSFER_ROLE"), deployer)); - assertTrue(openEdition.hasRole(keccak256("MINTER_ROLE"), deployer)); - assertTrue(openEdition.hasRole(keccak256("TRANSFER_ROLE"), address(0))); - } - - function test_revert_RoyaltyTooHigh() public { - uint128 _royaltyBps = 10001; - - vm.expectRevert(abi.encodeWithSelector(Royalty.RoyaltyExceededMaxFeeBps.selector, 10_000, _royaltyBps)); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - _royaltyBps, - platformFeeBps, - platformFeeRecipient, - openEditionImpl - ); - } - - function test_event_ContractURIUpdated() public { - vm.expectEmit(false, false, false, true); - emit ContractURIUpdated("", CONTRACT_URI); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient, - openEditionImpl - ); - } - - function test_event_OwnerUpdated() public { - vm.expectEmit(true, true, false, false); - emit OwnerUpdated(address(0), deployer); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient, - openEditionImpl - ); - } - - function test_event_TransferRoleAddressZero() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - vm.expectEmit(true, true, false, false); - emit RoleGranted(role, address(0), deployer); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient, - openEditionImpl - ); - } - - function test_event_TransferRoleAdmin() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - vm.expectEmit(true, true, false, false); - emit RoleGranted(role, deployer, deployer); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient, - openEditionImpl - ); - } - - function test_event_MinterRoleAdmin() public { - bytes32 role = keccak256("MINTER_ROLE"); - vm.expectEmit(true, true, false, false); - emit RoleGranted(role, deployer, deployer); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient, - openEditionImpl - ); - } - - function test_event_DefaultAdminRoleAdmin() public { - bytes32 role = bytes32(0x00); - vm.expectEmit(true, true, false, false); - emit RoleGranted(role, deployer, deployer); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient, - openEditionImpl - ); - } - - function test_event_PrimarysaleRecipientUpdated() public { - vm.expectEmit(true, false, false, false); - emit PrimarySaleRecipientUpdated(saleRecipient); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient, - openEditionImpl - ); - } -} diff --git a/src/test/open-edition-flat-fee/initialize/initialize.tree b/src/test/open-edition-flat-fee/initialize/initialize.tree deleted file mode 100644 index f56ad144b..000000000 --- a/src/test/open-edition-flat-fee/initialize/initialize.tree +++ /dev/null @@ -1,39 +0,0 @@ -function initialize( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _saleRecipient, - address _royaltyRecipient, - uint128 _royaltyBps, - uint128 _platformFeeBps, - address _platformFeeRecipient -) -├── when _trustedForwarders.length > 0 -│ └── it should set _trustedForwarder[_trustedForwarders[i]] as true for each address in _trustedForwarders ✅ -├── it should set _name as the value provided in _name ✅ -├── it should set _symbol as the value provided in _symbol ✅ -├── it should set _currentIndex as 0 ✅ -├── it should set contractURI as _contractURI ✅ -├── it should emit ContractURIUpdated with the parameters: prevURI, _uri ✅ -├── it should set _defaultAdmin as the owner of the contract ✅ -├── it should emit OwnerUpdated with the parameters: _prevOwner, _defaultAdmin ✅ -├── it should assign the role DEFAULT_ADMIN_ROLE to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: DEFAULT_ADMIN_ROLE, _defaultAdmin, msg.sender ✅ -├── it should assign the role _minterRole to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: _minterRole, _defaultAdmin, msg.sender ✅ -├── it should assign the role _transferRole to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: _transferRole, _defaultAdmin, msg.sender ✅ -├── it should assign the role _transferRole to address(0) ✅ -├── it should emit RoleGranted with the parameters: _transferRole, address(0), msg.sender ✅ -├── when _royaltyBps is greater than 10_000 -│ └── it should revert ✅ -├── when _royaltyBps is less than or equal to 10_000 -│ ├── it should set royaltyRecipient as _royaltyRecipient ✅ -│ ├── it should set royaltyBps as uint16(_royaltyBps) ✅ -│ └── it should emit DefaultRoyalty with the parameters _royaltyRecipient, _royaltyBps -├── it should set recipient as _primarySaleRecipient ✅ -├── it should emit PrimarySaleRecipientUpdated with the parameters _primarySaleRecipient ✅ -├── it should set transferRole as keccak256("TRANSFER_ROLE") ✅ -└── it should set minterRole as keccak256("MINTER_ROLE") ✅ diff --git a/src/test/open-edition-flat-fee/misc/misc.t.sol b/src/test/open-edition-flat-fee/misc/misc.t.sol deleted file mode 100644 index 760373600..000000000 --- a/src/test/open-edition-flat-fee/misc/misc.t.sol +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { IERC721AUpgradeable, OpenEditionERC721FlatFee, ISharedMetadata } from "contracts/prebuilts/open-edition/OpenEditionERC721FlatFee.sol"; -import { NFTMetadataRenderer } from "contracts/lib/NFTMetadataRenderer.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "src/test/utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; - -contract HarnessOpenEditionERC721FlatFee is OpenEditionERC721FlatFee { - function msgData() public view returns (bytes memory) { - return _msgData(); - } -} - -contract OpenEditionERC721FlatFeeTest_misc is BaseTest { - OpenEditionERC721FlatFee public openEdition; - HarnessOpenEditionERC721FlatFee public harnessOpenEdition; - - address private openEditionImpl; - address private harnessImpl; - - address private receiver = 0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd; - - ISharedMetadata.SharedMetadataInfo public sharedMetadata; - - function setUp() public override { - super.setUp(); - openEditionImpl = address(new OpenEditionERC721FlatFee()); - vm.prank(deployer); - openEdition = OpenEditionERC721FlatFee( - address( - new TWProxy( - openEditionImpl, - abi.encodeCall( - OpenEditionERC721FlatFee.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ); - - sharedMetadata = ISharedMetadata.SharedMetadataInfo({ - name: "Test", - description: "Test", - imageURI: "https://test.com", - animationURI: "https://test.com" - }); - } - - function deployHarness() internal { - harnessImpl = address(new HarnessOpenEditionERC721FlatFee()); - harnessOpenEdition = HarnessOpenEditionERC721FlatFee( - address( - new TWProxy( - harnessImpl, - abi.encodeCall( - OpenEditionERC721FlatFee.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc - //////////////////////////////////////////////////////////////*/ - - modifier claimTokens() { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721FlatFee.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - OpenEditionERC721FlatFee.ClaimCondition[] memory conditions = new OpenEditionERC721FlatFee.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.prank(receiver, receiver); - openEdition.claim(receiver, 100, address(erc20), 0, alp, ""); - _; - } - - modifier callerOwner() { - vm.startPrank(receiver); - _; - } - - modifier callerNotOwner() { - _; - } - - function test_tokenURI_revert_tokenDoesNotExist() public { - vm.expectRevert(bytes("!ID")); - openEdition.tokenURI(1); - } - - function test_tokenURI_returnMetadata() public claimTokens { - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - - string memory uri = openEdition.tokenURI(1); - assertEq( - uri, - NFTMetadataRenderer.createMetadataEdition({ - name: sharedMetadata.name, - description: sharedMetadata.description, - imageURI: sharedMetadata.imageURI, - animationURI: sharedMetadata.animationURI, - tokenOfEdition: 1 - }) - ); - } - - function test_startTokenId_returnOne() public { - assertEq(openEdition.startTokenId(), 1); - } - - function test_totalMinted_returnZero() public { - assertEq(openEdition.totalMinted(), 0); - } - - function test_totalMinted_returnOneHundred() public claimTokens { - assertEq(openEdition.totalMinted(), 100); - } - - function test_nextTokenIdToMint_returnOne() public { - assertEq(openEdition.nextTokenIdToMint(), 1); - } - - function test_nextTokenIdToMint_returnOneHundredAndOne() public claimTokens { - assertEq(openEdition.nextTokenIdToMint(), 101); - } - - function test_nextTokenIdToClaim_returnOne() public { - assertEq(openEdition.nextTokenIdToClaim(), 1); - } - - function test_nextTokenIdToClaim_returnOneHundredAndOne() public claimTokens { - assertEq(openEdition.nextTokenIdToClaim(), 101); - } - - function test_burn_revert_callerNotOwner() public claimTokens callerNotOwner { - vm.expectRevert(IERC721AUpgradeable.TransferCallerNotOwnerNorApproved.selector); - openEdition.burn(1); - } - - function test_burn_state_callerOwner() public claimTokens callerOwner { - uint256 balanceBeforeBurn = openEdition.balanceOf(receiver); - - openEdition.burn(1); - - uint256 balanceAfterBurn = openEdition.balanceOf(receiver); - - assertEq(balanceBeforeBurn - balanceAfterBurn, 1); - } - - function test_burn_state_callerApproved() public claimTokens { - uint256 balanceBeforeBurn = openEdition.balanceOf(receiver); - - vm.prank(receiver); - openEdition.setApprovalForAll(deployer, true); - - vm.prank(deployer); - openEdition.burn(1); - - uint256 balanceAfterBurn = openEdition.balanceOf(receiver); - - assertEq(balanceBeforeBurn - balanceAfterBurn, 1); - } - - function test_supportsInterface() public { - assertEq(openEdition.supportsInterface(type(IERC2981Upgradeable).interfaceId), true); - bytes4 invalidId = bytes4(0); - assertEq(openEdition.supportsInterface(invalidId), false); - } - - function test_msgData_returnValue() public { - deployHarness(); - bytes memory msgData = harnessOpenEdition.msgData(); - bytes4 expectedData = harnessOpenEdition.msgData.selector; - assertEq(bytes4(msgData), expectedData); - } -} diff --git a/src/test/open-edition-flat-fee/misc/misc.tree b/src/test/open-edition-flat-fee/misc/misc.tree deleted file mode 100644 index 07abb950c..000000000 --- a/src/test/open-edition-flat-fee/misc/misc.tree +++ /dev/null @@ -1,33 +0,0 @@ -function tokenURI(uint256 _tokenId) -├── when _tokenId does not exist -│ └── it should revert ✅ -└── when _tokenID does exist - └── it should return the shared metadata ✅ - -function supportsInterface(bytes4 interfaceId) -├── it should return true for any of the listed interface ids ✅ -└── it should return false for any interfaces ids that are not listed ✅ - -function _startTokenId() -└── it should return 1 ✅ - -function startTokenId() -└── it should return _startTokenId (1) ✅ - -function totalminted() -└── it should return the total number of NFTs minted ✅ - -function nextTokenIdToMint() -└── it should return the next token ID to mint (last minted + 1) ✅ - -function nextTokenIdToClaim() -└── it should return the next token ID to mint (last minted + 1) ✅ - -function burn(uint256 tokenId) -├── when caller is not the owner of tokenId -│ ├── when caller is not an approved operator of the owner of tokenId -│ │ └── it should revert ✅ -│ └── when caller is an approved operator of the owner of tokenId -│ └── it should burn the token ✅ -└── when caller is the owner of tokenId - └── it should burn the token ✅ \ No newline at end of file diff --git a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol deleted file mode 100644 index 1ed885681..000000000 --- a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { OpenEditionERC721 } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "src/test/utils/BaseTest.sol"; - -contract OpenEditionERC721Harness is OpenEditionERC721 { - function beforeTokenTransfers(address from, address to, uint256 startTokenId_, uint256 quantity) public { - _beforeTokenTransfers(from, to, startTokenId_, quantity); - } -} - -contract OpenEditionERC721Test_beforeTokenTransfers is BaseTest { - OpenEditionERC721Harness public openEdition; - - address private openEditionImpl; - - function setUp() public override { - super.setUp(); - openEditionImpl = address(new OpenEditionERC721Harness()); - vm.prank(deployer); - openEdition = OpenEditionERC721Harness( - address( - new TWProxy( - openEditionImpl, - abi.encodeCall( - OpenEditionERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps - ) - ) - ) - ) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc - //////////////////////////////////////////////////////////////*/ - - function test_revert_transfersRestricted() public { - address from = address(0x1); - address to = address(0x2); - bytes32 role = keccak256("TRANSFER_ROLE"); - vm.prank(deployer); - openEdition.revokeRole(role, address(0)); - - vm.expectRevert(bytes("!T")); - openEdition.beforeTokenTransfers(from, to, 0, 1); - } -} diff --git a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree deleted file mode 100644 index 078bd6a79..000000000 --- a/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree +++ /dev/null @@ -1,12 +0,0 @@ -function _beforeTokenTransfers( - address from, - address to, - uint256 startTokenId_, - uint256 quantity -) -└── when address(0) does not have the transfer role - └── when from does not equal address(0) - └── when to does not equal address(0) - └── when from does not have the transfer role - └── when to does not have the transfer role - └── it should revert ✅ diff --git a/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol b/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol deleted file mode 100644 index 6903bdae7..000000000 --- a/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { OpenEditionERC721 } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "src/test/utils/BaseTest.sol"; - -contract OpenEditionERC721Harness is OpenEditionERC721 { - function canSetPrimarySaleRecipient() external view returns (bool) { - return _canSetPrimarySaleRecipient(); - } - - function canSetOwner() external view returns (bool) { - return _canSetOwner(); - } - - /// @dev Checks whether royalty info can be set in the given execution context. - function canSetRoyaltyInfo() external view returns (bool) { - return _canSetRoyaltyInfo(); - } - - /// @dev Checks whether contract metadata can be set in the given execution context. - function canSetContractURI() external view returns (bool) { - return _canSetContractURI(); - } - - /// @dev Checks whether platform fee info can be set in the given execution context. - function canSetClaimConditions() external view returns (bool) { - return _canSetClaimConditions(); - } - - /// @dev Returns whether the shared metadata of tokens can be set in the given execution context. - function canSetSharedMetadata() external view virtual returns (bool) { - return _canSetSharedMetadata(); - } -} - -contract OpenEditionERC721Test_canSetFunctions is BaseTest { - OpenEditionERC721Harness public openEdition; - - address private openEditionImpl; - - function setUp() public override { - super.setUp(); - openEditionImpl = address(new OpenEditionERC721Harness()); - vm.prank(deployer); - openEdition = OpenEditionERC721Harness( - address( - new TWProxy( - openEditionImpl, - abi.encodeCall( - OpenEditionERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps - ) - ) - ) - ) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc - //////////////////////////////////////////////////////////////*/ - - function test_canSetPrimarySaleRecipient_returnTrue() public { - vm.prank(deployer); - assertTrue(openEdition.canSetPrimarySaleRecipient()); - } - - function test_canSetPrimarySaleRecipient_returnFalse() public { - assertFalse(openEdition.canSetPrimarySaleRecipient()); - } - - function test_canSetOwner_returnTrue() public { - vm.prank(deployer); - assertTrue(openEdition.canSetOwner()); - } - - function test_canSetOwner_returnFalse() public { - assertFalse(openEdition.canSetOwner()); - } - - function test_canSetRoyaltyInfo_returnTrue() public { - vm.prank(deployer); - assertTrue(openEdition.canSetRoyaltyInfo()); - } - - function test_canSetRoyaltyInfo_returnFalse() public { - assertFalse(openEdition.canSetRoyaltyInfo()); - } - - function test_canSetContractURI_returnTrue() public { - vm.prank(deployer); - assertTrue(openEdition.canSetContractURI()); - } - - function test_canSetContractURI_returnFalse() public { - assertFalse(openEdition.canSetContractURI()); - } - - function test_canSetClaimConditions_returnTrue() public { - vm.prank(deployer); - assertTrue(openEdition.canSetClaimConditions()); - } - - function test_canSetClaimConditions_returnFalse() public { - assertFalse(openEdition.canSetClaimConditions()); - } - - function test_canSetSharedMetadata_returnTrue() public { - vm.prank(deployer); - assertTrue(openEdition.canSetSharedMetadata()); - } - - function test_canSetSharedMetadata_returnFalse() public { - assertFalse(openEdition.canSetSharedMetadata()); - } -} diff --git a/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree b/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree deleted file mode 100644 index 1ccb478fd..000000000 --- a/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree +++ /dev/null @@ -1,39 +0,0 @@ -function _canSetPrimarySaleRecipient() -├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - -function _canSetOwner() -├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - - -function _canSetRoyaltyInfo() -├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - - -function _canSetContractURI() -├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - - -function _canSetClaimConditions() -├── when _msgSender has DEFAULT_ADMIN_ROLE -│ └── it should return true ✅ -└── when _msgSender does not have DEFAULT_ADMIN_ROLE - └── it should return false ✅ - - -function _canSetSharedMetadata() -├── when _msgSender has minter role -│ └── it should return true ✅ -└── when _msgSender does not have minter role - └── it should return false ✅ diff --git a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol deleted file mode 100644 index 5b49bcdd7..000000000 --- a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol +++ /dev/null @@ -1,203 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { OpenEditionERC721 } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "src/test/utils/BaseTest.sol"; - -contract OpenEditionERC721Harness is OpenEditionERC721 { - function collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) external payable { - _collectPriceOnClaim(_primarySaleRecipient, _quantityToClaim, _currency, _pricePerToken); - } -} - -contract OpenEditionERC721Test_collectPrice is BaseTest { - OpenEditionERC721Harness public openEdition; - - address private openEditionImpl; - - address private currency; - address private primarySaleRecipient; - uint256 private msgValue; - uint256 private pricePerToken; - uint256 private qty = 1; - - function setUp() public override { - super.setUp(); - openEditionImpl = address(new OpenEditionERC721Harness()); - vm.prank(deployer); - openEdition = OpenEditionERC721Harness( - address( - new TWProxy( - openEditionImpl, - abi.encodeCall( - OpenEditionERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps - ) - ) - ) - ) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc - //////////////////////////////////////////////////////////////*/ - - modifier pricePerTokenZero() { - _; - } - - modifier pricePerTokenNotZero() { - pricePerToken = 1 ether; - _; - } - - modifier msgValueZero() { - _; - } - - modifier msgValueNotZero() { - msgValue = 1 ether; - _; - } - - modifier valuePriceMismatch() { - msgValue = 1 ether; - pricePerToken = 2 ether; - _; - } - - modifier primarySaleRecipientZeroAddress() { - primarySaleRecipient = address(0); - _; - } - - modifier primarySaleRecipientNotZeroAddress() { - primarySaleRecipient = address(0x0999); - _; - } - - modifier currencyNativeToken() { - currency = NATIVE_TOKEN; - _; - } - - modifier currencyNotNativeToken() { - currency = address(erc20); - _; - } - - function test_revert_pricePerTokenZeroMsgValueNotZero() public pricePerTokenZero msgValueNotZero { - vm.expectRevert("!Value"); - openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); - } - - function test_revert_nativeCurrencyValuePriceMismatch() public currencyNativeToken valuePriceMismatch { - vm.expectRevert(bytes("!V")); - openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); - } - - function test_revert_erc20ValuePriceMismatch() public currencyNotNativeToken valuePriceMismatch { - vm.expectRevert(bytes("!V")); - openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); - } - - function test_state_nativeCurrency() - public - currencyNativeToken - pricePerTokenNotZero - msgValueNotZero - primarySaleRecipientNotZeroAddress - { - uint256 beforeBalancePrimarySaleRecipient = address(primarySaleRecipient).balance; - - openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); - - uint256 afterBalancePrimarySaleRecipient = address(primarySaleRecipient).balance; - - uint256 primarySaleRecipientVal = msgValue; - - assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); - } - - function test_revert_erc20_msgValueNotZero() - public - currencyNotNativeToken - msgValueNotZero - primarySaleRecipientNotZeroAddress - { - vm.expectRevert("!Value"); - openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); - } - - function test_state_erc20() public currencyNotNativeToken pricePerTokenNotZero primarySaleRecipientNotZeroAddress { - erc20.mint(address(this), pricePerToken); - ERC20(erc20).approve(address(openEdition), pricePerToken); - uint256 beforeBalancePrimarySaleRecipient = erc20.balanceOf(primarySaleRecipient); - - openEdition.collectPriceOnClaim(primarySaleRecipient, qty, currency, pricePerToken); - - uint256 afterBalancePrimarySaleRecipient = erc20.balanceOf(primarySaleRecipient); - - uint256 primarySaleRecipientVal = 1 ether; - - assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); - } - - function test_state_erc20StoredPrimarySaleRecipient() - public - currencyNotNativeToken - pricePerTokenNotZero - primarySaleRecipientZeroAddress - { - address storedPrimarySaleRecipient = openEdition.primarySaleRecipient(); - - erc20.mint(address(this), pricePerToken); - ERC20(erc20).approve(address(openEdition), pricePerToken); - uint256 beforeBalancePrimarySaleRecipient = erc20.balanceOf(storedPrimarySaleRecipient); - - openEdition.collectPriceOnClaim(primarySaleRecipient, qty, currency, pricePerToken); - - uint256 afterBalancePrimarySaleRecipient = erc20.balanceOf(storedPrimarySaleRecipient); - - uint256 primarySaleRecipientVal = 1 ether; - - assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); - } - - function test_state_nativeCurrencyStoredPrimarySaleRecipient() - public - currencyNativeToken - pricePerTokenNotZero - primarySaleRecipientZeroAddress - msgValueNotZero - { - address storedPrimarySaleRecipient = openEdition.primarySaleRecipient(); - - uint256 beforeBalancePrimarySaleRecipient = address(storedPrimarySaleRecipient).balance; - - openEdition.collectPriceOnClaim{ value: msgValue }(primarySaleRecipient, qty, currency, pricePerToken); - - uint256 afterBalancePrimarySaleRecipient = address(storedPrimarySaleRecipient).balance; - - uint256 primarySaleRecipientVal = msgValue; - - assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); - } -} diff --git a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree deleted file mode 100644 index 2054cf049..000000000 --- a/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree +++ /dev/null @@ -1,37 +0,0 @@ -function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken -) -├── when _pricePerToken is equal to zero -│ ├── when msg.value does not equal to zero -│ │ └── it should revert ✅ -│ └── when msg.value is equal to zero -│ └── it should return ✅ -└── when _pricePerToken is not equal to zero - ├── when _primarySaleRecipient is equal to address(0) - │ ├── when saleRecipient for _tokenId is equal to address(0) - │ │ ├── when currency is native token - │ │ │ ├── when msg.value does not equal totalPrice - │ │ │ │ └── it should revert ✅ - │ │ │ └── when msg.value does equal totalPrice - │ │ │ └── it should transfer totalPrice to primarySaleRecipient in native token ✅ - │ │ └── when currency is not native token - │ │ └── it should transfer totalPrice to primarySaleRecipient in _currency token ✅ - │ └── when salerecipient for _tokenId is not equal to address(0) - │ ├── when currency is native token - │ │ ├── when msg.value does not equal totalPrice - │ │ │ └── it should revert ✅ - │ │ └── when msg.value does equal totalPrice - │ │ └── it should transfer totalPrice to saleRecipient for _tokenId in native token ✅ - │ └── when currency is not native token - │ └── it should transfer totalPrice to saleRecipient for _tokenId in _currency token ✅ - └── when _primarySaleRecipient is not equal to address(0) - ├── when currency is native token - │ ├── when msg.value does not equal totalPrice - │ │ └── it should revert ✅ - │ └── when msg.value does equal totalPrice - │ └── it should transfer totalPrice to _primarySaleRecipient in native token ✅ - └── when currency is not native token - └── it should transfer totalPrice to _primarySaleRecipient in _currency token ✅ \ No newline at end of file diff --git a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol deleted file mode 100644 index d0d2f2173..000000000 --- a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { OpenEditionERC721, IERC721AUpgradeable } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "src/test/utils/BaseTest.sol"; - -contract OpenEditionERC721Harness is OpenEditionERC721 { - function transferTokensOnClaim(address _to, uint256 quantityBeingClaimed) public { - _transferTokensOnClaim(_to, quantityBeingClaimed); - } -} - -contract MockERC721Receiver { - function onERC721Received(address, address, uint256, bytes memory) external pure returns (bytes4) { - return this.onERC721Received.selector; - } -} - -contract MockERC721NotReceiver {} - -contract OpenEditionERC721Test_transferTokensOnClaim is BaseTest { - OpenEditionERC721Harness public openEdition; - - MockERC721NotReceiver private notReceiver; - MockERC721Receiver private receiver; - - address private openEditionImpl; - - function setUp() public override { - super.setUp(); - openEditionImpl = address(new OpenEditionERC721Harness()); - vm.prank(deployer); - openEdition = OpenEditionERC721Harness( - address( - new TWProxy( - openEditionImpl, - abi.encodeCall( - OpenEditionERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps - ) - ) - ) - ) - ); - - receiver = new MockERC721Receiver(); - notReceiver = new MockERC721NotReceiver(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc - //////////////////////////////////////////////////////////////*/ - - function test_revert_TransferToNonReceiverContract() public { - vm.expectRevert(IERC721AUpgradeable.TransferToNonERC721ReceiverImplementer.selector); - openEdition.transferTokensOnClaim(address(notReceiver), 1); - } - - function test_state_transferToReceiverContract() public { - uint256 receiverBalanceBefore = openEdition.balanceOf(address(receiver)); - uint256 nextTokenToMintBefore = openEdition.nextTokenIdToMint(); - - openEdition.transferTokensOnClaim(address(receiver), 1); - - uint256 receiverBalanceAfter = openEdition.balanceOf(address(receiver)); - uint256 nextTokenToMintAfter = openEdition.nextTokenIdToMint(); - - assertEq(receiverBalanceAfter, receiverBalanceBefore + 1); - assertEq(nextTokenToMintAfter, nextTokenToMintBefore + 1); - } - - function test_state_transferToEOA() public { - address to = address(0x01); - uint256 receiverBalanceBefore = openEdition.balanceOf(to); - uint256 nextTokenToMintBefore = openEdition.nextTokenIdToMint(); - - openEdition.transferTokensOnClaim(to, 1); - - uint256 receiverBalanceAfter = openEdition.balanceOf(to); - uint256 nextTokenToMintAfter = openEdition.nextTokenIdToMint(); - - assertEq(receiverBalanceAfter, receiverBalanceBefore + 1); - assertEq(nextTokenToMintAfter, nextTokenToMintBefore + 1); - } -} diff --git a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree deleted file mode 100644 index bddcf87f6..000000000 --- a/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree +++ /dev/null @@ -1,8 +0,0 @@ -function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) -├── when _to is a smart contract -│ ├── when _to has not implemented ERC721Receiver -│ │ └── it should revert ✅ -│ └── when _to has implemented ERC721Receiver -│ └── it should mint _quantityBeingClaimed tokens to _to ✅ -└── when _to is an EOA - └── it should mint _quantityBeingClaimed tokens to _to ✅ \ No newline at end of file diff --git a/src/test/open-edition/initialize/initialize.t.sol b/src/test/open-edition/initialize/initialize.t.sol deleted file mode 100644 index 9f759ffb6..000000000 --- a/src/test/open-edition/initialize/initialize.t.sol +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { OpenEditionERC721, Royalty } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "src/test/utils/BaseTest.sol"; - -contract OpenEditionERC721Test_initialize is BaseTest { - event ContractURIUpdated(string prevURI, string newURI); - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - event PrimarySaleRecipientUpdated(address indexed recipient); - - OpenEditionERC721 public openEdition; - - address private openEditionImpl; - - function deployOpenEdition( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _saleRecipient, - address _royaltyRecipient, - uint128 _royaltyBps, - address _imp - ) public { - vm.prank(deployer); - openEdition = OpenEditionERC721( - address( - new TWProxy( - _imp, - abi.encodeCall( - OpenEditionERC721.initialize, - ( - _defaultAdmin, - _name, - _symbol, - _contractURI, - _trustedForwarders, - _saleRecipient, - _royaltyRecipient, - _royaltyBps - ) - ) - ) - ) - ); - } - - function setUp() public override { - super.setUp(); - openEditionImpl = address(new OpenEditionERC721()); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: initialize - //////////////////////////////////////////////////////////////*/ - - function test_state() public { - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - openEditionImpl - ); - - address _saleRecipient = openEdition.primarySaleRecipient(); - (address _royaltyRecipient, uint16 _royaltyBps) = openEdition.getDefaultRoyaltyInfo(); - string memory _name = openEdition.name(); - string memory _symbol = openEdition.symbol(); - string memory _contractURI = openEdition.contractURI(); - address _owner = openEdition.owner(); - - assertEq(_name, NAME); - assertEq(_symbol, SYMBOL); - assertEq(_contractURI, CONTRACT_URI); - assertEq(_saleRecipient, saleRecipient); - assertEq(_royaltyRecipient, royaltyRecipient); - assertEq(_royaltyBps, royaltyBps); - assertEq(_owner, deployer); - - for (uint256 i = 0; i < forwarders().length; i++) { - assertEq(openEdition.isTrustedForwarder(forwarders()[i]), true); - } - - assertTrue(openEdition.hasRole(openEdition.DEFAULT_ADMIN_ROLE(), deployer)); - assertTrue(openEdition.hasRole(keccak256("TRANSFER_ROLE"), deployer)); - assertTrue(openEdition.hasRole(keccak256("MINTER_ROLE"), deployer)); - assertTrue(openEdition.hasRole(keccak256("TRANSFER_ROLE"), address(0))); - } - - function test_revert_RoyaltyTooHigh() public { - uint128 _royaltyBps = 10001; - - vm.expectRevert(abi.encodeWithSelector(Royalty.RoyaltyExceededMaxFeeBps.selector, 10_000, _royaltyBps)); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - _royaltyBps, - openEditionImpl - ); - } - - function test_event_ContractURIUpdated() public { - vm.expectEmit(false, false, false, true); - emit ContractURIUpdated("", CONTRACT_URI); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - openEditionImpl - ); - } - - function test_event_OwnerUpdated() public { - vm.expectEmit(true, true, false, false); - emit OwnerUpdated(address(0), deployer); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - openEditionImpl - ); - } - - function test_event_TransferRoleAddressZero() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - vm.expectEmit(true, true, false, false); - emit RoleGranted(role, address(0), deployer); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - openEditionImpl - ); - } - - function test_event_TransferRoleAdmin() public { - bytes32 role = keccak256("TRANSFER_ROLE"); - vm.expectEmit(true, true, false, false); - emit RoleGranted(role, deployer, deployer); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - openEditionImpl - ); - } - - function test_event_MinterRoleAdmin() public { - bytes32 role = keccak256("MINTER_ROLE"); - vm.expectEmit(true, true, false, false); - emit RoleGranted(role, deployer, deployer); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - openEditionImpl - ); - } - - function test_event_DefaultAdminRoleAdmin() public { - bytes32 role = bytes32(0x00); - vm.expectEmit(true, true, false, false); - emit RoleGranted(role, deployer, deployer); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - openEditionImpl - ); - } - - function test_event_PrimarysaleRecipientUpdated() public { - vm.expectEmit(true, false, false, false); - emit PrimarySaleRecipientUpdated(saleRecipient); - deployOpenEdition( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - openEditionImpl - ); - } -} diff --git a/src/test/open-edition/initialize/initialize.tree b/src/test/open-edition/initialize/initialize.tree deleted file mode 100644 index f56ad144b..000000000 --- a/src/test/open-edition/initialize/initialize.tree +++ /dev/null @@ -1,39 +0,0 @@ -function initialize( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _saleRecipient, - address _royaltyRecipient, - uint128 _royaltyBps, - uint128 _platformFeeBps, - address _platformFeeRecipient -) -├── when _trustedForwarders.length > 0 -│ └── it should set _trustedForwarder[_trustedForwarders[i]] as true for each address in _trustedForwarders ✅ -├── it should set _name as the value provided in _name ✅ -├── it should set _symbol as the value provided in _symbol ✅ -├── it should set _currentIndex as 0 ✅ -├── it should set contractURI as _contractURI ✅ -├── it should emit ContractURIUpdated with the parameters: prevURI, _uri ✅ -├── it should set _defaultAdmin as the owner of the contract ✅ -├── it should emit OwnerUpdated with the parameters: _prevOwner, _defaultAdmin ✅ -├── it should assign the role DEFAULT_ADMIN_ROLE to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: DEFAULT_ADMIN_ROLE, _defaultAdmin, msg.sender ✅ -├── it should assign the role _minterRole to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: _minterRole, _defaultAdmin, msg.sender ✅ -├── it should assign the role _transferRole to _defaultAdmin ✅ -├── it should emit RoleGranted with the parameters: _transferRole, _defaultAdmin, msg.sender ✅ -├── it should assign the role _transferRole to address(0) ✅ -├── it should emit RoleGranted with the parameters: _transferRole, address(0), msg.sender ✅ -├── when _royaltyBps is greater than 10_000 -│ └── it should revert ✅ -├── when _royaltyBps is less than or equal to 10_000 -│ ├── it should set royaltyRecipient as _royaltyRecipient ✅ -│ ├── it should set royaltyBps as uint16(_royaltyBps) ✅ -│ └── it should emit DefaultRoyalty with the parameters _royaltyRecipient, _royaltyBps -├── it should set recipient as _primarySaleRecipient ✅ -├── it should emit PrimarySaleRecipientUpdated with the parameters _primarySaleRecipient ✅ -├── it should set transferRole as keccak256("TRANSFER_ROLE") ✅ -└── it should set minterRole as keccak256("MINTER_ROLE") ✅ diff --git a/src/test/open-edition/misc/misc.t.sol b/src/test/open-edition/misc/misc.t.sol deleted file mode 100644 index 44e6b20cb..000000000 --- a/src/test/open-edition/misc/misc.t.sol +++ /dev/null @@ -1,229 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { IERC721AUpgradeable, OpenEditionERC721, ISharedMetadata } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; -import { NFTMetadataRenderer } from "contracts/lib/NFTMetadataRenderer.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Test imports -import "src/test/utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; - -contract HarnessOpenEditionERC721 is OpenEditionERC721 { - function msgData() public view returns (bytes memory) { - return _msgData(); - } -} - -contract OpenEditionERC721Test_misc is BaseTest { - OpenEditionERC721 public openEdition; - HarnessOpenEditionERC721 public harnessOpenEdition; - - address private openEditionImpl; - address private harnessImpl; - - address private receiver = 0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd; - - ISharedMetadata.SharedMetadataInfo public sharedMetadata; - - function setUp() public override { - super.setUp(); - openEditionImpl = address(new OpenEditionERC721()); - vm.prank(deployer); - openEdition = OpenEditionERC721( - address( - new TWProxy( - openEditionImpl, - abi.encodeCall( - OpenEditionERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps - ) - ) - ) - ) - ); - - sharedMetadata = ISharedMetadata.SharedMetadataInfo({ - name: "Test", - description: "Test", - imageURI: "https://test.com", - animationURI: "https://test.com" - }); - } - - function deployHarness() internal { - harnessImpl = address(new HarnessOpenEditionERC721()); - harnessOpenEdition = HarnessOpenEditionERC721( - address( - new TWProxy( - harnessImpl, - abi.encodeCall( - OpenEditionERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps - ) - ) - ) - ) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: misc - //////////////////////////////////////////////////////////////*/ - - modifier claimTokens() { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "300"; - inputs[3] = "0"; - inputs[4] = Strings.toHexString(uint160(address(erc20))); // address of erc20 - - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - OpenEditionERC721.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = 300; - alp.pricePerToken = 0; - alp.currency = address(erc20); - - vm.warp(1); - - OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 10; - conditions[0].merkleRoot = root; - conditions[0].pricePerToken = 10; - conditions[0].currency = address(erc20); - - vm.prank(deployer); - openEdition.setClaimConditions(conditions, false); - - vm.prank(receiver, receiver); - openEdition.claim(receiver, 100, address(erc20), 0, alp, ""); - _; - } - - modifier callerOwner() { - vm.startPrank(receiver); - _; - } - - modifier callerNotOwner() { - _; - } - - function test_tokenURI_revert_tokenDoesNotExist() public { - vm.expectRevert(bytes("!ID")); - openEdition.tokenURI(1); - } - - function test_tokenURI_returnMetadata() public claimTokens { - vm.prank(deployer); - openEdition.setSharedMetadata(sharedMetadata); - - string memory uri = openEdition.tokenURI(1); - assertEq( - uri, - NFTMetadataRenderer.createMetadataEdition({ - name: sharedMetadata.name, - description: sharedMetadata.description, - imageURI: sharedMetadata.imageURI, - animationURI: sharedMetadata.animationURI, - tokenOfEdition: 1 - }) - ); - } - - function test_startTokenId_returnOne() public { - assertEq(openEdition.startTokenId(), 1); - } - - function test_totalMinted_returnZero() public { - assertEq(openEdition.totalMinted(), 0); - } - - function test_totalMinted_returnOneHundred() public claimTokens { - assertEq(openEdition.totalMinted(), 100); - } - - function test_nextTokenIdToMint_returnOne() public { - assertEq(openEdition.nextTokenIdToMint(), 1); - } - - function test_nextTokenIdToMint_returnOneHundredAndOne() public claimTokens { - assertEq(openEdition.nextTokenIdToMint(), 101); - } - - function test_nextTokenIdToClaim_returnOne() public { - assertEq(openEdition.nextTokenIdToClaim(), 1); - } - - function test_nextTokenIdToClaim_returnOneHundredAndOne() public claimTokens { - assertEq(openEdition.nextTokenIdToClaim(), 101); - } - - function test_burn_revert_callerNotOwner() public claimTokens callerNotOwner { - vm.expectRevert(IERC721AUpgradeable.TransferCallerNotOwnerNorApproved.selector); - openEdition.burn(1); - } - - function test_burn_state_callerOwner() public claimTokens callerOwner { - uint256 balanceBeforeBurn = openEdition.balanceOf(receiver); - - openEdition.burn(1); - - uint256 balanceAfterBurn = openEdition.balanceOf(receiver); - - assertEq(balanceBeforeBurn - balanceAfterBurn, 1); - } - - function test_burn_state_callerApproved() public claimTokens { - uint256 balanceBeforeBurn = openEdition.balanceOf(receiver); - - vm.prank(receiver); - openEdition.setApprovalForAll(deployer, true); - - vm.prank(deployer); - openEdition.burn(1); - - uint256 balanceAfterBurn = openEdition.balanceOf(receiver); - - assertEq(balanceBeforeBurn - balanceAfterBurn, 1); - } - - function test_supportsInterface() public { - assertEq(openEdition.supportsInterface(type(IERC2981Upgradeable).interfaceId), true); - bytes4 invalidId = bytes4(0); - assertEq(openEdition.supportsInterface(invalidId), false); - } - - function test_msgData_returnValue() public { - deployHarness(); - bytes memory msgData = harnessOpenEdition.msgData(); - bytes4 expectedData = harnessOpenEdition.msgData.selector; - assertEq(bytes4(msgData), expectedData); - } -} diff --git a/src/test/open-edition/misc/misc.tree b/src/test/open-edition/misc/misc.tree deleted file mode 100644 index 07abb950c..000000000 --- a/src/test/open-edition/misc/misc.tree +++ /dev/null @@ -1,33 +0,0 @@ -function tokenURI(uint256 _tokenId) -├── when _tokenId does not exist -│ └── it should revert ✅ -└── when _tokenID does exist - └── it should return the shared metadata ✅ - -function supportsInterface(bytes4 interfaceId) -├── it should return true for any of the listed interface ids ✅ -└── it should return false for any interfaces ids that are not listed ✅ - -function _startTokenId() -└── it should return 1 ✅ - -function startTokenId() -└── it should return _startTokenId (1) ✅ - -function totalminted() -└── it should return the total number of NFTs minted ✅ - -function nextTokenIdToMint() -└── it should return the next token ID to mint (last minted + 1) ✅ - -function nextTokenIdToClaim() -└── it should return the next token ID to mint (last minted + 1) ✅ - -function burn(uint256 tokenId) -├── when caller is not the owner of tokenId -│ ├── when caller is not an approved operator of the owner of tokenId -│ │ └── it should revert ✅ -│ └── when caller is an approved operator of the owner of tokenId -│ └── it should burn the token ✅ -└── when caller is the owner of tokenId - └── it should burn the token ✅ \ No newline at end of file diff --git a/src/test/pack/Pack.t.sol b/src/test/pack/Pack.t.sol deleted file mode 100644 index 37fdae635..000000000 --- a/src/test/pack/Pack.t.sol +++ /dev/null @@ -1,1253 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { Pack, IERC2981Upgradeable, IERC721Receiver, IERC1155Upgradeable } from "contracts/prebuilts/pack/Pack.sol"; -import { IPack } from "contracts/prebuilts/interface/IPack.sol"; -import { ITokenBundle } from "contracts/extension/interface/ITokenBundle.sol"; -import { CurrencyTransferLib } from "contracts/lib/CurrencyTransferLib.sol"; - -// Test imports -import { MockERC20 } from "../mocks/MockERC20.sol"; -import { Wallet } from "../utils/Wallet.sol"; -import "../utils/BaseTest.sol"; - -contract PackTest is BaseTest { - /// @notice Emitted when a set of packs is created. - event PackCreated(uint256 indexed packId, address recipient, uint256 totalPacksCreated); - - /// @notice Emitted when a pack is opened. - event PackOpened( - uint256 indexed packId, - address indexed opener, - uint256 numOfPacksOpened, - ITokenBundle.Token[] rewardUnitsDistributed - ); - - Pack internal pack; - - Wallet internal tokenOwner; - string internal packUri; - ITokenBundle.Token[] internal packContents; - ITokenBundle.Token[] internal additionalContents; - uint256[] internal numOfRewardUnits; - uint256[] internal additionalContentsRewardUnits; - - function setUp() public override { - super.setUp(); - - pack = Pack(payable(getContract("Pack"))); - - tokenOwner = getWallet(); - packUri = "ipfs://"; - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 0, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 0, - totalAmount: 100 - }) - ); - numOfRewardUnits.push(20); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }) - ); - numOfRewardUnits.push(50); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 1, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 2, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }) - ); - numOfRewardUnits.push(100); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 3, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 4, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 5, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 1, - totalAmount: 500 - }) - ); - numOfRewardUnits.push(50); - - erc20.mint(address(tokenOwner), 2000 ether); - erc721.mint(address(tokenOwner), 6); - erc1155.mint(address(tokenOwner), 0, 100); - erc1155.mint(address(tokenOwner), 1, 500); - - // additional contents, to check `addPackContents` - additionalContents.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 2, - totalAmount: 200 - }) - ); - additionalContentsRewardUnits.push(50); - - additionalContents.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }) - ); - additionalContentsRewardUnits.push(100); - - tokenOwner.setAllowanceERC20(address(erc20), address(pack), type(uint256).max); - tokenOwner.setApprovalForAllERC721(address(erc721), address(pack), true); - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(pack), true); - - vm.prank(deployer); - pack.grantRole(keccak256("MINTER_ROLE"), address(tokenOwner)); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_revert_addPackContents_RandomAccountGrief() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - // random address tries to transfer zero amount - address randomAccount = address(0x123); - vm.prank(randomAccount); - pack.safeTransferFrom(randomAccount, address(567), packId, 0, ""); // zero transfer - - // canUpdatePack should remain true, since no packs were transferred - assertTrue(pack.canUpdatePack(packId)); - - erc20.mint(address(tokenOwner), 1000 ether); - erc1155.mint(address(tokenOwner), 2, 200); - - vm.prank(address(tokenOwner)); - // Should not revert - pack.addPackContents(packId, additionalContents, additionalContentsRewardUnits, recipient); - } - - function test_checkForwarders() public { - assertFalse(pack.isTrustedForwarder(eoaForwarder)); - assertFalse(pack.isTrustedForwarder(forwarder)); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `createPack` - //////////////////////////////////////////////////////////////*/ - - function test_interface() public pure { - console2.logBytes4(type(IERC20).interfaceId); - console2.logBytes4(type(IERC721).interfaceId); - console2.logBytes4(type(IERC1155).interfaceId); - } - - function test_supportsInterface() public { - assertEq(pack.supportsInterface(type(IERC2981Upgradeable).interfaceId), true); - assertEq(pack.supportsInterface(type(IERC721Receiver).interfaceId), true); - assertEq(pack.supportsInterface(type(IERC1155Receiver).interfaceId), true); - assertEq(pack.supportsInterface(type(IERC1155Upgradeable).interfaceId), true); - } - - /** - * note: Testing state changes; token owner calls `createPack` to pack owned tokens. - */ - function test_state_createPack() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - assertEq(packId + 1, pack.nextTokenIdToMint()); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, packContents.length); - for (uint256 i = 0; i < packed.length; i += 1) { - assertEq(packed[i].assetContract, packContents[i].assetContract); - assertEq(uint256(packed[i].tokenType), uint256(packContents[i].tokenType)); - assertEq(packed[i].tokenId, packContents[i].tokenId); - assertEq(packed[i].totalAmount, packContents[i].totalAmount); - } - - assertEq(packUri, pack.uri(packId)); - } - - /* - * note: Testing state changes; token owner calls `createPack` to pack native tokens. - */ - function test_state_createPack_nativeTokens() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.deal(address(tokenOwner), 100 ether); - packContents.push( - ITokenBundle.Token({ - assetContract: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 20 ether - }) - ); - numOfRewardUnits.push(20); - - vm.prank(address(tokenOwner)); - pack.createPack{ value: 20 ether }(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - assertEq(packId + 1, pack.nextTokenIdToMint()); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, packContents.length); - for (uint256 i = 0; i < packed.length; i += 1) { - assertEq(packed[i].assetContract, packContents[i].assetContract); - assertEq(uint256(packed[i].tokenType), uint256(packContents[i].tokenType)); - assertEq(packed[i].tokenId, packContents[i].tokenId); - assertEq(packed[i].totalAmount, packContents[i].totalAmount); - } - - assertEq(packUri, pack.uri(packId)); - } - - /** - * note: Testing state changes; token owner calls `createPack` to pack owned tokens. - * Only assets with ASSET_ROLE can be packed. - */ - function test_state_createPack_withAssetRoleRestriction() public { - vm.startPrank(deployer); - pack.revokeRole(keccak256("ASSET_ROLE"), address(0)); - for (uint256 i = 0; i < packContents.length; i += 1) { - if (!pack.hasRole(keccak256("ASSET_ROLE"), packContents[i].assetContract)) { - pack.grantRole(keccak256("ASSET_ROLE"), packContents[i].assetContract); - } - } - vm.stopPrank(); - - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - assertEq(packId + 1, pack.nextTokenIdToMint()); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, packContents.length); - for (uint256 i = 0; i < packed.length; i += 1) { - assertEq(packed[i].assetContract, packContents[i].assetContract); - assertEq(uint256(packed[i].tokenType), uint256(packContents[i].tokenType)); - assertEq(packed[i].tokenId, packContents[i].tokenId); - assertEq(packed[i].totalAmount, packContents[i].totalAmount); - } - - assertEq(packUri, pack.uri(packId)); - } - - /** - * note: Testing event emission; token owner calls `createPack` to pack owned tokens. - */ - function test_event_createPack_PackCreated() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectEmit(true, true, true, true); - emit PackCreated(packId, recipient, 226); - - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - vm.stopPrank(); - } - - /** - * note: Testing token balances; token owner calls `createPack` to pack owned tokens. - */ - function test_balances_createPack() public { - // ERC20 balance - assertEq(erc20.balanceOf(address(tokenOwner)), 2000 ether); - assertEq(erc20.balanceOf(address(pack)), 0); - - // ERC721 balance - assertEq(erc721.ownerOf(0), address(tokenOwner)); - assertEq(erc721.ownerOf(1), address(tokenOwner)); - assertEq(erc721.ownerOf(2), address(tokenOwner)); - assertEq(erc721.ownerOf(3), address(tokenOwner)); - assertEq(erc721.ownerOf(4), address(tokenOwner)); - assertEq(erc721.ownerOf(5), address(tokenOwner)); - - // ERC1155 balance - assertEq(erc1155.balanceOf(address(tokenOwner), 0), 100); - assertEq(erc1155.balanceOf(address(pack), 0), 0); - - assertEq(erc1155.balanceOf(address(tokenOwner), 1), 500); - assertEq(erc1155.balanceOf(address(pack), 1), 0); - - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - // ERC20 balance - assertEq(erc20.balanceOf(address(tokenOwner)), 0); - assertEq(erc20.balanceOf(address(pack)), 2000 ether); - - // ERC721 balance - assertEq(erc721.ownerOf(0), address(pack)); - assertEq(erc721.ownerOf(1), address(pack)); - assertEq(erc721.ownerOf(2), address(pack)); - assertEq(erc721.ownerOf(3), address(pack)); - assertEq(erc721.ownerOf(4), address(pack)); - assertEq(erc721.ownerOf(5), address(pack)); - - // ERC1155 balance - assertEq(erc1155.balanceOf(address(tokenOwner), 0), 0); - assertEq(erc1155.balanceOf(address(pack), 0), 100); - - assertEq(erc1155.balanceOf(address(tokenOwner), 1), 0); - assertEq(erc1155.balanceOf(address(pack), 1), 500); - - // Pack wrapped token balance - assertEq(pack.balanceOf(address(recipient), packId), totalSupply); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack owned tokens. - * Only assets with ASSET_ROLE can be packed, but assets being packed don't have that role. - */ - function test_revert_createPack_access_ASSET_ROLE() public { - vm.prank(deployer); - pack.revokeRole(keccak256("ASSET_ROLE"), address(0)); - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - address(erc721), - keccak256("ASSET_ROLE") - ) - ); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack owned tokens, without MINTER_ROLE. - */ - function test_revert_createPack_access_MINTER_ROLE() public { - vm.prank(address(tokenOwner)); - pack.renounceRole(keccak256("MINTER_ROLE"), address(tokenOwner)); - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - address(tokenOwner), - keccak256("MINTER_ROLE") - ) - ); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` with insufficient value when packing native tokens. - */ - function test_revert_createPack_nativeTokens_insufficientValue() public { - address recipient = address(0x123); - - vm.deal(address(tokenOwner), 100 ether); - - packContents.push( - ITokenBundle.Token({ - assetContract: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 20 ether - }) - ); - numOfRewardUnits.push(1); - - vm.prank(address(tokenOwner)); - vm.expectRevert( - abi.encodeWithSelector(CurrencyTransferLib.CurrencyTransferLibMismatchedValue.selector, 0, 20 ether) - ); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack un-owned ERC20 tokens. - */ - function test_revert_createPack_notOwner_ERC20() public { - tokenOwner.transferERC20(address(erc20), address(0x12), 1000 ether); - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC20: transfer amount exceeds balance"); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack un-owned ERC721 tokens. - */ - function test_revert_createPack_notOwner_ERC721() public { - tokenOwner.transferERC721(address(erc721), address(0x12), 0); - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC721: caller is not token owner or approved"); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack un-owned ERC1155 tokens. - */ - function test_revert_createPack_notOwner_ERC1155() public { - tokenOwner.transferERC1155(address(erc1155), address(0x12), 0, 100, ""); - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC1155: insufficient balance for transfer"); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack un-approved ERC20 tokens. - */ - function test_revert_createPack_notApprovedTransfer_ERC20() public { - tokenOwner.setAllowanceERC20(address(erc20), address(pack), 0); - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC20: insufficient allowance"); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack un-approved ERC721 tokens. - */ - function test_revert_createPack_notApprovedTransfer_ERC721() public { - tokenOwner.setApprovalForAllERC721(address(erc721), address(pack), false); - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC721: caller is not token owner or approved"); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack un-approved ERC1155 tokens. - */ - function test_revert_createPack_notApprovedTransfer_ERC1155() public { - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(pack), false); - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC1155: caller is not token owner or approved"); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` with invalid token-type. - */ - function test_revert_createPack_invalidTokenType() public { - ITokenBundle.Token[] memory invalidContent = new ITokenBundle.Token[](1); - uint256[] memory rewardUnits = new uint256[](1); - - invalidContent[0] = ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1 - }); - rewardUnits[0] = 1; - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("!TokenType"); - pack.createPack(invalidContent, rewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` with total-amount as 0. - */ - function test_revert_createPack_zeroTotalAmount() public { - ITokenBundle.Token[] memory invalidContent = new ITokenBundle.Token[](1); - uint256[] memory rewardUnits = new uint256[](1); - - invalidContent[0] = ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 0 - }); - rewardUnits[0] = 10; - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("0 amt"); - pack.createPack(invalidContent, rewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` with no tokens to pack. - */ - function test_revert_createPack_noTokensToPack() public { - ITokenBundle.Token[] memory emptyContent; - uint256[] memory rewardUnits; - - address recipient = address(0x123); - - bytes memory err = "!Len"; - vm.startPrank(address(tokenOwner)); - vm.expectRevert(err); - pack.createPack(emptyContent, rewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` with unequal length of contents and rewardUnits. - */ - function test_revert_createPack_invalidRewardUnits() public { - uint256[] memory rewardUnits; - - address recipient = address(0x123); - - bytes memory err = "!Len"; - vm.startPrank(address(tokenOwner)); - vm.expectRevert(err); - pack.createPack(packContents, rewardUnits, packUri, 0, 1, recipient); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `addPackContents` - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing state changes; token owner calls `addPackContents` to pack more tokens. - */ - function test_state_addPackContents() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, packContents.length); - for (uint256 i = 0; i < packed.length; i += 1) { - assertEq(packed[i].assetContract, packContents[i].assetContract); - assertEq(uint256(packed[i].tokenType), uint256(packContents[i].tokenType)); - assertEq(packed[i].tokenId, packContents[i].tokenId); - assertEq(packed[i].totalAmount, packContents[i].totalAmount); - } - - erc20.mint(address(tokenOwner), 1000 ether); - erc1155.mint(address(tokenOwner), 2, 200); - - vm.prank(address(tokenOwner)); - pack.addPackContents(packId, additionalContents, additionalContentsRewardUnits, recipient); - - (packed, ) = pack.getPackContents(packId); - assertEq(packed.length, packContents.length + additionalContents.length); - for (uint256 i = packContents.length; i < packed.length; i += 1) { - assertEq(packed[i].assetContract, additionalContents[i - packContents.length].assetContract); - assertEq(uint256(packed[i].tokenType), uint256(additionalContents[i - packContents.length].tokenType)); - assertEq(packed[i].tokenId, additionalContents[i - packContents.length].tokenId); - assertEq(packed[i].totalAmount, additionalContents[i - packContents.length].totalAmount); - } - } - - /** - * note: Testing token balances; token owner calls `addPackContents` to pack more tokens - * in an already existing pack. - */ - function test_balances_addPackContents() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - // ERC20 balance - assertEq(erc20.balanceOf(address(tokenOwner)), 0); - assertEq(erc20.balanceOf(address(pack)), 2000 ether); - - // ERC721 balance - assertEq(erc721.ownerOf(0), address(pack)); - assertEq(erc721.ownerOf(1), address(pack)); - assertEq(erc721.ownerOf(2), address(pack)); - assertEq(erc721.ownerOf(3), address(pack)); - assertEq(erc721.ownerOf(4), address(pack)); - assertEq(erc721.ownerOf(5), address(pack)); - - // ERC1155 balance - assertEq(erc1155.balanceOf(address(tokenOwner), 0), 0); - assertEq(erc1155.balanceOf(address(pack), 0), 100); - - assertEq(erc1155.balanceOf(address(tokenOwner), 1), 0); - assertEq(erc1155.balanceOf(address(pack), 1), 500); - - // Pack wrapped token balance - assertEq(pack.balanceOf(address(recipient), packId), totalSupply); - - erc20.mint(address(tokenOwner), 1000 ether); - erc1155.mint(address(tokenOwner), 2, 200); - - vm.prank(address(tokenOwner)); - (uint256 newTotalSupply, uint256 additionalSupply) = pack.addPackContents( - packId, - additionalContents, - additionalContentsRewardUnits, - recipient - ); - - // ERC20 balance after adding more tokens - assertEq(erc20.balanceOf(address(tokenOwner)), 0); - assertEq(erc20.balanceOf(address(pack)), 3000 ether); - - // ERC1155 balance after adding more tokens - assertEq(erc1155.balanceOf(address(tokenOwner), 2), 0); - assertEq(erc1155.balanceOf(address(pack), 2), 200); - - // Pack wrapped token balance - assertEq(pack.balanceOf(address(recipient), packId), newTotalSupply); - assertEq(totalSupply + additionalSupply, newTotalSupply); - } - - /** - * note: Testing revert condition; non-creator calls `addPackContents`. - */ - function test_revert_addPackContents_NotMinterRole() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - address randomAccount = address(0x123); - - vm.prank(randomAccount); - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - randomAccount, - keccak256("MINTER_ROLE") - ) - ); - pack.addPackContents(packId, additionalContents, additionalContentsRewardUnits, recipient); - } - - /** - * note: Testing revert condition; adding tokens to non-existent pack. - */ - function test_revert_addPackContents_PackNonExistent() public { - vm.prank(address(tokenOwner)); - vm.expectRevert("!Allowed"); - pack.addPackContents(0, packContents, numOfRewardUnits, address(1)); - } - - /** - * note: Testing revert condition; adding tokens after packs have been distributed. - */ - function test_revert_addPackContents_CantUpdateAnymore() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - vm.prank(recipient); - pack.safeTransferFrom(recipient, address(567), packId, 1, ""); - - vm.prank(address(tokenOwner)); - vm.expectRevert("!Allowed"); - pack.addPackContents(packId, additionalContents, additionalContentsRewardUnits, recipient); - } - - /** - * note: Testing revert condition; adding tokens with a different recipient. - */ - function test_revert_addPackContents_NotRecipient() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - address randomRecipient = address(0x12345); - - bytes memory err = "!Bal"; - vm.expectRevert(err); - vm.prank(address(tokenOwner)); - pack.addPackContents(packId, additionalContents, additionalContentsRewardUnits, randomRecipient); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `openPack` - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing state changes; pack owner calls `openPack` to redeem underlying rewards. - */ - function test_state_openPack() public { - vm.warp(1000); - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 3; - address recipient = address(1); - - vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); - - vm.prank(recipient, recipient); - ITokenBundle.Token[] memory rewardUnits = pack.openPack(packId, packsToOpen); - console2.log("total reward units: ", rewardUnits.length); - - for (uint256 i = 0; i < rewardUnits.length; i++) { - console2.log("----- reward unit number: ", i, "------"); - console2.log("asset contract: ", rewardUnits[i].assetContract); - console2.log("token type: ", uint256(rewardUnits[i].tokenType)); - console2.log("tokenId: ", rewardUnits[i].tokenId); - if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC20) { - console2.log("total amount: ", rewardUnits[i].totalAmount / 1 ether, "ether"); - } else { - console2.log("total amount: ", rewardUnits[i].totalAmount); - } - console2.log(""); - } - - assertEq(packUri, pack.uri(packId)); - assertEq(pack.totalSupply(packId), totalSupply - packsToOpen); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, packContents.length); - } - - /** - * note: Total amount should get updated correctly -- reduce perUnitAmount from totalAmount of the token content, for each reward - */ - function test_state_openPack_totalAmounts_ERC721() public { - vm.warp(1000); - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 1; - address recipient = address(1); - - erc721.mint(address(tokenOwner), 6); - - ITokenBundle.Token[] memory tempContents = new ITokenBundle.Token[](1); - uint256[] memory tempNumRewardUnits = new uint256[](1); - - tempContents[0] = ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 0, - totalAmount: 1 - }); - tempNumRewardUnits[0] = 1; - - vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(tempContents, tempNumRewardUnits, packUri, 0, 1, recipient); - - vm.prank(recipient, recipient); - ITokenBundle.Token[] memory rewardUnits = pack.openPack(packId, packsToOpen); - - assertEq(packUri, pack.uri(packId)); - assertEq(pack.totalSupply(packId), totalSupply - packsToOpen); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, tempContents.length); - assertEq(packed[0].totalAmount, tempContents[0].totalAmount - rewardUnits[0].totalAmount); - } - - /** - * note: Total amount should get updated correctly -- reduce perUnitAmount from totalAmount of the token content, for each reward - */ - function test_state_openPack_totalAmounts_ERC1155() public { - vm.warp(1000); - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 1; - address recipient = address(1); - - erc1155.mint(address(tokenOwner), 0, 100); - - ITokenBundle.Token[] memory tempContents = new ITokenBundle.Token[](1); - uint256[] memory tempNumRewardUnits = new uint256[](1); - - tempContents[0] = ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 0, - totalAmount: 100 - }); - tempNumRewardUnits[0] = 10; - - vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(tempContents, tempNumRewardUnits, packUri, 0, 1, recipient); - - vm.prank(recipient, recipient); - ITokenBundle.Token[] memory rewardUnits = pack.openPack(packId, packsToOpen); - - assertEq(packUri, pack.uri(packId)); - assertEq(pack.totalSupply(packId), totalSupply - packsToOpen); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, tempContents.length); - assertEq(packed[0].totalAmount, tempContents[0].totalAmount - rewardUnits[0].totalAmount); - } - - /** - * note: Total amount should get updated correctly -- reduce perUnitAmount from totalAmount of the token content, for each reward - */ - function test_state_openPack_totalAmounts_ERC20() public { - vm.warp(1000); - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 1; - address recipient = address(1); - - erc20.mint(address(tokenOwner), 2000 ether); - - ITokenBundle.Token[] memory tempContents = new ITokenBundle.Token[](1); - uint256[] memory tempNumRewardUnits = new uint256[](1); - - tempContents[0] = ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }); - tempNumRewardUnits[0] = 50; - - vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(tempContents, tempNumRewardUnits, packUri, 0, 1, recipient); - - vm.prank(recipient, recipient); - ITokenBundle.Token[] memory rewardUnits = pack.openPack(packId, packsToOpen); - - assertEq(packUri, pack.uri(packId)); - assertEq(pack.totalSupply(packId), totalSupply - packsToOpen); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, tempContents.length); - assertEq(packed[0].totalAmount, tempContents[0].totalAmount - rewardUnits[0].totalAmount); - } - - /** - * note: Testing event emission; pack owner calls `openPack` to open owned packs. - */ - function test_event_openPack_PackOpened() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - - ITokenBundle.Token[] memory emptyRewardUnitsForTestingEvent; - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - vm.expectEmit(true, true, false, false); - emit PackOpened(packId, recipient, 1, emptyRewardUnitsForTestingEvent); - - vm.prank(recipient, recipient); - pack.openPack(packId, 1); - } - - function test_balances_openPack() public { - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 3; - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); - - // ERC20 balance - assertEq(erc20.balanceOf(address(recipient)), 0); - assertEq(erc20.balanceOf(address(pack)), 2000 ether); - - // ERC721 balance - assertEq(erc721.ownerOf(0), address(pack)); - assertEq(erc721.ownerOf(1), address(pack)); - assertEq(erc721.ownerOf(2), address(pack)); - assertEq(erc721.ownerOf(3), address(pack)); - assertEq(erc721.ownerOf(4), address(pack)); - assertEq(erc721.ownerOf(5), address(pack)); - - // ERC1155 balance - assertEq(erc1155.balanceOf(address(recipient), 0), 0); - assertEq(erc1155.balanceOf(address(pack), 0), 100); - - assertEq(erc1155.balanceOf(address(recipient), 1), 0); - assertEq(erc1155.balanceOf(address(pack), 1), 500); - - vm.prank(recipient, recipient); - ITokenBundle.Token[] memory rewardUnits = pack.openPack(packId, packsToOpen); - console2.log("total reward units: ", rewardUnits.length); - - uint256 erc20Amount; - uint256[] memory erc1155Amounts = new uint256[](2); - uint256 erc721Amount; - - for (uint256 i = 0; i < rewardUnits.length; i++) { - console2.log("----- reward unit number: ", i, "------"); - console2.log("asset contract: ", rewardUnits[i].assetContract); - console2.log("token type: ", uint256(rewardUnits[i].tokenType)); - console2.log("tokenId: ", rewardUnits[i].tokenId); - if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC20) { - console2.log("total amount: ", rewardUnits[i].totalAmount / 1 ether, "ether"); - console.log("balance of recipient: ", erc20.balanceOf(address(recipient)) / 1 ether, "ether"); - erc20Amount += rewardUnits[i].totalAmount; - } else if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC1155) { - console2.log("total amount: ", rewardUnits[i].totalAmount); - console.log("balance of recipient: ", erc1155.balanceOf(address(recipient), rewardUnits[i].tokenId)); - erc1155Amounts[rewardUnits[i].tokenId] += rewardUnits[i].totalAmount; - } else if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC721) { - console2.log("total amount: ", rewardUnits[i].totalAmount); - console.log("balance of recipient: ", erc721.balanceOf(address(recipient))); - erc721Amount += rewardUnits[i].totalAmount; - } - console2.log(""); - } - - assertEq(erc20.balanceOf(address(recipient)), erc20Amount); - assertEq(erc721.balanceOf(address(recipient)), erc721Amount); - - for (uint256 i = 0; i < erc1155Amounts.length; i += 1) { - assertEq(erc1155.balanceOf(address(recipient), i), erc1155Amounts[i]); - } - } - - /** - * note: Testing revert condition; caller of `openPack` is not EOA. - */ - function test_revert_openPack_notEOA() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - vm.startPrank(recipient, address(27)); - string memory err = "!EOA"; - vm.expectRevert(bytes(err)); - pack.openPack(packId, 1); - } - - /** - * note: Testing revert condition; pack owner calls `openPack` to open more than owned packs. - */ - function test_revert_openPack_openMoreThanOwned() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - bytes memory err = "!Bal"; - vm.startPrank(recipient, recipient); - vm.expectRevert(err); - pack.openPack(packId, totalSupply + 1); - } - - /** - * note: Testing revert condition; pack owner calls `openPack` before start timestamp. - */ - function test_revert_openPack_openBeforeStart() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 1000, 1, recipient); - - vm.startPrank(recipient, recipient); - vm.expectRevert("cant open"); - pack.openPack(packId, 1); - } - - /** - * note: Testing revert condition; pack owner calls `openPack` with pack-id non-existent or not owned. - */ - function test_revert_openPack_invalidPackId() public { - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - bytes memory err = "!Bal"; - vm.startPrank(recipient, recipient); - vm.expectRevert(err); - pack.openPack(2, 1); - } - - /*/////////////////////////////////////////////////////////////// - Fuzz testing - //////////////////////////////////////////////////////////////*/ - - uint256 internal constant MAX_TOKENS = 2000; - - function getTokensToPack( - uint256 len - ) internal returns (ITokenBundle.Token[] memory tokensToPack, uint256[] memory rewardUnits) { - vm.assume(len < MAX_TOKENS); - tokensToPack = new ITokenBundle.Token[](len); - rewardUnits = new uint256[](len); - - for (uint256 i = 0; i < len; i += 1) { - uint256 random = uint256(keccak256(abi.encodePacked(len + i))) % MAX_TOKENS; - uint256 selector = random % 4; - - if (selector == 0) { - tokensToPack[i] = ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: (random + 1) * 10 ether - }); - rewardUnits[i] = random + 1; - - erc20.mint(address(tokenOwner), tokensToPack[i].totalAmount); - } else if (selector == 1) { - uint256 tokenId = erc721.nextTokenIdToMint(); - - tokensToPack[i] = ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: tokenId, - totalAmount: 1 - }); - rewardUnits[i] = 1; - - erc721.mint(address(tokenOwner), 1); - } else if (selector == 2) { - tokensToPack[i] = ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: random, - totalAmount: (random + 1) * 10 - }); - rewardUnits[i] = random + 1; - - erc1155.mint(address(tokenOwner), tokensToPack[i].tokenId, tokensToPack[i].totalAmount); - } else if (selector == 3) { - tokensToPack[i] = ITokenBundle.Token({ - assetContract: address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 5 ether - }); - rewardUnits[i] = 5; - } - } - } - - function checkBalances( - ITokenBundle.Token[] memory rewardUnits, - address - ) - internal - pure - returns (uint256 nativeTokenAmount, uint256 erc20Amount, uint256[] memory erc1155Amounts, uint256 erc721Amount) - { - erc1155Amounts = new uint256[](MAX_TOKENS); - - for (uint256 i = 0; i < rewardUnits.length; i++) { - // console2.log("----- reward unit number: ", i, "------"); - // console2.log("asset contract: ", rewardUnits[i].assetContract); - // console2.log("token type: ", uint256(rewardUnits[i].tokenType)); - // console2.log("tokenId: ", rewardUnits[i].tokenId); - if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC20) { - if (rewardUnits[i].assetContract == address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)) { - // console2.log("total amount: ", rewardUnits[i].totalAmount / 1 ether, "ether"); - // console.log("balance of recipient: ", address(recipient).balance); - nativeTokenAmount += rewardUnits[i].totalAmount; - } else { - // console2.log("total amount: ", rewardUnits[i].totalAmount / 1 ether, "ether"); - // console.log("balance of recipient: ", erc20.balanceOf(address(recipient)) / 1 ether, "ether"); - erc20Amount += rewardUnits[i].totalAmount; - } - } else if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC1155) { - // console2.log("total amount: ", rewardUnits[i].totalAmount); - // console.log("balance of recipient: ", erc1155.balanceOf(address(recipient), rewardUnits[i].tokenId)); - erc1155Amounts[rewardUnits[i].tokenId] += rewardUnits[i].totalAmount; - } else if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC721) { - // console2.log("total amount: ", rewardUnits[i].totalAmount); - // console.log("balance of recipient: ", erc721.balanceOf(address(recipient))); - erc721Amount += rewardUnits[i].totalAmount; - } - // console2.log(""); - } - } - - function test_fuzz_state_createPack(uint256 x, uint128 y) public { - (ITokenBundle.Token[] memory tokensToPack, uint256[] memory rewardUnits) = getTokensToPack(x); - if (tokensToPack.length == 0) { - return; - } - - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - uint256 totalRewardUnits; - uint256 nativeTokenPacked; - - for (uint256 i = 0; i < tokensToPack.length; i += 1) { - totalRewardUnits += rewardUnits[i]; - if (tokensToPack[i].assetContract == address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)) { - nativeTokenPacked += tokensToPack[i].totalAmount; - } - } - vm.deal(address(tokenOwner), nativeTokenPacked); - vm.assume(y > 0 && totalRewardUnits % y == 0); - - vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack{ value: nativeTokenPacked }( - tokensToPack, - rewardUnits, - packUri, - 0, - y, - recipient - ); - console2.log("total supply: ", totalSupply); - console2.log("total reward units: ", totalRewardUnits); - - assertEq(packId + 1, pack.nextTokenIdToMint()); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, tokensToPack.length); - for (uint256 i = 0; i < packed.length; i += 1) { - assertEq(packed[i].assetContract, tokensToPack[i].assetContract); - assertEq(uint256(packed[i].tokenType), uint256(tokensToPack[i].tokenType)); - assertEq(packed[i].tokenId, tokensToPack[i].tokenId); - assertEq(packed[i].totalAmount, tokensToPack[i].totalAmount); - } - - assertEq(packUri, pack.uri(packId)); - } - - /*/////////////////////////////////////////////////////////////// - Scenario/Exploit tests - //////////////////////////////////////////////////////////////*/ - /** - * note: Testing revert condition; token owner calls `createPack` to pack owned tokens. - */ - function test_revert_createPack_reentrancy() public { - MaliciousERC20 malERC20 = new MaliciousERC20(payable(address(pack))); - ITokenBundle.Token[] memory content = new ITokenBundle.Token[](1); - uint256[] memory rewards = new uint256[](1); - - malERC20.mint(address(tokenOwner), 10 ether); - content[0] = ITokenBundle.Token({ - assetContract: address(malERC20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }); - rewards[0] = 10; - - tokenOwner.setAllowanceERC20(address(malERC20), address(pack), 10 ether); - - address recipient = address(0x123); - - vm.prank(address(deployer)); - pack.grantRole(keccak256("MINTER_ROLE"), address(malERC20)); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ReentrancyGuard: reentrant call"); - pack.createPack(content, rewards, packUri, 0, 1, recipient); - } -} - -contract MaliciousERC20 is MockERC20, ITokenBundle { - Pack public pack; - - constructor(address payable _pack) { - pack = Pack(_pack); - } - - function transferFrom(address from, address to, uint256 amount) public override returns (bool) { - ITokenBundle.Token[] memory content = new ITokenBundle.Token[](1); - uint256[] memory rewards = new uint256[](1); - - address recipient = address(0x123); - pack.createPack(content, rewards, "", 0, 1, recipient); - return super.transferFrom(from, to, amount); - } -} diff --git a/src/test/pack/PackVRFDirect.t.sol b/src/test/pack/PackVRFDirect.t.sol deleted file mode 100644 index 55a620e19..000000000 --- a/src/test/pack/PackVRFDirect.t.sol +++ /dev/null @@ -1,1055 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { PackVRFDirect, IERC2981Upgradeable, IERC721Receiver, IERC1155Upgradeable } from "contracts/prebuilts/pack/PackVRFDirect.sol"; -import { IPack } from "contracts/prebuilts/interface/IPack.sol"; -import { ITokenBundle } from "contracts/extension/interface/ITokenBundle.sol"; -import { CurrencyTransferLib } from "contracts/lib/CurrencyTransferLib.sol"; - -// Test imports -import { MockERC20 } from "../mocks/MockERC20.sol"; -import { Wallet } from "../utils/Wallet.sol"; -import "../utils/BaseTest.sol"; - -contract PackVRFDirectTest is BaseTest { - /// @notice Emitted when a set of packs is created. - event PackCreated(uint256 indexed packId, address recipient, uint256 totalPacksCreated); - - /// @notice Emitted when the opening of a pack is requested. - event PackOpenRequested(address indexed opener, uint256 indexed packId, uint256 amountToOpen, uint256 requestId); - - /// @notice Emitted when Chainlink VRF fulfills a random number request. - event PackRandomnessFulfilled(uint256 indexed packId, uint256 indexed requestId); - - /// @notice Emitted when a pack is opened. - event PackOpened( - uint256 indexed packId, - address indexed opener, - uint256 numOfPacksOpened, - ITokenBundle.Token[] rewardUnitsDistributed - ); - - PackVRFDirect internal pack; - - Wallet internal tokenOwner; - string internal packUri; - ITokenBundle.Token[] internal packContents; - ITokenBundle.Token[] internal additionalContents; - uint256[] internal numOfRewardUnits; - uint256[] internal additionalContentsRewardUnits; - - function setUp() public virtual override { - super.setUp(); - - pack = PackVRFDirect(payable(getContract("PackVRFDirect"))); - - tokenOwner = getWallet(); - packUri = "ipfs://"; - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 0, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 0, - totalAmount: 100 - }) - ); - numOfRewardUnits.push(20); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }) - ); - numOfRewardUnits.push(50); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 1, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 2, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }) - ); - numOfRewardUnits.push(100); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 3, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 4, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 5, - totalAmount: 1 - }) - ); - numOfRewardUnits.push(1); - - packContents.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 1, - totalAmount: 500 - }) - ); - numOfRewardUnits.push(50); - - erc20.mint(address(tokenOwner), 2000 ether); - erc721.mint(address(tokenOwner), 6); - erc1155.mint(address(tokenOwner), 0, 100); - erc1155.mint(address(tokenOwner), 1, 500); - - // additional contents, to check `addPackContents` - additionalContents.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 2, - totalAmount: 200 - }) - ); - additionalContentsRewardUnits.push(50); - - additionalContents.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1000 ether - }) - ); - additionalContentsRewardUnits.push(100); - - tokenOwner.setAllowanceERC20(address(erc20), address(pack), type(uint256).max); - tokenOwner.setApprovalForAllERC721(address(erc721), address(pack), true); - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(pack), true); - - vm.prank(deployer); - pack.grantRole(keccak256("MINTER_ROLE"), address(tokenOwner)); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `createPack` - //////////////////////////////////////////////////////////////*/ - - function test_interface() public pure { - console2.logBytes4(type(IERC20).interfaceId); - console2.logBytes4(type(IERC721).interfaceId); - console2.logBytes4(type(IERC1155).interfaceId); - } - - function test_supportsInterface() public { - assertEq(pack.supportsInterface(type(IERC2981Upgradeable).interfaceId), true); - assertEq(pack.supportsInterface(type(IERC721Receiver).interfaceId), true); - assertEq(pack.supportsInterface(type(IERC1155Receiver).interfaceId), true); - assertEq(pack.supportsInterface(type(IERC1155Upgradeable).interfaceId), true); - } - - /** - * note: Testing state changes; token owner calls `createPack` to pack owned tokens. - */ - function test_state_createPack() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - assertEq(packId + 1, pack.nextTokenIdToMint()); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, packContents.length); - for (uint256 i = 0; i < packed.length; i += 1) { - assertEq(packed[i].assetContract, packContents[i].assetContract); - assertEq(uint256(packed[i].tokenType), uint256(packContents[i].tokenType)); - assertEq(packed[i].tokenId, packContents[i].tokenId); - assertEq(packed[i].totalAmount, packContents[i].totalAmount); - } - - assertEq(packUri, pack.uri(packId)); - } - - /* - * note: Testing state changes; token owner calls `createPack` to pack native tokens. - */ - function test_state_createPack_nativeTokens() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.deal(address(tokenOwner), 100 ether); - packContents.push( - ITokenBundle.Token({ - assetContract: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 20 ether - }) - ); - numOfRewardUnits.push(20); - - vm.prank(address(tokenOwner)); - pack.createPack{ value: 20 ether }(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - assertEq(packId + 1, pack.nextTokenIdToMint()); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, packContents.length); - for (uint256 i = 0; i < packed.length; i += 1) { - assertEq(packed[i].assetContract, packContents[i].assetContract); - assertEq(uint256(packed[i].tokenType), uint256(packContents[i].tokenType)); - assertEq(packed[i].tokenId, packContents[i].tokenId); - assertEq(packed[i].totalAmount, packContents[i].totalAmount); - } - - assertEq(packUri, pack.uri(packId)); - } - - /** - * note: Testing event emission; token owner calls `createPack` to pack owned tokens. - */ - function test_event_createPack_PackCreated() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectEmit(true, true, true, true); - emit PackCreated(packId, recipient, 226); - - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - vm.stopPrank(); - } - - /** - * note: Testing token balances; token owner calls `createPack` to pack owned tokens. - */ - function test_balances_createPack() public { - // ERC20 balance - assertEq(erc20.balanceOf(address(tokenOwner)), 2000 ether); - assertEq(erc20.balanceOf(address(pack)), 0); - - // ERC721 balance - assertEq(erc721.ownerOf(0), address(tokenOwner)); - assertEq(erc721.ownerOf(1), address(tokenOwner)); - assertEq(erc721.ownerOf(2), address(tokenOwner)); - assertEq(erc721.ownerOf(3), address(tokenOwner)); - assertEq(erc721.ownerOf(4), address(tokenOwner)); - assertEq(erc721.ownerOf(5), address(tokenOwner)); - - // ERC1155 balance - assertEq(erc1155.balanceOf(address(tokenOwner), 0), 100); - assertEq(erc1155.balanceOf(address(pack), 0), 0); - - assertEq(erc1155.balanceOf(address(tokenOwner), 1), 500); - assertEq(erc1155.balanceOf(address(pack), 1), 0); - - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(1); - - vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - // ERC20 balance - assertEq(erc20.balanceOf(address(tokenOwner)), 0); - assertEq(erc20.balanceOf(address(pack)), 2000 ether); - - // ERC721 balance - assertEq(erc721.ownerOf(0), address(pack)); - assertEq(erc721.ownerOf(1), address(pack)); - assertEq(erc721.ownerOf(2), address(pack)); - assertEq(erc721.ownerOf(3), address(pack)); - assertEq(erc721.ownerOf(4), address(pack)); - assertEq(erc721.ownerOf(5), address(pack)); - - // ERC1155 balance - assertEq(erc1155.balanceOf(address(tokenOwner), 0), 0); - assertEq(erc1155.balanceOf(address(pack), 0), 100); - - assertEq(erc1155.balanceOf(address(tokenOwner), 1), 0); - assertEq(erc1155.balanceOf(address(pack), 1), 500); - - // Pack wrapped token balance - assertEq(pack.balanceOf(address(recipient), packId), totalSupply); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack owned tokens, without MINTER_ROLE. - */ - function test_revert_createPack_access_MINTER_ROLE() public { - vm.prank(address(tokenOwner)); - pack.renounceRole(keccak256("MINTER_ROLE"), address(tokenOwner)); - - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - address(tokenOwner), - keccak256("MINTER_ROLE") - ) - ); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` with insufficient value when packing native tokens. - */ - function test_revert_createPack_nativeTokens_insufficientValue() public { - address recipient = address(0x123); - - vm.deal(address(tokenOwner), 100 ether); - - packContents.push( - ITokenBundle.Token({ - assetContract: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 20 ether - }) - ); - numOfRewardUnits.push(1); - - vm.prank(address(tokenOwner)); - vm.expectRevert( - abi.encodeWithSelector(CurrencyTransferLib.CurrencyTransferLibMismatchedValue.selector, 0, 20 ether) - ); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack un-owned ERC20 tokens. - */ - function test_revert_createPack_notOwner_ERC20() public { - tokenOwner.transferERC20(address(erc20), address(0x12), 1000 ether); - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC20: transfer amount exceeds balance"); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack un-owned ERC721 tokens. - */ - function test_revert_createPack_notOwner_ERC721() public { - tokenOwner.transferERC721(address(erc721), address(0x12), 0); - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC721: caller is not token owner or approved"); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack un-owned ERC1155 tokens. - */ - function test_revert_createPack_notOwner_ERC1155() public { - tokenOwner.transferERC1155(address(erc1155), address(0x12), 0, 100, ""); - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC1155: insufficient balance for transfer"); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack un-approved ERC20 tokens. - */ - function test_revert_createPack_notApprovedTransfer_ERC20() public { - tokenOwner.setAllowanceERC20(address(erc20), address(pack), 0); - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC20: insufficient allowance"); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack un-approved ERC721 tokens. - */ - function test_revert_createPack_notApprovedTransfer_ERC721() public { - tokenOwner.setApprovalForAllERC721(address(erc721), address(pack), false); - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC721: caller is not token owner or approved"); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` to pack un-approved ERC1155 tokens. - */ - function test_revert_createPack_notApprovedTransfer_ERC1155() public { - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(pack), false); - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC1155: caller is not token owner or approved"); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` with invalid token-type. - */ - function test_revert_createPack_invalidTokenType() public { - ITokenBundle.Token[] memory invalidContent = new ITokenBundle.Token[](1); - uint256[] memory rewardUnits = new uint256[](1); - - invalidContent[0] = ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 1 - }); - rewardUnits[0] = 1; - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("!TokenType"); - pack.createPack(invalidContent, rewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` with total-amount as 0. - */ - function test_revert_createPack_zeroTotalAmount() public { - ITokenBundle.Token[] memory invalidContent = new ITokenBundle.Token[](1); - uint256[] memory rewardUnits = new uint256[](1); - - invalidContent[0] = ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 0 - }); - rewardUnits[0] = 10; - - address recipient = address(0x123); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("0 amt"); - pack.createPack(invalidContent, rewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` with no tokens to pack. - */ - function test_revert_createPack_noTokensToPack() public { - ITokenBundle.Token[] memory emptyContent; - uint256[] memory rewardUnits; - - address recipient = address(0x123); - - bytes memory err = "!Len"; - vm.startPrank(address(tokenOwner)); - vm.expectRevert(err); - pack.createPack(emptyContent, rewardUnits, packUri, 0, 1, recipient); - } - - /** - * note: Testing revert condition; token owner calls `createPack` with unequal length of contents and rewardUnits. - */ - function test_revert_createPack_invalidRewardUnits() public { - uint256[] memory rewardUnits; - - address recipient = address(0x123); - - bytes memory err = "!Len"; - vm.startPrank(address(tokenOwner)); - vm.expectRevert(err); - pack.createPack(packContents, rewardUnits, packUri, 0, 1, recipient); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `openPackAndClaimRewards` - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing state changes; pack owner calls `openPack` to redeem underlying rewards. - */ - function test_state_openPackAndClaimRewards() public { - vm.warp(1000); - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 3; - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); - - vm.prank(recipient, recipient); - uint256 requestId = pack.openPackAndClaimRewards(packId, packsToOpen, 2_500_000); - console2.log("request ID for opening pack:", requestId); - - uint256[] memory randomValues = new uint256[](1); - randomValues[0] = 12345678; - - ITokenBundle.Token[] memory emptyRewardUnitsForTestingEvent; - - vm.expectEmit(true, true, false, false); - emit PackOpened(packId, recipient, 1, emptyRewardUnitsForTestingEvent); - - vm.prank(vrfV2Wrapper); - pack.rawFulfillRandomWords(requestId, randomValues); - - assertFalse(pack.canClaimRewards(recipient)); - } - - /** - * note: Testing state changes; pack owner calls `openPack` to redeem underlying rewards. - */ - function test_state_openPackAndClaimRewards_lowGasFailsafe() public { - vm.warp(1000); - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 3; - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); - - vm.prank(recipient, recipient); - uint256 requestId = pack.openPackAndClaimRewards(packId, packsToOpen, 2); - console2.log("request ID for opening pack:", requestId); - - uint256[] memory randomValues = new uint256[](1); - randomValues[0] = 12345678; - - // check state before - assertFalse(pack.canClaimRewards(recipient)); - console.log(pack.canClaimRewards(recipient)); - - // mock the call with low gas, causing revert in _claimRewards - vm.prank(vrfV2Wrapper); - pack.rawFulfillRandomWords{ gas: 100_000 }(requestId, randomValues); - - // check state after - assertTrue(pack.canClaimRewards(recipient)); - console.log(pack.canClaimRewards(recipient)); - } - - /** - * note: Cannot open pack again while a previous openPack request is in flight. - */ - function test_revert_openPackAndClaimRewards_ReqInFlight() public { - vm.warp(1000); - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 3; - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); - - vm.prank(recipient, recipient); - uint256 requestId = pack.openPackAndClaimRewards(packId, packsToOpen, 2_500_000); - console2.log("request ID for opening pack:", requestId); - - vm.expectRevert("ReqInFlight"); - - vm.prank(recipient, recipient); - pack.openPackAndClaimRewards(packId, packsToOpen, 2_500_000); - - vm.expectRevert("ReqInFlight"); - - vm.prank(recipient, recipient); - pack.openPack(packId, packsToOpen); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `openPack` - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing state changes; pack owner calls `openPack` to redeem underlying rewards. - */ - function test_state_openPack() public { - vm.warp(1000); - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 3; - address recipient = address(1); - - vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); - - vm.prank(recipient, recipient); - uint256 requestId = pack.openPack(packId, packsToOpen); - console2.log("request ID for opening pack:", requestId); - - uint256[] memory randomValues = new uint256[](1); - randomValues[0] = 12345678; - - vm.prank(vrfV2Wrapper); - pack.rawFulfillRandomWords(requestId, randomValues); - - vm.prank(recipient, recipient); - ITokenBundle.Token[] memory rewardUnits = pack.claimRewards(); - console2.log("total reward units: ", rewardUnits.length); - - for (uint256 i = 0; i < rewardUnits.length; i++) { - console2.log("----- reward unit number: ", i, "------"); - console2.log("asset contract: ", rewardUnits[i].assetContract); - console2.log("token type: ", uint256(rewardUnits[i].tokenType)); - console2.log("tokenId: ", rewardUnits[i].tokenId); - if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC20) { - console2.log("total amount: ", rewardUnits[i].totalAmount / 1 ether, "ether"); - } else { - console2.log("total amount: ", rewardUnits[i].totalAmount); - } - console2.log(""); - } - - assertEq(packUri, pack.uri(packId)); - assertEq(pack.totalSupply(packId), totalSupply - packsToOpen); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, packContents.length); - } - - /** - * note: Testing event emission; pack owner calls `openPack` to open owned packs. - */ - function test_event_openPack() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - vm.expectEmit(true, true, false, false); - emit PackOpenRequested(recipient, packId, 1, VRFV2Wrapper(vrfV2Wrapper).lastRequestId()); - - vm.prank(recipient, recipient); - uint256 requestId = pack.openPack(packId, 1); - - vm.expectEmit(true, true, false, true); - emit PackRandomnessFulfilled(packId, requestId); - - uint256[] memory randomValues = new uint256[](1); - randomValues[0] = 12345678; - - vm.prank(vrfV2Wrapper); - pack.rawFulfillRandomWords(requestId, randomValues); - - ITokenBundle.Token[] memory emptyRewardUnitsForTestingEvent; - - vm.expectEmit(true, true, false, false); - emit PackOpened(packId, recipient, 1, emptyRewardUnitsForTestingEvent); - - vm.prank(recipient, recipient); - pack.claimRewards(); - } - - function test_balances_openPack() public { - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 3; - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); - - // ERC20 balance - assertEq(erc20.balanceOf(address(recipient)), 0); - assertEq(erc20.balanceOf(address(pack)), 2000 ether); - - // ERC721 balance - assertEq(erc721.ownerOf(0), address(pack)); - assertEq(erc721.ownerOf(1), address(pack)); - assertEq(erc721.ownerOf(2), address(pack)); - assertEq(erc721.ownerOf(3), address(pack)); - assertEq(erc721.ownerOf(4), address(pack)); - assertEq(erc721.ownerOf(5), address(pack)); - - // ERC1155 balance - assertEq(erc1155.balanceOf(address(recipient), 0), 0); - assertEq(erc1155.balanceOf(address(pack), 0), 100); - - assertEq(erc1155.balanceOf(address(recipient), 1), 0); - assertEq(erc1155.balanceOf(address(pack), 1), 500); - - vm.prank(recipient, recipient); - uint256 requestId = pack.openPack(packId, packsToOpen); - console2.log("request ID for opening pack:", requestId); - - uint256[] memory randomValues = new uint256[](1); - randomValues[0] = 12345678; - - vm.prank(vrfV2Wrapper); - pack.rawFulfillRandomWords(requestId, randomValues); - - vm.prank(recipient, recipient); - ITokenBundle.Token[] memory rewardUnits = pack.claimRewards(); - console2.log("total reward units: ", rewardUnits.length); - - uint256 erc20Amount; - uint256[] memory erc1155Amounts = new uint256[](2); - uint256 erc721Amount; - - for (uint256 i = 0; i < rewardUnits.length; i++) { - console2.log("----- reward unit number: ", i, "------"); - console2.log("asset contract: ", rewardUnits[i].assetContract); - console2.log("token type: ", uint256(rewardUnits[i].tokenType)); - console2.log("tokenId: ", rewardUnits[i].tokenId); - if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC20) { - console2.log("total amount: ", rewardUnits[i].totalAmount / 1 ether, "ether"); - console.log("balance of recipient: ", erc20.balanceOf(address(recipient)) / 1 ether, "ether"); - erc20Amount += rewardUnits[i].totalAmount; - } else if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC1155) { - console2.log("total amount: ", rewardUnits[i].totalAmount); - console.log("balance of recipient: ", erc1155.balanceOf(address(recipient), rewardUnits[i].tokenId)); - erc1155Amounts[rewardUnits[i].tokenId] += rewardUnits[i].totalAmount; - } else if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC721) { - console2.log("total amount: ", rewardUnits[i].totalAmount); - console.log("balance of recipient: ", erc721.balanceOf(address(recipient))); - erc721Amount += rewardUnits[i].totalAmount; - } - console2.log(""); - } - - assertEq(erc20.balanceOf(address(recipient)), erc20Amount); - assertEq(erc721.balanceOf(address(recipient)), erc721Amount); - - for (uint256 i = 0; i < erc1155Amounts.length; i += 1) { - assertEq(erc1155.balanceOf(address(recipient), i), erc1155Amounts[i]); - } - } - - /** - * note: Testing revert condition; caller of `openPack` is not EOA. - */ - function test_revert_openPack_notEOA() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - vm.startPrank(recipient, address(27)); - string memory err = "!EOA"; - vm.expectRevert(bytes(err)); - pack.openPack(packId, 1); - } - - /** - * note: Cannot open pack again while a previous openPack request is in flight. - */ - function test_revert_openPack_ReqInFlight() public { - vm.warp(1000); - uint256 packId = pack.nextTokenIdToMint(); - uint256 packsToOpen = 3; - address recipient = address(1); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 2, recipient); - - vm.prank(recipient, recipient); - uint256 requestId = pack.openPack(packId, packsToOpen); - console2.log("request ID for opening pack:", requestId); - - vm.expectRevert("ReqInFlight"); - - vm.prank(recipient, recipient); - pack.openPack(packId, packsToOpen); - - vm.expectRevert("ReqInFlight"); - - vm.prank(recipient, recipient); - pack.openPackAndClaimRewards(packId, packsToOpen, 2_500_000); - } - - /** - * note: Testing revert condition; pack owner calls `openPack` to open more than owned packs. - */ - function test_revert_openPack_openMoreThanOwned() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - bytes memory err = "!Bal"; - vm.startPrank(recipient, recipient); - vm.expectRevert(err); - pack.openPack(packId, totalSupply + 1); - } - - /** - * note: Testing revert condition; pack owner calls `openPack` before start timestamp. - */ - function test_revert_openPack_openBeforeStart() public { - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 1000, 1, recipient); - - vm.startPrank(recipient, recipient); - vm.expectRevert("!Open"); - pack.openPack(packId, 1); - } - - /** - * note: Testing revert condition; pack owner calls `openPack` with pack-id non-existent or not owned. - */ - function test_revert_openPack_invalidPackId() public { - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); - - bytes memory err = "!Bal"; - vm.startPrank(recipient, recipient); - vm.expectRevert(err); - pack.openPack(2, 1); - } - - /*/////////////////////////////////////////////////////////////// - Fuzz testing - //////////////////////////////////////////////////////////////*/ - - uint256 internal constant MAX_TOKENS = 2000; - - function getTokensToPack( - uint256 len - ) internal returns (ITokenBundle.Token[] memory tokensToPack, uint256[] memory rewardUnits) { - vm.assume(len < MAX_TOKENS); - tokensToPack = new ITokenBundle.Token[](len); - rewardUnits = new uint256[](len); - - for (uint256 i = 0; i < len; i += 1) { - uint256 random = uint256(keccak256(abi.encodePacked(len + i))) % MAX_TOKENS; - uint256 selector = random % 4; - - if (selector == 0) { - tokensToPack[i] = ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: (random + 1) * 10 ether - }); - rewardUnits[i] = random + 1; - - erc20.mint(address(tokenOwner), tokensToPack[i].totalAmount); - } else if (selector == 1) { - uint256 tokenId = erc721.nextTokenIdToMint(); - - tokensToPack[i] = ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: tokenId, - totalAmount: 1 - }); - rewardUnits[i] = 1; - - erc721.mint(address(tokenOwner), 1); - } else if (selector == 2) { - tokensToPack[i] = ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: random, - totalAmount: (random + 1) * 10 - }); - rewardUnits[i] = random + 1; - - erc1155.mint(address(tokenOwner), tokensToPack[i].tokenId, tokensToPack[i].totalAmount); - } else if (selector == 3) { - tokensToPack[i] = ITokenBundle.Token({ - assetContract: address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 5 ether - }); - rewardUnits[i] = 5; - } - } - } - - function checkBalances( - ITokenBundle.Token[] memory rewardUnits, - address - ) - internal - pure - returns (uint256 nativeTokenAmount, uint256 erc20Amount, uint256[] memory erc1155Amounts, uint256 erc721Amount) - { - erc1155Amounts = new uint256[](MAX_TOKENS); - - for (uint256 i = 0; i < rewardUnits.length; i++) { - // console2.log("----- reward unit number: ", i, "------"); - // console2.log("asset contract: ", rewardUnits[i].assetContract); - // console2.log("token type: ", uint256(rewardUnits[i].tokenType)); - // console2.log("tokenId: ", rewardUnits[i].tokenId); - if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC20) { - if (rewardUnits[i].assetContract == address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)) { - // console2.log("total amount: ", rewardUnits[i].totalAmount / 1 ether, "ether"); - // console.log("balance of recipient: ", address(recipient).balance); - nativeTokenAmount += rewardUnits[i].totalAmount; - } else { - // console2.log("total amount: ", rewardUnits[i].totalAmount / 1 ether, "ether"); - // console.log("balance of recipient: ", erc20.balanceOf(address(recipient)) / 1 ether, "ether"); - erc20Amount += rewardUnits[i].totalAmount; - } - } else if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC1155) { - // console2.log("total amount: ", rewardUnits[i].totalAmount); - // console.log("balance of recipient: ", erc1155.balanceOf(address(recipient), rewardUnits[i].tokenId)); - erc1155Amounts[rewardUnits[i].tokenId] += rewardUnits[i].totalAmount; - } else if (rewardUnits[i].tokenType == ITokenBundle.TokenType.ERC721) { - // console2.log("total amount: ", rewardUnits[i].totalAmount); - // console.log("balance of recipient: ", erc721.balanceOf(address(recipient))); - erc721Amount += rewardUnits[i].totalAmount; - } - // console2.log(""); - } - } - - function test_fuzz_state_createPack(uint256 x, uint128 y) public { - (ITokenBundle.Token[] memory tokensToPack, uint256[] memory rewardUnits) = getTokensToPack(x); - if (tokensToPack.length == 0) { - return; - } - - uint256 packId = pack.nextTokenIdToMint(); - address recipient = address(0x123); - uint256 totalRewardUnits; - uint256 nativeTokenPacked; - - for (uint256 i = 0; i < tokensToPack.length; i += 1) { - totalRewardUnits += rewardUnits[i]; - if (tokensToPack[i].assetContract == address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)) { - nativeTokenPacked += tokensToPack[i].totalAmount; - } - } - vm.deal(address(tokenOwner), nativeTokenPacked); - vm.assume(y > 0 && totalRewardUnits % y == 0); - - vm.prank(address(tokenOwner)); - (, uint256 totalSupply) = pack.createPack{ value: nativeTokenPacked }( - tokensToPack, - rewardUnits, - packUri, - 0, - y, - recipient - ); - console2.log("total supply: ", totalSupply); - console2.log("total reward units: ", totalRewardUnits); - - assertEq(packId + 1, pack.nextTokenIdToMint()); - - (ITokenBundle.Token[] memory packed, ) = pack.getPackContents(packId); - assertEq(packed.length, tokensToPack.length); - for (uint256 i = 0; i < packed.length; i += 1) { - assertEq(packed[i].assetContract, tokensToPack[i].assetContract); - assertEq(uint256(packed[i].tokenType), uint256(tokensToPack[i].tokenType)); - assertEq(packed[i].tokenId, tokensToPack[i].tokenId); - assertEq(packed[i].totalAmount, tokensToPack[i].totalAmount); - } - - assertEq(packUri, pack.uri(packId)); - } - - /*/////////////////////////////////////////////////////////////// - Scenario/Exploit tests - //////////////////////////////////////////////////////////////*/ - /** - * note: Testing revert condition; token owner calls `createPack` to pack owned tokens. - */ - function test_revert_createPack_reentrancy() public { - MaliciousERC20 malERC20 = new MaliciousERC20(payable(address(pack))); - ITokenBundle.Token[] memory content = new ITokenBundle.Token[](1); - uint256[] memory rewards = new uint256[](1); - - malERC20.mint(address(tokenOwner), 10 ether); - content[0] = ITokenBundle.Token({ - assetContract: address(malERC20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }); - rewards[0] = 10; - - tokenOwner.setAllowanceERC20(address(malERC20), address(pack), 10 ether); - - address recipient = address(0x123); - - vm.prank(address(deployer)); - pack.grantRole(keccak256("MINTER_ROLE"), address(malERC20)); - - vm.startPrank(address(tokenOwner)); - vm.expectRevert("ReentrancyGuard: reentrant call"); - pack.createPack(content, rewards, packUri, 0, 1, recipient); - } -} - -contract MaliciousERC20 is MockERC20, ITokenBundle { - Pack public pack; - - constructor(address payable _pack) { - pack = Pack(_pack); - } - - function transferFrom(address from, address to, uint256 amount) public override returns (bool) { - ITokenBundle.Token[] memory content = new ITokenBundle.Token[](1); - uint256[] memory rewards = new uint256[](1); - - address recipient = address(0x123); - pack.createPack(content, rewards, "", 0, 1, recipient); - return super.transferFrom(from, to, amount); - } -} diff --git a/src/test/plugin/Map.t.sol b/src/test/plugin/Map.t.sol deleted file mode 100644 index 1888b9841..000000000 --- a/src/test/plugin/Map.t.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { PluginMap, IPluginMap } from "contracts/extension/plugin/PluginMap.sol"; -import "../utils/BaseTest.sol"; - -contract MapTest is BaseTest { - using Strings for uint256; - PluginMap internal map; - - address[] private pluginAddresses; - IPluginMap.Plugin[] private plugins; - - function setUp() public override { - super.setUp(); - - uint256 total = 50; - - address pluginAddress; - - for (uint256 i = 0; i < total; i += 1) { - if (i % 10 == 0) { - pluginAddress = address(uint160(0x50000 + i)); - pluginAddresses.push(pluginAddress); - } - plugins.push( - IPluginMap.Plugin(bytes4(keccak256(abi.encodePacked(i.toString()))), i.toString(), pluginAddress) - ); - } - - map = new PluginMap(plugins); - } - - function test_state_getPluginForFunction() external { - uint256 len = plugins.length; - for (uint256 i = 0; i < len; i += 1) { - address pluginAddress = plugins[i].pluginAddress; - bytes4 selector = plugins[i].functionSelector; - - assertEq(pluginAddress, map.getPluginForFunction(selector)); - } - } - - function test_state_getAllFunctionsOfPlugin() external { - uint256 len = plugins.length; - for (uint256 i = 0; i < len; i += 1) { - address pluginAddress = plugins[i].pluginAddress; - - uint256 expectedNum; - - for (uint256 j = 0; j < plugins.length; j += 1) { - if (plugins[j].pluginAddress == pluginAddress) { - expectedNum += 1; - } - } - - bytes4[] memory expectedFns = new bytes4[](expectedNum); - uint256 idx; - - for (uint256 j = 0; j < plugins.length; j += 1) { - if (plugins[j].pluginAddress == pluginAddress) { - expectedFns[idx] = plugins[j].functionSelector; - idx += 1; - } - } - - bytes4[] memory fns = map.getAllFunctionsOfPlugin(pluginAddress); - - assertEq(fns.length, expectedNum); - - for (uint256 k = 0; k < fns.length; k += 1) { - assertEq(fns[k], expectedFns[k]); - } - } - } - - function test_state_getAllPlugins() external { - IPluginMap.Plugin[] memory pluginsStored = map.getAllPlugins(); - - for (uint256 i = 0; i < pluginsStored.length; i += 1) { - assertEq(pluginsStored[i].pluginAddress, plugins[i].pluginAddress); - assertEq(pluginsStored[i].functionSelector, plugins[i].functionSelector); - } - } -} diff --git a/src/test/plugin/Router.t.sol b/src/test/plugin/Router.t.sol deleted file mode 100644 index 532f0e1cb..000000000 --- a/src/test/plugin/Router.t.sol +++ /dev/null @@ -1,270 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "contracts/extension/plugin/PluginMap.sol"; -import "contracts/extension/plugin/Router.sol"; -import { BaseTest } from "../utils/BaseTest.sol"; -import "lib/forge-std/src/console.sol"; - -contract RouterImplementation is Router { - constructor(address _functionMap) Router(_functionMap) {} - - function _canSetPlugin() internal pure override returns (bool) { - return true; - } -} - -library CounterStorage { - /// @custom:storage-location erc7201:counter.storage - /// @dev keccak256(abi.encode(uint256(keccak256("counter.storage")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 public constant COUNTER_STORAGE_POSITION = - 0x3a8940d2c88113c2296117248b8b2aedcf41634993b4c0b4ea1a36805e66c300; - - struct Data { - uint256 number; - } - - function counterStorage() internal pure returns (Data storage counterData) { - bytes32 position = COUNTER_STORAGE_POSITION; - assembly { - counterData.slot := position - } - } -} - -contract Counter { - function number() external view returns (uint256) { - CounterStorage.Data storage data = CounterStorage.counterStorage(); - return data.number; - } - - function setNumber(uint256 _newNum) external { - CounterStorage.Data storage data = CounterStorage.counterStorage(); - data.number = _newNum; - } - - function doubleNumber() external { - CounterStorage.Data storage data = CounterStorage.counterStorage(); - data.number *= 4; // Buggy! - } - - function extraFunction() external pure {} -} - -contract CounterAlternate1 { - function doubleNumber() external { - CounterStorage.Data storage data = CounterStorage.counterStorage(); - data.number *= 2; // Fixed! - } -} - -contract CounterAlternate2 { - function tripleNumber() external { - CounterStorage.Data storage data = CounterStorage.counterStorage(); - data.number *= 3; // Fixed! - } -} - -contract RouterTest is BaseTest { - address internal map; - address internal router; - - address internal counter; - address internal counterAlternate1; - address internal counterAlternate2; - - function setUp() public override { - super.setUp(); - - counter = address(new Counter()); - counterAlternate1 = address(new CounterAlternate1()); - counterAlternate2 = address(new CounterAlternate2()); - - IPluginMap.Plugin[] memory pluginMaps = new IPluginMap.Plugin[](3); - pluginMaps[0] = IPluginMap.Plugin(Counter.number.selector, "number()", counter); - pluginMaps[1] = IPluginMap.Plugin(Counter.setNumber.selector, "setNumber(uint256)", counter); - pluginMaps[2] = IPluginMap.Plugin(Counter.doubleNumber.selector, "doubleNumber()", counter); - - map = address(new PluginMap(pluginMaps)); - router = address(new RouterImplementation(map)); - } - - function test_state_addPlugin() external { - // Set number. - uint256 num = 5; - Counter(router).setNumber(num); - assertEq(Counter(router).number(), num); - - // Add extension for `tripleNumber`. - RouterImplementation(payable(router)).addPlugin( - IPluginMap.Plugin(CounterAlternate2.tripleNumber.selector, "tripleNumber()", counterAlternate2) - ); - - // Triple number. - CounterAlternate2(router).tripleNumber(); - assertEq(Counter(router).number(), num * 3); - - // Get and check all overriden extensions. - IPluginMap.Plugin[] memory pluginsStored = RouterImplementation(payable(router)).getAllPlugins(); - assertEq(pluginsStored.length, 4); - - bool isStored; - - for (uint256 i = 0; i < pluginsStored.length; i += 1) { - if (pluginsStored[i].functionSelector == CounterAlternate2.tripleNumber.selector) { - isStored = true; - assertEq(pluginsStored[i].pluginAddress, counterAlternate2); - } - } - - assertTrue(isStored); - } - - function test_revert_addPlugin_defaultExists() external { - vm.expectRevert("Router: default plugin exists for function."); - RouterImplementation(payable(router)).addPlugin( - IPluginMap.Plugin(Counter.doubleNumber.selector, "doubleNumber()", counterAlternate1) - ); - } - - function test_revert_addPlugin_pluginAlreadyExists() external { - RouterImplementation(payable(router)).addPlugin( - IPluginMap.Plugin(CounterAlternate2.tripleNumber.selector, "tripleNumber()", counterAlternate2) - ); - vm.expectRevert(); - RouterImplementation(payable(router)).addPlugin( - IPluginMap.Plugin(CounterAlternate2.tripleNumber.selector, "tripleNumber()", counterAlternate2) - ); - } - - function test_revert_addPlugin_selectorSignatureMismatch() external { - vm.expectRevert("Router: fn selector and signature mismatch."); - RouterImplementation(payable(router)).addPlugin( - IPluginMap.Plugin(CounterAlternate2.tripleNumber.selector, "doubleNumber()", counterAlternate2) - ); - } - - function test_state_updatePlugin() external { - // Set number. - uint256 num = 5; - Counter(router).setNumber(num); - assertEq(Counter(router).number(), num); - - // Double number. Bug: it quadruples the number. - Counter(router).doubleNumber(); - assertEq(Counter(router).number(), num * 4); - - // Reset number. - Counter(router).setNumber(num); - assertEq(Counter(router).number(), num); - - // Fix the extension for `doubleNumber`. - RouterImplementation(payable(router)).updatePlugin( - IPluginMap.Plugin(Counter.doubleNumber.selector, "doubleNumber()", counterAlternate1) - ); - - // Double number. Fixed: it doubles the number. - Counter(router).doubleNumber(); - assertEq(Counter(router).number(), num * 2); - - // Get and check all overriden extensions. - assertEq( - RouterImplementation(payable(router)).getPluginForFunction(Counter.doubleNumber.selector), - counterAlternate1 - ); - - IPluginMap.Plugin[] memory pluginsStored = RouterImplementation(payable(router)).getAllPlugins(); - assertEq(pluginsStored.length, 3); - - bool isStored; - - for (uint256 i = 0; i < pluginsStored.length; i += 1) { - if (pluginsStored[i].functionSelector == Counter.doubleNumber.selector) { - assertEq(pluginsStored[i].pluginAddress, counterAlternate1); - isStored = true; - } - } - - assertTrue(isStored); - } - - function test_state_getAllFunctionsOfPlugin() public { - // add fixed function from counterAlternate - RouterImplementation(payable(router)).updatePlugin( - IPluginMap.Plugin(Counter.doubleNumber.selector, "doubleNumber()", counterAlternate1) - ); - - // add previously not added function of counter - RouterImplementation(payable(router)).addPlugin( - IPluginMap.Plugin(Counter.extraFunction.selector, "extraFunction()", counter) - ); - - // check plugins for counter - bytes4[] memory functions = RouterImplementation(payable(router)).getAllFunctionsOfPlugin(counter); - assertEq(functions.length, 4); - console.logBytes4(functions[0]); - console.logBytes4(functions[1]); - console.logBytes4(functions[2]); - console.logBytes4(functions[3]); - - // check plugins for counterAlternate - functions = RouterImplementation(payable(router)).getAllFunctionsOfPlugin(counterAlternate1); - assertEq(functions.length, 1); - console.logBytes4(functions[0]); - } - - function test_revert_updatePlugin_selectorSignatureMismatch() external { - vm.expectRevert("Router: fn selector and signature mismatch."); - RouterImplementation(payable(router)).updatePlugin( - IPluginMap.Plugin(CounterAlternate1.doubleNumber.selector, "tripleNumber()", counterAlternate2) - ); - } - - function test_revert_updatePlugin_functionDNE() external { - vm.expectRevert("Map: No plugin available for selector"); - RouterImplementation(payable(router)).updatePlugin( - IPluginMap.Plugin(CounterAlternate2.tripleNumber.selector, "tripleNumber()", counterAlternate2) - ); - } - - function test_state_removePlugin() external { - RouterImplementation(payable(router)).addPlugin( - IPluginMap.Plugin(CounterAlternate2.tripleNumber.selector, "tripleNumber()", counterAlternate2) - ); - - assertEq( - RouterImplementation(payable(router)).getPluginForFunction(CounterAlternate2.tripleNumber.selector), - counterAlternate2 - ); - - RouterImplementation(payable(router)).removePlugin(CounterAlternate2.tripleNumber.selector); - - vm.expectRevert("Map: No plugin available for selector"); - RouterImplementation(payable(router)).getPluginForFunction(CounterAlternate2.tripleNumber.selector); - } - - function test_revert_removePlugin_pluginDNE() external { - vm.expectRevert("Router: No plugin available for selector"); - RouterImplementation(payable(router)).removePlugin(CounterAlternate2.tripleNumber.selector); - } - - function test_state_getPluginForFunction() public { - // add fixed function from counterAlternate - RouterImplementation(payable(router)).updatePlugin( - IPluginMap.Plugin(Counter.doubleNumber.selector, "doubleNumber()", counterAlternate1) - ); - - // add previously not added function of counter - RouterImplementation(payable(router)).addPlugin( - IPluginMap.Plugin(Counter.extraFunction.selector, "extraFunction()", counter) - ); - - address pluginAddress = RouterImplementation(payable(router)).getPluginForFunction( - Counter.doubleNumber.selector - ); - assertEq(pluginAddress, counterAlternate1); - - pluginAddress = RouterImplementation(payable(router)).getPluginForFunction(Counter.extraFunction.selector); - assertEq(pluginAddress, counter); - } -} diff --git a/src/test/plugin/RouterImmutable.t.sol b/src/test/plugin/RouterImmutable.t.sol deleted file mode 100644 index 8ad44f4fc..000000000 --- a/src/test/plugin/RouterImmutable.t.sol +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "contracts/extension/plugin/PluginMap.sol"; -import "contracts/extension/plugin/RouterImmutable.sol"; -import { BaseTest } from "../utils/BaseTest.sol"; - -contract Counter { - uint256 private number_; - - function number() external view returns (uint256) { - return number_; - } - - function setNumber(uint256 _newNum) external { - number_ = _newNum; - } - - function doubleNumber() external { - number_ *= 2; - } -} - -contract RouterImmutableTest is BaseTest { - address internal map; - address internal router; - - function setUp() public override { - super.setUp(); - - address counter = address(new Counter()); - - IPluginMap.Plugin[] memory pluginMaps = new IPluginMap.Plugin[](2); - pluginMaps[0] = IPluginMap.Plugin(Counter.number.selector, "number()", counter); - pluginMaps[1] = IPluginMap.Plugin(Counter.setNumber.selector, "setNumber(uint256)", counter); - - map = address(new PluginMap(pluginMaps)); - router = address(new RouterImmutable(map)); - } - - function test_state_callWithRouter() external { - uint256 num = 5; - - Counter(router).setNumber(num); - - assertEq(Counter(router).number(), num); - } - - function test_revert_callWithRouter() external { - vm.expectRevert("Map: No plugin available for selector"); - Counter(router).doubleNumber(); - } -} diff --git a/src/test/scripts/generateRoot.ts b/src/test/scripts/generateRoot.ts deleted file mode 100644 index 128499938..000000000 --- a/src/test/scripts/generateRoot.ts +++ /dev/null @@ -1,31 +0,0 @@ -const { MerkleTree } = require("@thirdweb-dev/merkletree"); - -const keccak256 = require("keccak256"); -const { ethers } = require("ethers"); - -const process = require("process"); - -const members = [ - "0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3", - "0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd", - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -]; - -let val = process.argv[2]; -let price = process.argv[3]; -let currency = process.argv[4]; - -const hashedLeafs = members.map(l => - ethers.utils.solidityKeccak256( - ["address", "uint256", "uint256", "address"], - [l, val, price, currency], - ), -); - -const tree = new MerkleTree(hashedLeafs, keccak256, { - sort: true, - sortLeaves: true, - sortPairs: true, -}); - -process.stdout.write(ethers.utils.defaultAbiCoder.encode(["bytes32"], [tree.getHexRoot()])); diff --git a/src/test/scripts/generateRootAirdrop.ts b/src/test/scripts/generateRootAirdrop.ts deleted file mode 100644 index 4e2c1fdfb..000000000 --- a/src/test/scripts/generateRootAirdrop.ts +++ /dev/null @@ -1,26 +0,0 @@ -const { MerkleTree } = require("@thirdweb-dev/merkletree"); - -const keccak256 = require("keccak256"); -const { ethers } = require("ethers"); - -const process = require("process"); - -const members = [ - "0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3", - "0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd", - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -]; - -let val = process.argv[2]; - -const hashedLeafs = members.map(l => - ethers.utils.solidityKeccak256(["address", "uint256"], [l, val]), -); - -const tree = new MerkleTree(hashedLeafs, keccak256, { - sort: true, - sortLeaves: true, - sortPairs: true, -}); - -process.stdout.write(ethers.utils.defaultAbiCoder.encode(["bytes32"], [tree.getHexRoot()])); diff --git a/src/test/scripts/generateRootAirdrop1155.ts b/src/test/scripts/generateRootAirdrop1155.ts deleted file mode 100644 index d76990fad..000000000 --- a/src/test/scripts/generateRootAirdrop1155.ts +++ /dev/null @@ -1,27 +0,0 @@ -const { MerkleTree } = require("@thirdweb-dev/merkletree"); - -const keccak256 = require("keccak256"); -const { ethers } = require("ethers"); - -const process = require("process"); - -const members = [ - "0x9999999999999999999999999999999999999999", - "0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd", - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -]; - -let tokenId = process.argv[2]; -let quantity = process.argv[3]; - -const hashedLeafs = members.map(l => - ethers.utils.solidityKeccak256(["address", "uint256", "uint256"], [l, tokenId, quantity]), -); - -const tree = new MerkleTree(hashedLeafs, keccak256, { - sort: true, - sortLeaves: true, - sortPairs: true, -}); - -process.stdout.write(ethers.utils.defaultAbiCoder.encode(["bytes32"], [tree.getHexRoot()])); diff --git a/src/test/scripts/getCloneAddress.ts b/src/test/scripts/getCloneAddress.ts deleted file mode 100644 index 27ade61fd..000000000 --- a/src/test/scripts/getCloneAddress.ts +++ /dev/null @@ -1,23 +0,0 @@ -const { MerkleTree } = require("@thirdweb-dev/merkletree"); - -const keccak256 = require("keccak256"); -const { ethers } = require("ethers"); - -const process = require("process"); - -let implementationAddress = process.argv[2]; -let signer = process.argv[3]; -let salthash = process.argv[4]; - -const cloneBytecode = [ - "0x3d602d80600a3d3981f3363d3d373d3d3d363d73", - implementationAddress.replace(/0x/, "").toLowerCase(), - "5af43d82803e903d91602b57fd5bf3", -].join(""); - -const initCodeHash = ethers.utils.solidityKeccak256(["bytes"], [cloneBytecode]); - -const create2Address = ethers.utils.getCreate2Address(signer, salthash, initCodeHash); - -process.stdout.write(create2Address); -// process.stdout.write(ethers.utils.defaultAbiCoder.encode(["bytes32"], [create2Address])); diff --git a/src/test/scripts/getProof.ts b/src/test/scripts/getProof.ts deleted file mode 100644 index 2ef4fd4c5..000000000 --- a/src/test/scripts/getProof.ts +++ /dev/null @@ -1,38 +0,0 @@ -const { MerkleTree } = require("@thirdweb-dev/merkletree"); - -const keccak256 = require("keccak256"); -const { ethers } = require("ethers"); - -const process = require("process"); - -const members = [ - "0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3", - "0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd", - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -]; - -let val = process.argv[2]; -let price = process.argv[3]; -let currency = process.argv[4]; - -const hashedLeafs = members.map(l => - ethers.utils.solidityKeccak256( - ["address", "uint256", "uint256", "address"], - [l, val, price, currency], - ), -); - -const tree = new MerkleTree(hashedLeafs, keccak256, { - sort: true, - sortLeaves: true, - sortPairs: true, -}); - -const expectedProof = tree.getHexProof( - ethers.utils.solidityKeccak256( - ["address", "uint256", "uint256", "address"], - [members[1], val, price, currency], - ), -); - -process.stdout.write(ethers.utils.defaultAbiCoder.encode(["bytes32[]"], [expectedProof])); diff --git a/src/test/scripts/getProofAirdrop.ts b/src/test/scripts/getProofAirdrop.ts deleted file mode 100644 index f99582602..000000000 --- a/src/test/scripts/getProofAirdrop.ts +++ /dev/null @@ -1,30 +0,0 @@ -const { MerkleTree } = require("@thirdweb-dev/merkletree"); - -const keccak256 = require("keccak256"); -const { ethers } = require("ethers"); - -const process = require("process"); - -const members = [ - "0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3", - "0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd", - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -]; - -let val = process.argv[2]; - -const hashedLeafs = members.map(l => - ethers.utils.solidityKeccak256(["address", "uint256"], [l, val]), -); - -const tree = new MerkleTree(hashedLeafs, keccak256, { - sort: true, - sortLeaves: true, - sortPairs: true, -}); - -const expectedProof = tree.getHexProof( - ethers.utils.solidityKeccak256(["address", "uint256"], [members[1], val]), -); - -process.stdout.write(ethers.utils.defaultAbiCoder.encode(["bytes32[]"], [expectedProof])); diff --git a/src/test/scripts/getProofAirdrop1155.ts b/src/test/scripts/getProofAirdrop1155.ts deleted file mode 100644 index 6701fc479..000000000 --- a/src/test/scripts/getProofAirdrop1155.ts +++ /dev/null @@ -1,31 +0,0 @@ -const { MerkleTree } = require("@thirdweb-dev/merkletree"); - -const keccak256 = require("keccak256"); -const { ethers } = require("ethers"); - -const process = require("process"); - -const members = [ - "0x9999999999999999999999999999999999999999", - "0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd", - "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", -]; - -let tokenId = process.argv[2]; -let quantity = process.argv[3]; - -const hashedLeafs = members.map(l => - ethers.utils.solidityKeccak256(["address", "uint256", "uint256"], [l, tokenId, quantity]), -); - -const tree = new MerkleTree(hashedLeafs, keccak256, { - sort: true, - sortLeaves: true, - sortPairs: true, -}); - -const expectedProof = tree.getHexProof( - ethers.utils.solidityKeccak256(["address", "uint256", "uint256"], [members[1], tokenId, quantity]), -); - -process.stdout.write(ethers.utils.defaultAbiCoder.encode(["bytes32[]"], [expectedProof])); diff --git a/src/test/sdk/base/BaseUtilTest.sol b/src/test/sdk/base/BaseUtilTest.sol deleted file mode 100644 index 7ae0b458d..000000000 --- a/src/test/sdk/base/BaseUtilTest.sol +++ /dev/null @@ -1,110 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; -import "../../utils/Wallet.sol"; -import "../../mocks/WETH9.sol"; -import "../../mocks/MockERC20.sol"; -import "../../mocks/MockERC721.sol"; -import "../../mocks/MockERC1155.sol"; -import "contracts/infra/forwarder/Forwarder.sol"; - -abstract contract BaseUtilTest is DSTest, Test { - string public constant NAME = "NAME"; - string public constant SYMBOL = "SYMBOL"; - string public constant CONTRACT_URI = "CONTRACT_URI"; - address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - MockERC20 public erc20; - MockERC721 public erc721; - MockERC1155 public erc1155; - WETH9 public weth; - - address public forwarder; - - address public deployer = address(0x20000); - address public saleRecipient = address(0x30000); - address public royaltyRecipient = address(0x30001); - address public platformFeeRecipient = address(0x30002); - uint128 public royaltyBps = 500; // 5% - uint128 public platformFeeBps = 500; // 5% - uint256 public constant MAX_BPS = 10_000; // 100% - - uint256 public privateKey = 1234; - address public signer; - - mapping(bytes32 => address) public contracts; - - function setUp() public virtual { - signer = vm.addr(privateKey); - - erc20 = new MockERC20(); - erc721 = new MockERC721(); - erc1155 = new MockERC1155(); - weth = new WETH9(); - forwarder = address(new Forwarder()); - } - - function getActor(uint160 _index) public pure returns (address) { - return address(uint160(0x50000 + _index)); - } - - function getWallet() public returns (Wallet wallet) { - wallet = new Wallet(); - } - - function assertIsOwnerERC721(address _token, address _owner, uint256[] memory _tokenIds) internal { - for (uint256 i = 0; i < _tokenIds.length; i += 1) { - bool isOwnerOfToken = MockERC721(_token).ownerOf(_tokenIds[i]) == _owner; - assertTrue(isOwnerOfToken); - } - } - - function assertIsNotOwnerERC721(address _token, address _owner, uint256[] memory _tokenIds) internal { - for (uint256 i = 0; i < _tokenIds.length; i += 1) { - bool isOwnerOfToken = MockERC721(_token).ownerOf(_tokenIds[i]) == _owner; - assertTrue(!isOwnerOfToken); - } - } - - function assertBalERC1155Eq( - address _token, - address _owner, - uint256[] memory _tokenIds, - uint256[] memory _amounts - ) internal { - require(_tokenIds.length == _amounts.length, "unequal lengths"); - - for (uint256 i = 0; i < _tokenIds.length; i += 1) { - assertEq(MockERC1155(_token).balanceOf(_owner, _tokenIds[i]), _amounts[i]); - } - } - - function assertBalERC1155Gte( - address _token, - address _owner, - uint256[] memory _tokenIds, - uint256[] memory _amounts - ) internal { - require(_tokenIds.length == _amounts.length, "unequal lengths"); - - for (uint256 i = 0; i < _tokenIds.length; i += 1) { - assertTrue(MockERC1155(_token).balanceOf(_owner, _tokenIds[i]) >= _amounts[i]); - } - } - - function assertBalERC20Eq(address _token, address _owner, uint256 _amount) internal { - assertEq(MockERC20(_token).balanceOf(_owner), _amount); - } - - function assertBalERC20Gte(address _token, address _owner, uint256 _amount) internal { - assertTrue(MockERC20(_token).balanceOf(_owner) >= _amount); - } - - function forwarders() public view returns (address[] memory) { - address[] memory _forwarders = new address[](1); - _forwarders[0] = forwarder; - return _forwarders; - } -} diff --git a/src/test/sdk/base/ERC1155Base.t.sol b/src/test/sdk/base/ERC1155Base.t.sol deleted file mode 100644 index f6976a3da..000000000 --- a/src/test/sdk/base/ERC1155Base.t.sol +++ /dev/null @@ -1,498 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { ERC1155Base } from "contracts/base/ERC1155Base.sol"; -import { Strings } from "contracts/lib/Strings.sol"; - -contract ERC1155BaseTest is DSTest, Test { - using Strings for uint256; - - // Target contract - ERC1155Base internal base; - - // Signers - address internal admin; - address internal nftHolder; - - function setUp() public { - admin = address(0x123); - nftHolder = address(0x456); - - vm.prank(admin); - base = new ERC1155Base(admin, "name", "symbol", admin, 0); - } - - // ================== `mintTo` tests ======================== - - function test_state_mintTo_newNFTs() public { - uint256 tokenId = type(uint256).max; - string memory tokenURI = "ipfs://"; - uint256 amount = 100; - - uint256 expectedTokenIdMinted = base.nextTokenIdToMint(); - - vm.prank(admin); - base.mintTo(nftHolder, tokenId, tokenURI, amount); - - assertEq(base.balanceOf(nftHolder, expectedTokenIdMinted), amount); - assertEq(base.totalSupply(expectedTokenIdMinted), amount); - assertEq(base.nextTokenIdToMint(), expectedTokenIdMinted + 1); - assertEq(base.uri(expectedTokenIdMinted), tokenURI); - } - - function test_state_mintTo_existingNFTs() public { - string memory tokenURI = "ipfs://"; - uint256 startAmount = 1; - - uint256 tokenIdMinted = base.nextTokenIdToMint(); - - vm.prank(admin); - base.mintTo(admin, type(uint256).max, tokenURI, startAmount); - - assertEq(base.uri(tokenIdMinted), tokenURI); - assertEq(base.totalSupply(tokenIdMinted), startAmount); - assertEq(base.nextTokenIdToMint(), tokenIdMinted + 1); - - uint256 additionalAmount = 100; - - vm.prank(admin); - base.mintTo(nftHolder, tokenIdMinted, "", additionalAmount); - - assertEq(base.balanceOf(nftHolder, tokenIdMinted), additionalAmount); - assertEq(base.totalSupply(tokenIdMinted), additionalAmount + startAmount); - } - - function test_revert_mintTo_unauthorizedCaller() public { - uint256 tokenId = type(uint256).max; - string memory tokenURI = "ipfs://"; - uint256 amount = 100; - - vm.prank(nftHolder); - vm.expectRevert("Not authorized to mint."); - base.mintTo(nftHolder, tokenId, tokenURI, amount); - } - - function test_revert_mintTo_invalidId() public { - string memory tokenURI = "ipfs://"; - uint256 amount = 100; - - uint256 nextId = base.nextTokenIdToMint(); - - vm.prank(admin); - vm.expectRevert("invalid id"); - base.mintTo(nftHolder, nextId, tokenURI, amount); - } - - // ================== `mintTo` tests ======================== - - function test_state_batchMintTo_newNFTs() public { - uint256 numToMint = 3; - uint256[] memory tokenIds = new uint256[](numToMint); - uint256[] memory amounts = new uint256[](numToMint); - uint256[] memory expectedTokenIds = new uint256[](numToMint); - string memory baseURI = "ipfs://"; - - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = 0; i < numToMint; i += 1) { - tokenIds[i] = type(uint256).max; - amounts[i] = 100; - - expectedTokenIds[i] = nextId; - nextId += 1; - } - - vm.prank(admin); - base.batchMintTo(nftHolder, tokenIds, amounts, baseURI); - - for (uint256 i = 0; i < numToMint; i += 1) { - uint256 id = expectedTokenIds[i]; - - assertEq(base.balanceOf(nftHolder, id), amounts[i]); - assertEq(base.totalSupply(id), amounts[i]); - assertEq(base.uri(id), string(abi.encodePacked(baseURI, id.toString()))); - } - } - - function test_state_batchMintTo_existingNFTs() public { - uint256 numToMint = 3; - uint256[] memory tokenIds = new uint256[](numToMint); - uint256[] memory startAmounts = new uint256[](numToMint); - uint256[] memory nextAmounts = new uint256[](numToMint); - uint256[] memory expectedTokenIds = new uint256[](numToMint); - string memory baseURI = "ipfs://"; - - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = 0; i < numToMint; i += 1) { - tokenIds[i] = type(uint256).max; - startAmounts[i] = 1; - nextAmounts[i] = 99; - - expectedTokenIds[i] = nextId; - nextId += 1; - } - - vm.prank(admin); - base.batchMintTo(admin, tokenIds, startAmounts, baseURI); - - for (uint256 i = 0; i < numToMint; i += 1) { - uint256 id = expectedTokenIds[i]; - - assertEq(base.balanceOf(admin, id), startAmounts[i]); - assertEq(base.totalSupply(id), startAmounts[i]); - assertEq(base.uri(id), string(abi.encodePacked(baseURI, id.toString()))); - } - - vm.prank(admin); - base.batchMintTo(nftHolder, expectedTokenIds, nextAmounts, ""); - - for (uint256 i = 0; i < numToMint; i += 1) { - uint256 id = expectedTokenIds[i]; - - assertEq(base.balanceOf(nftHolder, id), nextAmounts[i]); - assertEq(base.totalSupply(id), startAmounts[i] + nextAmounts[i]); - assertEq(base.uri(id), string(abi.encodePacked(baseURI, id.toString()))); - } - } - - function test_state_batchMintTo_newAndExistingNFTs() public { - uint256 numToMint = 3; - uint256[] memory tokenIds = new uint256[](numToMint); - uint256[] memory startAmounts = new uint256[](numToMint); - uint256[] memory nextAmounts = new uint256[](numToMint); - uint256[] memory expectedTokenIds = new uint256[](numToMint); - string memory baseURI = "ipfs://"; - - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = 0; i < numToMint; i += 1) { - tokenIds[i] = type(uint256).max; - startAmounts[i] = 1; - nextAmounts[i] = 99; - - expectedTokenIds[i] = nextId; - nextId += 1; - } - - vm.prank(admin); - base.batchMintTo(admin, tokenIds, startAmounts, baseURI); - - for (uint256 i = 0; i < numToMint; i += 1) { - uint256 id = expectedTokenIds[i]; - - assertEq(base.balanceOf(admin, id), startAmounts[i]); - assertEq(base.totalSupply(id), startAmounts[i]); - assertEq(base.uri(id), string(abi.encodePacked(baseURI, id.toString()))); - } - - uint256[] memory newAndExistingTokenIds = new uint256[](numToMint + 1); - uint256[] memory newAmounts = new uint256[](numToMint + 1); - for (uint256 i = 0; i < numToMint; i += 1) { - newAndExistingTokenIds[i] = expectedTokenIds[i]; - newAmounts[i] = nextAmounts[i]; - } - newAndExistingTokenIds[numToMint] = type(uint256).max; - newAmounts[numToMint] = 100; - - uint256 expectedNewId = base.nextTokenIdToMint(); - string memory baseURIForNewNFT = "newipfs://"; - - vm.prank(admin); - base.batchMintTo(nftHolder, newAndExistingTokenIds, newAmounts, baseURIForNewNFT); - - for (uint256 i = 0; i < newAndExistingTokenIds.length; i += 1) { - if (i < numToMint) { - uint256 id = newAndExistingTokenIds[i]; - - assertEq(base.balanceOf(nftHolder, id), newAmounts[i]); - assertEq(base.totalSupply(id), startAmounts[i] + newAmounts[i]); - assertEq(base.uri(id), string(abi.encodePacked(baseURI, id.toString()))); - } else { - uint256 id = expectedNewId; - assertEq(base.balanceOf(nftHolder, id), newAmounts[i]); - assertEq(base.totalSupply(id), newAmounts[i]); - assertEq(base.uri(id), string(abi.encodePacked(baseURIForNewNFT, id.toString()))); - } - } - } - - function test_revert_batchMintTo_unauthorizedCaller() public { - uint256 numToMint = 3; - uint256[] memory tokenIds = new uint256[](numToMint); - uint256[] memory amounts = new uint256[](numToMint); - uint256[] memory expectedTokenIds = new uint256[](numToMint); - string memory baseURI = "ipfs://"; - - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = 0; i < numToMint; i += 1) { - tokenIds[i] = type(uint256).max; - amounts[i] = 100; - - expectedTokenIds[i] = nextId; - nextId += 1; - } - - vm.prank(nftHolder); - vm.expectRevert("Not authorized to mint."); - base.batchMintTo(nftHolder, tokenIds, amounts, baseURI); - } - - function test_revert_batchMintTo_mintingZeroTokens() public { - uint256 numToMint = 3; - uint256[] memory tokenIds = new uint256[](numToMint); - uint256[] memory amounts = new uint256[](0); - uint256[] memory expectedTokenIds = new uint256[](numToMint); - string memory baseURI = "ipfs://"; - - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = 0; i < numToMint; i += 1) { - tokenIds[i] = type(uint256).max; - - expectedTokenIds[i] = nextId; - nextId += 1; - } - - vm.prank(admin); - vm.expectRevert("Minting zero tokens."); - base.batchMintTo(nftHolder, tokenIds, amounts, baseURI); - } - - function test_revert_batchMintTo_lengthMismatch() public { - uint256 numToMint = 3; - uint256[] memory tokenIds = new uint256[](numToMint + 1); - uint256[] memory amounts = new uint256[](numToMint); - uint256[] memory expectedTokenIds = new uint256[](numToMint); - string memory baseURI = "ipfs://"; - - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = 0; i < numToMint; i += 1) { - tokenIds[i] = type(uint256).max; - amounts[i] = 100; - - expectedTokenIds[i] = nextId; - nextId += 1; - } - - vm.prank(admin); - vm.expectRevert("Length mismatch."); - base.batchMintTo(nftHolder, tokenIds, amounts, baseURI); - } - - function test_revert_batchMintTo_invalidId() public { - uint256 numToMint = 3; - uint256[] memory tokenIds = new uint256[](numToMint); - uint256[] memory amounts = new uint256[](numToMint); - uint256[] memory expectedTokenIds = new uint256[](numToMint); - string memory baseURI = "ipfs://"; - - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = 0; i < numToMint; i += 1) { - tokenIds[i] = i; - amounts[i] = 100; - - expectedTokenIds[i] = nextId; - nextId += 1; - } - - vm.prank(admin); - vm.expectRevert("invalid id"); - base.batchMintTo(nftHolder, tokenIds, amounts, baseURI); - } - - function test_state_burn() public { - uint256 tokenId = type(uint256).max; - string memory tokenURI = "ipfs://"; - uint256 amount = 100; - - uint256 expectedTokenIdMinted = base.nextTokenIdToMint(); - - vm.prank(admin); - base.mintTo(nftHolder, tokenId, tokenURI, amount); - - assertEq(base.balanceOf(nftHolder, expectedTokenIdMinted), amount); - assertEq(base.totalSupply(expectedTokenIdMinted), amount); - - vm.prank(nftHolder); - base.burn(nftHolder, expectedTokenIdMinted, amount); - - assertEq(base.balanceOf(nftHolder, expectedTokenIdMinted), 0); - assertEq(base.totalSupply(expectedTokenIdMinted), 0); - } - - function test_revert_burn_unapprovedCaller() public { - uint256 tokenId = type(uint256).max; - string memory tokenURI = "ipfs://"; - uint256 amount = 100; - - uint256 expectedTokenIdMinted = base.nextTokenIdToMint(); - - vm.prank(admin); - base.mintTo(nftHolder, tokenId, tokenURI, amount); - - assertEq(base.balanceOf(nftHolder, expectedTokenIdMinted), amount); - assertEq(base.totalSupply(expectedTokenIdMinted), amount); - - vm.prank(admin); - vm.expectRevert("Unapproved caller"); - base.burn(nftHolder, expectedTokenIdMinted, amount); - } - - function test_revert_burn_notEnoughTokensOwned() public { - uint256 tokenId = type(uint256).max; - string memory tokenURI = "ipfs://"; - uint256 amount = 100; - - uint256 expectedTokenIdMinted = base.nextTokenIdToMint(); - - vm.prank(admin); - base.mintTo(nftHolder, tokenId, tokenURI, amount); - - assertEq(base.balanceOf(nftHolder, expectedTokenIdMinted), amount); - assertEq(base.totalSupply(expectedTokenIdMinted), amount); - - vm.prank(nftHolder); - vm.expectRevert("Not enough tokens owned"); - base.burn(nftHolder, expectedTokenIdMinted, amount + 1); - } - - function test_state_burnBatch() public { - uint256 numToMint = 3; - uint256[] memory tokenIds = new uint256[](numToMint); - uint256[] memory amounts = new uint256[](numToMint); - uint256[] memory expectedTokenIds = new uint256[](numToMint); - string memory baseURI = "ipfs://"; - - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = 0; i < numToMint; i += 1) { - tokenIds[i] = type(uint256).max; - amounts[i] = 100; - - expectedTokenIds[i] = nextId; - nextId += 1; - } - - vm.prank(admin); - base.batchMintTo(nftHolder, tokenIds, amounts, baseURI); - - for (uint256 i = 0; i < numToMint; i += 1) { - uint256 id = expectedTokenIds[i]; - - assertEq(base.balanceOf(nftHolder, id), amounts[i]); - assertEq(base.totalSupply(id), amounts[i]); - } - - vm.prank(nftHolder); - base.burnBatch(nftHolder, expectedTokenIds, amounts); - - for (uint256 i = 0; i < numToMint; i += 1) { - uint256 id = expectedTokenIds[i]; - - assertEq(base.balanceOf(nftHolder, id), 0); - assertEq(base.totalSupply(id), 0); - } - } - - function test_revert_burnBatch_unapprovedCaller() public { - uint256 numToMint = 3; - uint256[] memory tokenIds = new uint256[](numToMint); - uint256[] memory amounts = new uint256[](numToMint); - uint256[] memory expectedTokenIds = new uint256[](numToMint); - string memory baseURI = "ipfs://"; - - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = 0; i < numToMint; i += 1) { - tokenIds[i] = type(uint256).max; - amounts[i] = 100; - - expectedTokenIds[i] = nextId; - nextId += 1; - } - - vm.prank(admin); - base.batchMintTo(nftHolder, tokenIds, amounts, baseURI); - - for (uint256 i = 0; i < numToMint; i += 1) { - uint256 id = expectedTokenIds[i]; - - assertEq(base.balanceOf(nftHolder, id), amounts[i]); - assertEq(base.totalSupply(id), amounts[i]); - } - - vm.prank(admin); - vm.expectRevert("Unapproved caller"); - base.burnBatch(nftHolder, expectedTokenIds, amounts); - } - - function test_revert_burnBatch_lengthMismatch() public { - uint256 numToMint = 3; - uint256[] memory tokenIds = new uint256[](numToMint); - uint256[] memory amounts = new uint256[](numToMint); - uint256[] memory mockAmounts = new uint256[](numToMint + 1); - uint256[] memory expectedTokenIds = new uint256[](numToMint); - string memory baseURI = "ipfs://"; - - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = 0; i < numToMint; i += 1) { - tokenIds[i] = type(uint256).max; - amounts[i] = 100; - mockAmounts[i] = 100; - - expectedTokenIds[i] = nextId; - nextId += 1; - } - - vm.prank(admin); - base.batchMintTo(nftHolder, tokenIds, amounts, baseURI); - - for (uint256 i = 0; i < numToMint; i += 1) { - uint256 id = expectedTokenIds[i]; - - assertEq(base.balanceOf(nftHolder, id), amounts[i]); - assertEq(base.totalSupply(id), amounts[i]); - } - - vm.prank(nftHolder); - vm.expectRevert("Length mismatch"); - base.burnBatch(nftHolder, expectedTokenIds, mockAmounts); - } - - function test_revert_burnBatch_notEnoughTokensOwned() public { - uint256 numToMint = 3; - uint256[] memory tokenIds = new uint256[](numToMint); - uint256[] memory amounts = new uint256[](numToMint); - uint256[] memory expectedTokenIds = new uint256[](numToMint); - string memory baseURI = "ipfs://"; - - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = 0; i < numToMint; i += 1) { - tokenIds[i] = type(uint256).max; - amounts[i] = 100; - - expectedTokenIds[i] = nextId; - nextId += 1; - } - - vm.prank(admin); - base.batchMintTo(nftHolder, tokenIds, amounts, baseURI); - - for (uint256 i = 0; i < numToMint; i += 1) { - amounts[i] += 1; - } - - vm.prank(nftHolder); - vm.expectRevert("Not enough tokens owned"); - base.burnBatch(nftHolder, expectedTokenIds, amounts); - } -} diff --git a/src/test/sdk/base/ERC1155DelayedReveal.t.sol b/src/test/sdk/base/ERC1155DelayedReveal.t.sol deleted file mode 100644 index ffc0e1a06..000000000 --- a/src/test/sdk/base/ERC1155DelayedReveal.t.sol +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { ERC1155DelayedReveal } from "contracts/base/ERC1155DelayedReveal.sol"; -import { Strings } from "contracts/lib/Strings.sol"; - -contract ERC1155DelayedRevealTest is DSTest, Test { - using Strings for uint256; - - // Target contract - ERC1155DelayedReveal internal base; - - // Signers - address internal admin; - address internal nftHolder; - - // Lazy mitning args - uint256 internal lazymintAmount = 10; - string internal baseURI = "ipfs://"; - string internal placeholderURI = "placeholderURI://"; - bytes internal key = "key"; - - function setUp() public { - admin = address(0x123); - nftHolder = address(0x456); - - vm.prank(admin); - base = new ERC1155DelayedReveal(admin, "name", "symbol", admin, 0); - - bytes memory encryptedBaseURI = base.encryptDecrypt(bytes(baseURI), key); - bytes32 provenanceHash = keccak256(abi.encodePacked(baseURI, key, block.chainid)); - vm.prank(admin); - base.lazyMint(lazymintAmount, placeholderURI, abi.encode(encryptedBaseURI, provenanceHash)); - } - - function test_state_reveal() public { - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = 0; i < nextId; i += 1) { - assertEq(base.uri(i), string(abi.encodePacked(placeholderURI, "0"))); - } - - vm.prank(admin); - base.reveal(0, key); - - for (uint256 i = 0; i < nextId; i += 1) { - assertEq(base.uri(i), string(abi.encodePacked(baseURI, i.toString()))); - } - } - - function test_state_reveal_additionalBatch() public { - uint256 nextIdBefore = base.nextTokenIdToMint(); - - string memory newBaseURI = "ipfsNew://"; - string memory newPlaceholderURI = "placeholderURINew://"; - bytes memory newKey = "newkey"; - - bytes memory encryptedBaseURI = base.encryptDecrypt(bytes(newBaseURI), newKey); - bytes32 provenanceHash = keccak256(abi.encodePacked(newBaseURI, newKey, block.chainid)); - vm.prank(admin); - base.lazyMint(lazymintAmount, newPlaceholderURI, abi.encode(encryptedBaseURI, provenanceHash)); - - uint256 nextId = base.nextTokenIdToMint(); - - for (uint256 i = nextIdBefore; i < nextId; i += 1) { - assertEq(base.uri(i), string(abi.encodePacked(newPlaceholderURI, "0"))); - } - - vm.prank(admin); - base.reveal(1, newKey); - - for (uint256 i = nextIdBefore; i < nextId; i += 1) { - assertEq(base.uri(i), string(abi.encodePacked(newBaseURI, i.toString()))); - } - } -} diff --git a/src/test/sdk/base/ERC1155Drop.t.sol b/src/test/sdk/base/ERC1155Drop.t.sol deleted file mode 100644 index 580fe3b77..000000000 --- a/src/test/sdk/base/ERC1155Drop.t.sol +++ /dev/null @@ -1,490 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { ERC1155Drop } from "contracts/base/ERC1155Drop.sol"; -import { Strings } from "contracts/lib/Strings.sol"; - -contract ERC1155DropTest is DSTest, Test { - using Strings for uint256; - - // Target contract - ERC1155Drop internal base; - - // Signers - uint256 internal adminPkey; - uint256 internal nftHolderPkey; - - address internal admin; - address internal nftHolder; - address internal saleRecipient; - - ERC1155Drop.ClaimCondition condition; - ERC1155Drop.AllowlistProof allowlistProof; - - uint256 internal targetTokenId; - - function setUp() public { - adminPkey = 123; - nftHolderPkey = 456; - - admin = vm.addr(adminPkey); - nftHolder = vm.addr(nftHolderPkey); - saleRecipient = address(0x8910); - - vm.deal(nftHolder, 100 ether); - - vm.prank(admin); - base = new ERC1155Drop(admin, "name", "symbol", admin, 0, saleRecipient); - - targetTokenId = base.nextTokenIdToMint(); - - vm.prank(admin); - base.lazyMint(1, "ipfs://", ""); - } - - function test_state_setClaimConditions() public { - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 5; - condition.merkleRoot = bytes32(0); - condition.pricePerToken = 0; - condition.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - (, uint256 maxClaimableSupplyBefore, , , , , , ) = base.claimCondition(targetTokenId); - - assertEq(maxClaimableSupplyBefore, 0); - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, true); - - (, uint256 maxClaimable, , uint256 quantityLimitPerWallet, , , address currency, ) = base.claimCondition( - targetTokenId - ); - - assertEq(maxClaimable, 100); - assertEq(quantityLimitPerWallet, 5); - assertEq(currency, 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); - } - - function test_state_setClaimConditions_resetEligibility() public { - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 5; - condition.merkleRoot = bytes32(0); - condition.pricePerToken = 0; - condition.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, false); - - vm.prank(nftHolder, nftHolder); - base.claim(nftHolder, targetTokenId, 1, condition.currency, condition.pricePerToken, allowlistProof, ""); - - (, , uint256 supplyClaimedBefore, , , , , ) = base.claimCondition(targetTokenId); - assertEq(supplyClaimedBefore, 1); - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, false); - - (, , uint256 supplyClaimedAfter, , , , , ) = base.claimCondition(targetTokenId); - assertEq(supplyClaimedBefore, supplyClaimedAfter); - } - - function test_revert_setClaimConditions_unauthorizedCaller() public { - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 5; - condition.merkleRoot = bytes32(0); - condition.pricePerToken = 0; - condition.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - (, uint256 maxClaimableSupplyBefore, , , , , , ) = base.claimCondition(targetTokenId); - - assertEq(maxClaimableSupplyBefore, 0); - - vm.prank(nftHolder); - vm.expectRevert("Not authorized"); - base.setClaimConditions(targetTokenId, condition, true); - } - - function test_revert_setClaimConditions_supplyClaimedAlready() public { - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 100; - condition.merkleRoot = bytes32(0); - condition.pricePerToken = 0; - condition.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, false); - - vm.prank(nftHolder, nftHolder); - base.claim( - nftHolder, - targetTokenId, - condition.quantityLimitPerWallet, - condition.currency, - condition.pricePerToken, - allowlistProof, - "" - ); - - condition.maxClaimableSupply = 50; - - vm.prank(admin); - vm.expectRevert("max supply claimed"); - base.setClaimConditions(targetTokenId, condition, false); - } - - function test_state_claim() public { - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 5; - condition.merkleRoot = bytes32(0); - condition.pricePerToken = 0; - condition.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - (, uint256 maxClaimableSupplyBefore, , , , , , ) = base.claimCondition(targetTokenId); - - assertEq(maxClaimableSupplyBefore, 0); - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, true); - - uint256 quantityToClaim = 5; - vm.prank(nftHolder, nftHolder); - base.claim( - nftHolder, - targetTokenId, - quantityToClaim, - condition.currency, - condition.pricePerToken, - allowlistProof, - "" - ); - - (, , uint256 supplyClaimed, , , , , ) = base.claimCondition(targetTokenId); - assertEq(supplyClaimed, quantityToClaim); - - assertEq(base.balanceOf(nftHolder, targetTokenId), quantityToClaim); - assertEq(base.totalSupply(targetTokenId), quantityToClaim); - } - - function test_state_claim_withPrice() public { - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 5; - condition.merkleRoot = bytes32(0); - condition.pricePerToken = 0.01 ether; - condition.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - (, uint256 maxClaimableSupplyBefore, , , , , , ) = base.claimCondition(targetTokenId); - - assertEq(maxClaimableSupplyBefore, 0); - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, true); - - uint256 quantityToClaim = 5; - uint256 totalPrice = quantityToClaim * condition.pricePerToken; - - uint256 saleRecipientBalBefore = saleRecipient.balance; - - vm.prank(nftHolder, nftHolder); - base.claim{ value: totalPrice }( - nftHolder, - targetTokenId, - quantityToClaim, - condition.currency, - condition.pricePerToken, - allowlistProof, - "" - ); - - assertEq(saleRecipient.balance, saleRecipientBalBefore + totalPrice); - } - - function test_state_claim_withAllowlist() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "1"; - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address claimer = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 5; - condition.merkleRoot = root; - condition.pricePerToken = 0; - condition.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - (, uint256 maxClaimableSupplyBefore, , , , , , ) = base.claimCondition(targetTokenId); - - assertEq(maxClaimableSupplyBefore, 0); - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, true); - - allowlistProof.proof = proofs; - allowlistProof.quantityLimitPerWallet = 1; - allowlistProof.pricePerToken = 0; - allowlistProof.currency = address(0); - - uint256 quantityToClaim = allowlistProof.quantityLimitPerWallet; - - vm.prank(claimer, claimer); - base.claim( - nftHolder, - targetTokenId, - quantityToClaim, - condition.currency, - condition.pricePerToken, - allowlistProof, - "" - ); - - (, , uint256 supplyClaimed, , , , , ) = base.claimCondition(targetTokenId); - assertEq(supplyClaimed, quantityToClaim); - - assertEq(base.balanceOf(nftHolder, targetTokenId), quantityToClaim); - assertEq(base.totalSupply(targetTokenId), quantityToClaim); - } - - function test_revert_claim_invalidQtyProof() public { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = "1"; - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - address claimer = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 5; - condition.merkleRoot = root; - condition.pricePerToken = 0; - condition.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - (, uint256 maxClaimableSupplyBefore, , , , , , ) = base.claimCondition(targetTokenId); - - assertEq(maxClaimableSupplyBefore, 0); - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, true); - - allowlistProof.proof = proofs; - allowlistProof.quantityLimitPerWallet = 1; - allowlistProof.pricePerToken = 0; - allowlistProof.currency = address(0); - - uint256 quantityToClaim = allowlistProof.quantityLimitPerWallet + 1; - - bytes memory errorQty = "!Qty"; - - vm.prank(claimer, claimer); - vm.expectRevert(errorQty); - base.claim( - nftHolder, - targetTokenId, - quantityToClaim, - condition.currency, - condition.pricePerToken, - allowlistProof, - "" - ); - } - - function test_revert_claim_invalidPrice() public { - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 5; - condition.merkleRoot = bytes32(0); - condition.pricePerToken = 0.01 ether; - condition.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - (, uint256 maxClaimableSupplyBefore, , , , , , ) = base.claimCondition(targetTokenId); - - assertEq(maxClaimableSupplyBefore, 0); - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, true); - - uint256 quantityToClaim = 5; - uint256 totalPrice = quantityToClaim * condition.pricePerToken; - - vm.prank(nftHolder, nftHolder); - vm.expectRevert("!PriceOrCurrency"); - base.claim{ value: totalPrice - 1 }( - nftHolder, - targetTokenId, - quantityToClaim, - condition.currency, - 0, - allowlistProof, - "" - ); - } - - function test_revert_claim_insufficientPrice() public { - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 5; - condition.merkleRoot = bytes32(0); - condition.pricePerToken = 0.01 ether; - condition.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - (, uint256 maxClaimableSupplyBefore, , , , , , ) = base.claimCondition(targetTokenId); - - assertEq(maxClaimableSupplyBefore, 0); - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, true); - - uint256 quantityToClaim = 5; - uint256 totalPrice = quantityToClaim * condition.pricePerToken; - - vm.prank(nftHolder, nftHolder); - vm.expectRevert("Invalid msg value"); - base.claim{ value: totalPrice - 1 }( - nftHolder, - targetTokenId, - quantityToClaim, - condition.currency, - condition.pricePerToken, - allowlistProof, - "" - ); - } - - function test_revert_claim_invalidCurrency() public { - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 5; - condition.merkleRoot = bytes32(0); - condition.pricePerToken = 0.01 ether; - condition.currency = address(0x123); - - (, uint256 maxClaimableSupplyBefore, , , , , , ) = base.claimCondition(targetTokenId); - - assertEq(maxClaimableSupplyBefore, 0); - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, true); - - uint256 quantityToClaim = 5; - uint256 totalPrice = quantityToClaim * condition.pricePerToken; - - vm.prank(nftHolder, nftHolder); - vm.expectRevert("!PriceOrCurrency"); - base.claim{ value: totalPrice }( - nftHolder, - targetTokenId, - quantityToClaim, - 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, - condition.pricePerToken, - allowlistProof, - "" - ); - } - - function test_revert_claim_invalidQuantity() public { - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 5; - condition.merkleRoot = bytes32(0); - condition.pricePerToken = 0.01 ether; - condition.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - (, uint256 maxClaimableSupplyBefore, , , , , , ) = base.claimCondition(targetTokenId); - - assertEq(maxClaimableSupplyBefore, 0); - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, true); - - uint256 quantityToClaim = condition.quantityLimitPerWallet + 1; - uint256 totalPrice = quantityToClaim * condition.pricePerToken; - - bytes memory errorQty = "!Qty"; - - vm.prank(nftHolder, nftHolder); - vm.expectRevert(errorQty); - base.claim{ value: totalPrice }( - nftHolder, - targetTokenId, - quantityToClaim, - condition.currency, - condition.pricePerToken, - allowlistProof, - "" - ); - } - - function test_revert_claim_exceedsMaxSupply() public { - condition.startTimestamp = block.timestamp; - condition.maxClaimableSupply = 100; - condition.supplyClaimed = 0; - condition.quantityLimitPerWallet = 101; - condition.merkleRoot = bytes32(0); - condition.pricePerToken = 0.01 ether; - condition.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - (, uint256 maxClaimableSupplyBefore, , , , , , ) = base.claimCondition(targetTokenId); - - assertEq(maxClaimableSupplyBefore, 0); - - vm.prank(admin); - base.setClaimConditions(targetTokenId, condition, true); - - uint256 quantityToClaim = condition.quantityLimitPerWallet; - uint256 totalPrice = quantityToClaim * condition.pricePerToken; - - vm.prank(nftHolder, nftHolder); - vm.expectRevert("!MaxSupply"); - base.claim{ value: totalPrice }( - nftHolder, - targetTokenId, - quantityToClaim, - condition.currency, - condition.pricePerToken, - allowlistProof, - "" - ); - } -} diff --git a/src/test/sdk/base/ERC1155LazyMint.t.sol b/src/test/sdk/base/ERC1155LazyMint.t.sol deleted file mode 100644 index 3d83c1b94..000000000 --- a/src/test/sdk/base/ERC1155LazyMint.t.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { ERC1155LazyMint } from "contracts/base/ERC1155LazyMint.sol"; -import { Strings } from "contracts/lib/Strings.sol"; - -contract ERC1155LazyMintTest is DSTest, Test { - using Strings for uint256; - - // Target contract - ERC1155LazyMint internal base; - - // Signers - address internal admin; - address internal nftHolder; - - // Lazy mitning args - uint256 internal lazymintAmount = 10; - string internal baseURI = "ipfs://"; - - function setUp() public { - admin = address(0x123); - nftHolder = address(0x456); - - vm.prank(admin); - base = new ERC1155LazyMint(admin, "name", "symbol", admin, 0); - - // Lazy mint tokens - vm.prank(admin); - base.lazyMint(lazymintAmount, baseURI, ""); - - assertEq(base.nextTokenIdToMint(), lazymintAmount); - } - - function test_state_claim() public { - uint256 tokenId = 0; - uint256 amount = 100; - - vm.prank(nftHolder); - base.claim(nftHolder, tokenId, amount); - - assertEq(base.balanceOf(nftHolder, tokenId), amount); - assertEq(base.totalSupply(tokenId), amount); - assertEq(base.uri(tokenId), string(abi.encodePacked(baseURI, tokenId.toString()))); - } - - function test_revert_mintTo_invalidId() public { - uint256 tokenId = base.nextTokenIdToMint(); - uint256 amount = 100; - - vm.prank(nftHolder); - vm.expectRevert("invalid id"); - base.claim(nftHolder, tokenId, amount); - } -} diff --git a/src/test/sdk/base/ERC1155SignatureMint.t.sol b/src/test/sdk/base/ERC1155SignatureMint.t.sol deleted file mode 100644 index 77433ba98..000000000 --- a/src/test/sdk/base/ERC1155SignatureMint.t.sol +++ /dev/null @@ -1,240 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { ERC1155SignatureMint } from "contracts/base/ERC1155SignatureMint.sol"; -import { Strings } from "contracts/lib/Strings.sol"; - -contract ERC1155SignatureMintTest is DSTest, Test { - using Strings for uint256; - - // Target contract - ERC1155SignatureMint internal base; - - // Signers - uint256 internal adminPkey; - uint256 internal nftHolderPkey; - - address internal admin; - address internal nftHolder; - address internal saleRecipient; - - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - ERC1155SignatureMint.MintRequest req; - - function signMintRequest( - ERC1155SignatureMint.MintRequest memory _request, - uint256 privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = bytes.concat( - abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - _request.tokenId, - keccak256(bytes(_request.uri)) - ), - abi.encode( - _request.quantity, - _request.pricePerToken, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ) - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - function setUp() public { - adminPkey = 123; - nftHolderPkey = 456; - - admin = vm.addr(adminPkey); - nftHolder = vm.addr(nftHolderPkey); - saleRecipient = address(0x8910); - - vm.deal(nftHolder, 100 ether); - - vm.prank(admin); - base = new ERC1155SignatureMint(admin, "name", "symbol", admin, 0, saleRecipient); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,uint256 tokenId,string uri,uint256 quantity,uint256 pricePerToken,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("SignatureMintERC1155")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(base))); - } - - function test_state_mintWithSignature_newNFTs() public { - req.to = nftHolder; - req.royaltyRecipient = admin; - req.royaltyBps = 0; - req.primarySaleRecipient = saleRecipient; - req.tokenId = type(uint256).max; - req.uri = "ipfs://"; - req.quantity = 100; - req.pricePerToken = 0; - req.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - req.validityStartTimestamp = 0; - req.validityEndTimestamp = type(uint128).max; - req.uid = keccak256("uid"); - - bytes memory signature = signMintRequest(req, adminPkey); - - uint256 tokenId = base.nextTokenIdToMint(); - assertEq(base.totalSupply(tokenId), 0); - - vm.prank(nftHolder); - base.mintWithSignature(req, signature); - - assertEq(base.balanceOf(nftHolder, tokenId), req.quantity); - assertEq(base.totalSupply(tokenId), req.quantity); - assertEq(base.uri(tokenId), req.uri); - } - - function test_state_mintWithSignature_existingNFTs() public { - req.to = nftHolder; - req.royaltyRecipient = admin; - req.royaltyBps = 0; - req.primarySaleRecipient = saleRecipient; - req.tokenId = type(uint256).max; - req.uri = "ipfs://"; - req.quantity = 100; - req.pricePerToken = 0; - req.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - req.validityStartTimestamp = 0; - req.validityEndTimestamp = type(uint128).max; - req.uid = keccak256("uid"); - - bytes memory signature = signMintRequest(req, adminPkey); - - uint256 tokenId = base.nextTokenIdToMint(); - assertEq(base.totalSupply(tokenId), 0); - - vm.prank(nftHolder); - base.mintWithSignature(req, signature); - - assertEq(base.balanceOf(nftHolder, tokenId), req.quantity); - assertEq(base.totalSupply(tokenId), req.quantity); - - req.tokenId = tokenId; - string memory originalURI = req.uri; - req.uri = "wrongURI://"; - req.uid = keccak256("new uid"); - - bytes memory signature2 = signMintRequest(req, adminPkey); - vm.prank(nftHolder); - base.mintWithSignature(req, signature2); - - assertEq(base.balanceOf(nftHolder, tokenId), req.quantity * 2); - assertEq(base.totalSupply(tokenId), req.quantity * 2); - assertEq(base.uri(tokenId), originalURI); - } - - function test_state_mintWithSignature_withPrice() public { - req.to = nftHolder; - req.royaltyRecipient = admin; - req.royaltyBps = 0; - req.primarySaleRecipient = saleRecipient; - req.tokenId = type(uint256).max; - req.uri = "ipfs://"; - req.quantity = 100; - req.pricePerToken = 0.01 ether; - req.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - req.validityStartTimestamp = 0; - req.validityEndTimestamp = type(uint128).max; - req.uid = keccak256("uid"); - - uint256 saleRecipientBalBefore = saleRecipient.balance; - uint256 totalPrice = req.pricePerToken * req.quantity; - - bytes memory signature = signMintRequest(req, adminPkey); - vm.prank(nftHolder); - base.mintWithSignature{ value: totalPrice }(req, signature); - - assertEq(saleRecipient.balance, saleRecipientBalBefore + totalPrice); - } - - function test_revert_mintWithSignature_withPrice_incorrectPrice() public { - req.to = nftHolder; - req.royaltyRecipient = admin; - req.royaltyBps = 0; - req.primarySaleRecipient = saleRecipient; - req.tokenId = type(uint256).max; - req.uri = "ipfs://"; - req.quantity = 100; - req.pricePerToken = 0.01 ether; - req.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - req.validityStartTimestamp = 0; - req.validityEndTimestamp = type(uint128).max; - req.uid = keccak256("uid"); - - uint256 totalPrice = req.pricePerToken * req.quantity; - bytes memory signature = signMintRequest(req, adminPkey); - vm.prank(nftHolder); - vm.expectRevert("Invalid msg value"); - base.mintWithSignature{ value: totalPrice - 1 }(req, signature); - } - - function test_revert_mintWithSignature_mintingZeroTokens() public { - req.to = nftHolder; - req.royaltyRecipient = admin; - req.royaltyBps = 0; - req.primarySaleRecipient = saleRecipient; - req.tokenId = type(uint256).max; - req.uri = "ipfs://"; - req.quantity = 0; - req.pricePerToken = 0; - req.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - req.validityStartTimestamp = 0; - req.validityEndTimestamp = type(uint128).max; - req.uid = keccak256("uid"); - - bytes memory signature = signMintRequest(req, adminPkey); - vm.prank(nftHolder); - vm.expectRevert("Minting zero tokens."); - base.mintWithSignature(req, signature); - } - - function test_revert_mintWithSignature_invalidId() public { - uint256 nextId = base.nextTokenIdToMint(); - - req.to = nftHolder; - req.royaltyRecipient = admin; - req.royaltyBps = 0; - req.primarySaleRecipient = saleRecipient; - req.tokenId = nextId; - req.uri = "ipfs://"; - req.quantity = 100; - req.pricePerToken = 0; - req.currency = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - req.validityStartTimestamp = 0; - req.validityEndTimestamp = type(uint128).max; - req.uid = keccak256("uid"); - - bytes memory signature = signMintRequest(req, adminPkey); - vm.prank(nftHolder); - vm.expectRevert("invalid id"); - base.mintWithSignature(req, signature); - } -} diff --git a/src/test/sdk/base/ERC20Base.t.sol b/src/test/sdk/base/ERC20Base.t.sol deleted file mode 100644 index ec8131cfd..000000000 --- a/src/test/sdk/base/ERC20Base.t.sol +++ /dev/null @@ -1,248 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./BaseUtilTest.sol"; -import { ERC20Base } from "contracts/base/ERC20Base.sol"; - -contract BaseERC20BaseTest is BaseUtilTest { - ERC20Base internal base; - using Strings for uint256; - - bytes32 internal permitTypeHash; - bytes32 internal permitNameHash; - bytes32 internal permitVersionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - uint256 public recipientPrivateKey = 5678; - address public recipient; - - function setUp() public override { - super.setUp(); - vm.prank(deployer); - base = new ERC20Base(deployer, NAME, SYMBOL); - - recipient = vm.addr(recipientPrivateKey); - - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - - // permit related inputs - permitTypeHash = keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ); - permitNameHash = keccak256(bytes(NAME)); - permitVersionHash = keccak256("1"); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mint` - //////////////////////////////////////////////////////////////*/ - - function test_state_mint() public { - uint256 amount = 5 ether; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - vm.prank(deployer); - base.mintTo(recipient, amount); - - assertEq(base.totalSupply(), currentTotalSupply + amount); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + amount); - } - - function test_revert_mint_NotAuthorized() public { - uint256 amount = 5 ether; - - vm.expectRevert("Not authorized to mint."); - vm.prank(address(0x1)); - base.mintTo(recipient, amount); - } - - function test_revert_mint_MintingZeroTokens() public { - uint256 amount = 0; - - vm.expectRevert("Minting zero tokens."); - vm.prank(deployer); - base.mintTo(recipient, amount); - } - - function test_revert_mint_MintToZeroAddress() public { - uint256 amount = 1; - - vm.expectRevert("ERC20: mint to the zero address"); - vm.prank(deployer); - base.mintTo(address(0), amount); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `burn` - //////////////////////////////////////////////////////////////*/ - - function test_state_burn() public { - uint256 amount = 5 ether; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - vm.prank(deployer); - base.mintTo(recipient, amount); - - assertEq(base.totalSupply(), currentTotalSupply + amount); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + amount); - - // burn minted tokens - currentTotalSupply = base.totalSupply(); - currentBalanceOfRecipient = base.balanceOf(recipient); - vm.prank(recipient); - base.burn(amount); - - assertEq(base.totalSupply(), currentTotalSupply - amount); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient - amount); - } - - function test_revert_burn_NotEnoughBalance() public { - uint256 amount = 5 ether; - - vm.prank(deployer); - base.mintTo(recipient, amount); - - vm.expectRevert("not enough balance"); - vm.prank(recipient); - base.burn(amount + 1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `permit` - //////////////////////////////////////////////////////////////*/ - - function test_state_permit() public { - uint256 amount = 5 ether; - - // mint amount to recipient - vm.prank(deployer); - base.mintTo(recipient, amount); - - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call permit to approve _value to _spender - base.permit(_owner, _spender, _value, _deadline, v, r, s); - - // check allowance - uint256 _allowance = base.allowance(_owner, _spender); - - assertEq(_allowance, _value); - assertEq(base.nonces(_owner), _nonce + 1); - } - - function test_revert_permit_IncorrectKey() public { - uint256 amount = 5 ether; - uint256 wrongPrivateKey = 2345; - - // mint amount to recipient - vm.prank(deployer); - base.mintTo(recipient, amount); - - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(wrongPrivateKey, typedDataHash); // sign with wrong key - - // call permit to approve _value to _spender - vm.expectRevert("ERC20Permit: invalid signature"); - base.permit(_owner, _spender, _value, _deadline, v, r, s); - } - - function test_revert_permit_UsedNonce() public { - uint256 amount = 5 ether; - - // mint amount to recipient - vm.prank(deployer); - base.mintTo(recipient, amount); - - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call permit to approve _value to _spender - base.permit(_owner, _spender, _value, _deadline, v, r, s); - - // sign again with same nonce - (v, r, s) = vm.sign(recipientPrivateKey, typedDataHash); - - vm.expectRevert("ERC20Permit: invalid signature"); - base.permit(_owner, _spender, _value, _deadline, v, r, s); - } - - function test_revert_permit_ExpiredDeadline() public { - uint256 amount = 5 ether; - // uint256 wrongPrivateKey = 2345; - - // mint amount to recipient - vm.prank(deployer); - base.mintTo(recipient, amount); - - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call permit to approve _value to _spender - vm.warp(_deadline + 1); - vm.expectRevert("ERC20Permit: expired deadline"); - base.permit(_owner, _spender, _value, _deadline, v, r, s); - } -} diff --git a/src/test/sdk/base/ERC20Drop.t.sol b/src/test/sdk/base/ERC20Drop.t.sol deleted file mode 100644 index cb4457298..000000000 --- a/src/test/sdk/base/ERC20Drop.t.sol +++ /dev/null @@ -1,300 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./BaseUtilTest.sol"; -import { ERC20Drop } from "contracts/base/ERC20Drop.sol"; - -contract BaseERC20DropTest is BaseUtilTest { - ERC20Drop internal base; - using Strings for uint256; - - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - // permit - bytes32 internal permitTypeHash; - bytes32 internal permitNameHash; - bytes32 internal permitVersionHash; - - uint256 public recipientPrivateKey = 5678; - address public recipient; - - function setUp() public override { - super.setUp(); - vm.prank(signer); - base = new ERC20Drop(signer, NAME, SYMBOL, saleRecipient); - - recipient = vm.addr(recipientPrivateKey); - erc20.mint(recipient, 1_000_000 ether); - vm.deal(recipient, 1_000_000 ether); - - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - - // permit related inputs - permitTypeHash = keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ); - permitNameHash = keccak256(bytes(NAME)); - permitVersionHash = keccak256("1"); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `claim` - //////////////////////////////////////////////////////////////*/ - - function test_state_claim_ZeroPrice() public { - vm.warp(1); - - address claimer = address(0x345); - uint256 _quantity = 10; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - bytes32[] memory proofs = new bytes32[](0); - - ERC20Drop.AllowlistProof memory alp; - alp.proof = proofs; - - ERC20Drop.ClaimCondition[] memory conditions = new ERC20Drop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(signer); - base.setClaimConditions(conditions[0], false); - - vm.prank(claimer, claimer); - base.claim(recipient, _quantity, address(0), 0, alp, ""); - - assertEq(base.totalSupply(), currentTotalSupply + _quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _quantity); - } - - function test_state_claim_NonZeroPrice_ERC20() public { - vm.warp(1); - - address claimer = address(0x345); - uint256 _quantity = 10 ether; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - bytes32[] memory proofs = new bytes32[](0); - - ERC20Drop.AllowlistProof memory alp; - alp.proof = proofs; - - ERC20Drop.ClaimCondition[] memory conditions = new ERC20Drop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100 ether; - conditions[0].quantityLimitPerWallet = 100 ether; - - // set price and currency - conditions[0].pricePerToken = 1 ether; - conditions[0].currency = address(erc20); - - uint256 totalPrice = (conditions[0].pricePerToken * _quantity) / 1 ether; - - vm.prank(signer); - base.setClaimConditions(conditions[0], false); - - // mint erc20 to claimer, and approve to base - erc20.mint(claimer, 1000 ether); - vm.prank(claimer); - erc20.approve(address(base), totalPrice); - - vm.prank(claimer, claimer); - base.claim(recipient, _quantity, address(erc20), 1 ether, alp, ""); - - assertEq(base.totalSupply(), currentTotalSupply + _quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _quantity); - } - - function test_state_claim_NonZeroPrice_NativeToken() public { - vm.warp(1); - - address claimer = address(0x345); - uint256 _quantity = 10 ether; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - bytes32[] memory proofs = new bytes32[](0); - - ERC20Drop.AllowlistProof memory alp; - alp.proof = proofs; - - ERC20Drop.ClaimCondition[] memory conditions = new ERC20Drop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100 ether; - conditions[0].quantityLimitPerWallet = 100 ether; - - // set price and currency - conditions[0].pricePerToken = 1 ether; - conditions[0].currency = address(NATIVE_TOKEN); - - uint256 totalPrice = (conditions[0].pricePerToken * _quantity) / 1 ether; - - vm.prank(signer); - base.setClaimConditions(conditions[0], false); - - // deal NATIVE_TOKEN to claimer - vm.deal(claimer, 1_000 ether); - - vm.prank(claimer, claimer); - base.claim{ value: totalPrice }(recipient, _quantity, address(NATIVE_TOKEN), 1 ether, alp, ""); - - assertEq(base.totalSupply(), currentTotalSupply + _quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _quantity); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `burn` - //////////////////////////////////////////////////////////////*/ - - function test_state_burn() public { - address claimer = address(0x345); - uint256 _quantity = 10 ether; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - bytes32[] memory proofs = new bytes32[](0); - - ERC20Drop.AllowlistProof memory alp; - alp.proof = proofs; - - ERC20Drop.ClaimCondition[] memory conditions = new ERC20Drop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100 ether; - conditions[0].quantityLimitPerWallet = 100 ether; - - vm.prank(signer); - base.setClaimConditions(conditions[0], false); - - vm.prank(claimer, claimer); - base.claim(recipient, _quantity, address(0), 0, alp, ""); - - // burn minted tokens - currentTotalSupply = base.totalSupply(); - currentBalanceOfRecipient = base.balanceOf(recipient); - vm.prank(recipient); - base.burn(_quantity); - - assertEq(base.totalSupply(), currentTotalSupply - _quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient - _quantity); - } - - function test_revert_burn_NotEnoughBalance() public { - vm.expectRevert("not enough balance"); - vm.prank(recipient); - base.burn(1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `permit` - //////////////////////////////////////////////////////////////*/ - - function test_state_permit() public { - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call permit to approve _value to _spender - base.permit(_owner, _spender, _value, _deadline, v, r, s); - - // check allowance - uint256 _allowance = base.allowance(_owner, _spender); - - assertEq(_allowance, _value); - assertEq(base.nonces(_owner), _nonce + 1); - } - - function test_revert_permit_IncorrectKey() public { - uint256 wrongPrivateKey = 2345; - - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(wrongPrivateKey, typedDataHash); // sign with wrong key - - // call permit to approve _value to _spender - vm.expectRevert("ERC20Permit: invalid signature"); - base.permit(_owner, _spender, _value, _deadline, v, r, s); - } - - function test_revert_permit_UsedNonce() public { - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call permit to approve _value to _spender - base.permit(_owner, _spender, _value, _deadline, v, r, s); - - // sign again with same nonce - (v, r, s) = vm.sign(recipientPrivateKey, typedDataHash); - - vm.expectRevert("ERC20Permit: invalid signature"); - base.permit(_owner, _spender, _value, _deadline, v, r, s); - } - - function test_revert_permit_ExpiredDeadline() public { - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call permit to approve _value to _spender - vm.warp(_deadline + 1); - vm.expectRevert("ERC20Permit: expired deadline"); - base.permit(_owner, _spender, _value, _deadline, v, r, s); - } -} diff --git a/src/test/sdk/base/ERC20DropVote.t.sol b/src/test/sdk/base/ERC20DropVote.t.sol deleted file mode 100644 index fd32af1de..000000000 --- a/src/test/sdk/base/ERC20DropVote.t.sol +++ /dev/null @@ -1,377 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./BaseUtilTest.sol"; -import { ERC20DropVote } from "contracts/base/ERC20DropVote.sol"; - -contract BaseERC20DropVoteTest is BaseUtilTest { - ERC20DropVote internal base; - using Strings for uint256; - - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - // permit and vote - bytes32 internal permitTypeHash; - bytes32 internal delegationTypeHash; - bytes32 internal permitNameHash; - bytes32 internal permitVersionHash; - - uint256 public recipientPrivateKey = 5678; - address public recipient; - - function setUp() public override { - super.setUp(); - vm.prank(signer); - base = new ERC20DropVote(signer, NAME, SYMBOL, saleRecipient); - - recipient = vm.addr(recipientPrivateKey); - erc20.mint(recipient, 1_000_000 ether); - vm.deal(recipient, 1_000_000 ether); - - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - - // permit related inputs - permitTypeHash = keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ); - permitNameHash = keccak256(bytes(NAME)); - permitVersionHash = keccak256("1"); - - // vote-delegation related inputs - delegationTypeHash = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `claim` - //////////////////////////////////////////////////////////////*/ - - function test_state_claim_ZeroPrice() public { - vm.warp(1); - - address claimer = address(0x345); - uint256 _quantity = 10; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - bytes32[] memory proofs = new bytes32[](0); - - ERC20DropVote.AllowlistProof memory alp; - alp.proof = proofs; - - ERC20DropVote.ClaimCondition[] memory conditions = new ERC20DropVote.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(signer); - base.setClaimConditions(conditions[0], false); - - vm.prank(claimer, claimer); - base.claim(recipient, _quantity, address(0), 0, alp, ""); - - assertEq(base.totalSupply(), currentTotalSupply + _quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _quantity); - } - - function test_state_claim_NonZeroPrice_ERC20() public { - vm.warp(1); - - address claimer = address(0x345); - uint256 _quantity = 10 ether; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - bytes32[] memory proofs = new bytes32[](0); - - ERC20DropVote.AllowlistProof memory alp; - alp.proof = proofs; - - ERC20DropVote.ClaimCondition[] memory conditions = new ERC20DropVote.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100 ether; - conditions[0].quantityLimitPerWallet = 100 ether; - - // set price and currency - conditions[0].pricePerToken = 1 ether; - conditions[0].currency = address(erc20); - - // uint256 totalPrice = (conditions[0].pricePerToken * _quantity) / 1 ether; - - vm.prank(signer); - base.setClaimConditions(conditions[0], false); - - // mint erc20 to claimer, and approve to base - erc20.mint(claimer, 1000 ether); - vm.prank(claimer); - erc20.approve(address(base), 1_000 ether); - - vm.prank(claimer, claimer); - base.claim(recipient, _quantity, address(erc20), 1 ether, alp, ""); - - assertEq(base.totalSupply(), currentTotalSupply + _quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _quantity); - } - - function test_state_claim_NonZeroPrice_NativeToken() public { - vm.warp(1); - - address claimer = address(0x345); - uint256 _quantity = 10 ether; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - bytes32[] memory proofs = new bytes32[](0); - - ERC20DropVote.AllowlistProof memory alp; - alp.proof = proofs; - - ERC20DropVote.ClaimCondition[] memory conditions = new ERC20DropVote.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100 ether; - conditions[0].quantityLimitPerWallet = 100 ether; - - // set price and currency - conditions[0].pricePerToken = 1 ether; - conditions[0].currency = address(NATIVE_TOKEN); - - uint256 totalPrice = (conditions[0].pricePerToken * _quantity) / 1 ether; - - vm.prank(signer); - base.setClaimConditions(conditions[0], false); - - // deal NATIVE_TOKEN to claimer - vm.deal(claimer, 1_000 ether); - - vm.prank(claimer, claimer); - base.claim{ value: totalPrice }(recipient, _quantity, address(NATIVE_TOKEN), 1 ether, alp, ""); - - assertEq(base.totalSupply(), currentTotalSupply + _quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _quantity); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `burn` - //////////////////////////////////////////////////////////////*/ - - function test_state_burn() public { - address claimer = address(0x345); - uint256 _quantity = 10 ether; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - bytes32[] memory proofs = new bytes32[](0); - - ERC20DropVote.AllowlistProof memory alp; - alp.proof = proofs; - - ERC20DropVote.ClaimCondition[] memory conditions = new ERC20DropVote.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100 ether; - conditions[0].quantityLimitPerWallet = 100 ether; - - vm.prank(signer); - base.setClaimConditions(conditions[0], false); - - vm.prank(claimer, claimer); - base.claim(recipient, _quantity, address(0), 0, alp, ""); - - // burn minted tokens - currentTotalSupply = base.totalSupply(); - currentBalanceOfRecipient = base.balanceOf(recipient); - vm.prank(recipient); - base.burn(_quantity); - - assertEq(base.totalSupply(), currentTotalSupply - _quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient - _quantity); - } - - function test_revert_burn_NotEnoughBalance() public { - vm.expectRevert("not enough balance"); - vm.prank(recipient); - base.burn(1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `permit` - //////////////////////////////////////////////////////////////*/ - - function test_state_permit() public { - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call permit to approve _value to _spender - base.permit(_owner, _spender, _value, _deadline, v, r, s); - - // check allowance - uint256 _allowance = base.allowance(_owner, _spender); - - assertEq(_allowance, _value); - assertEq(base.nonces(_owner), _nonce + 1); - } - - function test_revert_permit_IncorrectKey() public { - uint256 wrongPrivateKey = 2345; - - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(wrongPrivateKey, typedDataHash); // sign with wrong key - - // call permit to approve _value to _spender - vm.expectRevert("ERC20Permit: invalid signature"); - base.permit(_owner, _spender, _value, _deadline, v, r, s); - } - - function test_revert_permit_UsedNonce() public { - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call permit to approve _value to _spender - base.permit(_owner, _spender, _value, _deadline, v, r, s); - - // sign again with same nonce - (v, r, s) = vm.sign(recipientPrivateKey, typedDataHash); - - vm.expectRevert("ERC20Permit: invalid signature"); - base.permit(_owner, _spender, _value, _deadline, v, r, s); - } - - function test_revert_permit_ExpiredDeadline() public { - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call permit to approve _value to _spender - vm.warp(_deadline + 1); - vm.expectRevert("ERC20Permit: expired deadline"); - base.permit(_owner, _spender, _value, _deadline, v, r, s); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `delegateBySig` - //////////////////////////////////////////////////////////////*/ - - function test_state_delegateBySig() public { - // generate delegation signature - address _delegatee = address(0x789); - uint256 _nonce = base.nonces(recipient); - uint256 _expiry = 1000; - - // vote extends permit, so name-hash and version-hash are same for both - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(delegationTypeHash, _delegatee, _nonce, _expiry)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call delegationBySig to delegate votes from recipient address to delegatee address - base.delegateBySig(_delegatee, _nonce, _expiry, v, r, s); - - assertEq(base.delegates(recipient), _delegatee); - } - - function test_revert_delegateBySig_InvalidNonce() public { - // generate delegation signature - address _delegatee = address(0x789); - uint256 _nonce = base.nonces(recipient); - uint256 _expiry = 1000; - - // vote extends permit, so name-hash and version-hash are same for both - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256( - abi.encode( - delegationTypeHash, - _delegatee, - _nonce + 1, // invalid nonce - _expiry - ) - ); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call delegationBySig to delegate votes from recipient address to delegatee address - vm.expectRevert("ERC20Votes: invalid nonce"); - base.delegateBySig(_delegatee, _nonce + 1, _expiry, v, r, s); - } - - function test_revert_delegateBySig_SignatureExpired() public { - // generate delegation signature - address _delegatee = address(0x789); - uint256 _nonce = base.nonces(recipient); - uint256 _expiry = 1000; - - // vote extends permit, so name-hash and version-hash are same for both - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(delegationTypeHash, _delegatee, _nonce, _expiry)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call delegationBySig to delegate votes from recipient address to delegatee address - vm.warp(_expiry + 1); - vm.expectRevert("ERC20Votes: signature expired"); - base.delegateBySig(_delegatee, _nonce, _expiry, v, r, s); - } -} diff --git a/src/test/sdk/base/ERC20SignatureMint.t.sol b/src/test/sdk/base/ERC20SignatureMint.t.sol deleted file mode 100644 index 9c4560868..000000000 --- a/src/test/sdk/base/ERC20SignatureMint.t.sol +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./BaseUtilTest.sol"; -import { ERC20SignatureMint } from "contracts/base/ERC20SignatureMint.sol"; - -contract BaseERC20SignatureMintTest is BaseUtilTest { - ERC20SignatureMint internal base; - using Strings for uint256; - - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - ERC20SignatureMint.MintRequest _mintrequest; - bytes _signature; - - address recipient; - - function setUp() public override { - super.setUp(); - vm.prank(signer); - base = new ERC20SignatureMint(signer, NAME, SYMBOL, saleRecipient); - - recipient = address(0x123); - erc20.mint(recipient, 1_000_000 ether); - vm.deal(recipient, 1_000_000 ether); - - typehashMintRequest = keccak256( - "MintRequest(address to,address primarySaleRecipient,uint256 quantity,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("SignatureMintERC20")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(base))); - - _mintrequest.to = recipient; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.quantity = 100 ether; - _mintrequest.price = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - ERC20SignatureMint.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.primarySaleRecipient, - _request.quantity, - _request.price, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintWithSignature` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintWithSignature_ZeroPrice() public { - vm.warp(1000); - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - address recoveredSigner = base.mintWithSignature(_mintrequest, _signature); - - assertEq(base.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - assertEq(signer, recoveredSigner); - } - - function test_state_mintWithSignature_NonZeroPrice_ERC20() public { - vm.warp(1000); - - _mintrequest.price = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - erc20.approve(address(base), _mintrequest.price); - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - uint256 erc20BalanceOfRecipient = erc20.balanceOf(recipient); - uint256 erc20BalanceOfSeller = erc20.balanceOf(saleRecipient); - - uint256 totalPrice = _mintrequest.price; - - vm.prank(recipient); - base.mintWithSignature(_mintrequest, _signature); - - // check token balances - assertEq(base.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - - // check erc20 currency balances - assertEq(erc20.balanceOf(recipient), erc20BalanceOfRecipient - totalPrice); - assertEq(erc20.balanceOf(saleRecipient), erc20BalanceOfSeller + totalPrice); - } - - function test_state_mintWithSignature_NonZeroPrice_NativeToken() public { - vm.warp(1000); - - _mintrequest.price = 1 ether; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - uint256 etherBalanceOfRecipient = recipient.balance; - uint256 etherBalanceOfSeller = saleRecipient.balance; - - uint256 totalPrice = _mintrequest.price; - - vm.prank(recipient); - base.mintWithSignature{ value: totalPrice }(_mintrequest, _signature); - - assertEq(base.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - - // check native-token balances - assertEq(recipient.balance, etherBalanceOfRecipient - totalPrice); - assertEq(saleRecipient.balance, etherBalanceOfSeller + totalPrice); - } - - function test_revert_mintWithSignature_MustSendTotalPrice() public { - vm.warp(1000); - - _mintrequest.price = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("Must send total price."); - base.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_MintingZeroTokens() public { - vm.warp(1000); - - _mintrequest.quantity = 0; - _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("Minting zero tokens."); - base.mintWithSignature(_mintrequest, _signature); - } -} diff --git a/src/test/sdk/base/ERC20SignatureMintVote.t.sol b/src/test/sdk/base/ERC20SignatureMintVote.t.sol deleted file mode 100644 index f705aba8e..000000000 --- a/src/test/sdk/base/ERC20SignatureMintVote.t.sol +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./BaseUtilTest.sol"; -import { ERC20SignatureMintVote } from "contracts/base/ERC20SignatureMintVote.sol"; - -contract BaseERC20SignatureMintVoteTest is BaseUtilTest { - ERC20SignatureMintVote internal base; - using Strings for uint256; - - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - ERC20SignatureMintVote.MintRequest _mintrequest; - bytes _signature; - - address recipient; - - function setUp() public override { - super.setUp(); - vm.prank(signer); - base = new ERC20SignatureMintVote(signer, NAME, SYMBOL, saleRecipient); - - recipient = address(0x123); - erc20.mint(recipient, 1_000 ether); - vm.deal(recipient, 1_000 ether); - - typehashMintRequest = keccak256( - "MintRequest(address to,address primarySaleRecipient,uint256 quantity,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("SignatureMintERC20")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(base))); - - _mintrequest.to = recipient; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.quantity = 100 ether; - _mintrequest.price = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - ERC20SignatureMintVote.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.primarySaleRecipient, - _request.quantity, - _request.price, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintWithSignature` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintWithSignature_ZeroPrice() public { - vm.warp(1000); - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - address recoveredSigner = base.mintWithSignature(_mintrequest, _signature); - - assertEq(base.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - assertEq(signer, recoveredSigner); - } - - function test_state_mintWithSignature_NonZeroPrice_ERC20() public { - vm.warp(1000); - - _mintrequest.price = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - erc20.approve(address(base), _mintrequest.price); - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - uint256 erc20BalanceOfRecipient = erc20.balanceOf(recipient); - uint256 erc20BalanceOfSeller = erc20.balanceOf(saleRecipient); - - uint256 totalPrice = _mintrequest.price; - - vm.prank(recipient); - base.mintWithSignature(_mintrequest, _signature); - - // check token balances - assertEq(base.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - - // check erc20 currency balances - assertEq(erc20.balanceOf(recipient), erc20BalanceOfRecipient - totalPrice); - assertEq(erc20.balanceOf(saleRecipient), erc20BalanceOfSeller + totalPrice); - } - - function test_state_mintWithSignature_NonZeroPrice_NativeToken() public { - vm.warp(1000); - - _mintrequest.price = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - uint256 etherBalanceOfRecipient = recipient.balance; - uint256 etherBalanceOfSeller = saleRecipient.balance; - - uint256 totalPrice = _mintrequest.price; - - vm.prank(recipient); - base.mintWithSignature{ value: totalPrice }(_mintrequest, _signature); - - assertEq(base.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - - // check native-token balances - assertEq(recipient.balance, etherBalanceOfRecipient - totalPrice); - assertEq(saleRecipient.balance, etherBalanceOfSeller + totalPrice); - } - - function test_revert_mintWithSignature_MustSendTotalPrice() public { - vm.warp(1000); - - _mintrequest.price = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("Must send total price."); - base.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_MintingZeroTokens() public { - vm.warp(1000); - - _mintrequest.quantity = 0; - _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("Minting zero tokens."); - base.mintWithSignature(_mintrequest, _signature); - } -} diff --git a/src/test/sdk/base/ERC20Vote.t.sol b/src/test/sdk/base/ERC20Vote.t.sol deleted file mode 100644 index ca57c1c76..000000000 --- a/src/test/sdk/base/ERC20Vote.t.sol +++ /dev/null @@ -1,325 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./BaseUtilTest.sol"; -import { ERC20Vote } from "contracts/base/ERC20Vote.sol"; - -contract BaseERC20VoteTest is BaseUtilTest { - ERC20Vote internal base; - using Strings for uint256; - - bytes32 internal permitTypeHash; - bytes32 internal delegationTypeHash; - bytes32 internal permitNameHash; - bytes32 internal permitVersionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - uint256 public recipientPrivateKey = 5678; - address public recipient; - - function setUp() public override { - super.setUp(); - vm.prank(deployer); - base = new ERC20Vote(deployer, NAME, SYMBOL); - - recipient = vm.addr(recipientPrivateKey); - - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - - // permit related inputs - permitTypeHash = keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ); - permitNameHash = keccak256(bytes(NAME)); - permitVersionHash = keccak256("1"); - - // vote-delegation related inputs - delegationTypeHash = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mint` - //////////////////////////////////////////////////////////////*/ - - function test_state_mint() public { - uint256 amount = 5 ether; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - vm.prank(deployer); - base.mintTo(recipient, amount); - - assertEq(base.totalSupply(), currentTotalSupply + amount); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + amount); - } - - function test_revert_mint_NotAuthorized() public { - uint256 amount = 5 ether; - - vm.expectRevert("Not authorized to mint."); - vm.prank(address(0x1)); - base.mintTo(recipient, amount); - } - - function test_revert_mint_MintingZeroTokens() public { - uint256 amount = 0; - - vm.expectRevert("Minting zero tokens."); - vm.prank(deployer); - base.mintTo(recipient, amount); - } - - function test_revert_mint_MintToZeroAddress() public { - uint256 amount = 1; - - vm.expectRevert("ERC20: mint to the zero address"); - vm.prank(deployer); - base.mintTo(address(0), amount); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `burn` - //////////////////////////////////////////////////////////////*/ - - function test_state_burn() public { - uint256 amount = 5 ether; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - vm.prank(deployer); - base.mintTo(recipient, amount); - - assertEq(base.totalSupply(), currentTotalSupply + amount); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + amount); - - // burn minted tokens - currentTotalSupply = base.totalSupply(); - currentBalanceOfRecipient = base.balanceOf(recipient); - vm.prank(recipient); - base.burn(amount); - - assertEq(base.totalSupply(), currentTotalSupply - amount); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient - amount); - } - - function test_revert_burn_NotEnoughBalance() public { - uint256 amount = 5 ether; - - vm.prank(deployer); - base.mintTo(recipient, amount); - - vm.expectRevert("not enough balance"); - vm.prank(recipient); - base.burn(amount + 1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `permit` - //////////////////////////////////////////////////////////////*/ - - function test_state_permit() public { - uint256 amount = 5 ether; - - // mint amount to recipient - vm.prank(deployer); - base.mintTo(recipient, amount); - - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call permit to approve _value to _spender - base.permit(_owner, _spender, _value, _deadline, v, r, s); - - // check allowance - uint256 _allowance = base.allowance(_owner, _spender); - - assertEq(_allowance, _value); - assertEq(base.nonces(_owner), _nonce + 1); - } - - function test_revert_permit_IncorrectKey() public { - uint256 amount = 5 ether; - uint256 wrongPrivateKey = 2345; - - // mint amount to recipient - vm.prank(deployer); - base.mintTo(recipient, amount); - - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(wrongPrivateKey, typedDataHash); // sign with wrong key - - // call permit to approve _value to _spender - vm.expectRevert("ERC20Permit: invalid signature"); - base.permit(_owner, _spender, _value, _deadline, v, r, s); - } - - function test_revert_permit_UsedNonce() public { - uint256 amount = 5 ether; - - // mint amount to recipient - vm.prank(deployer); - base.mintTo(recipient, amount); - - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call permit to approve _value to _spender - base.permit(_owner, _spender, _value, _deadline, v, r, s); - - // sign again with same nonce - (v, r, s) = vm.sign(recipientPrivateKey, typedDataHash); - - vm.expectRevert("ERC20Permit: invalid signature"); - base.permit(_owner, _spender, _value, _deadline, v, r, s); - } - - function test_revert_permit_ExpiredDeadline() public { - uint256 amount = 5 ether; - uint256 wrongPrivateKey = 2345; - - // mint amount to recipient - vm.prank(deployer); - base.mintTo(recipient, amount); - - // generate permit signature - address _owner = recipient; - address _spender = address(0x789); - uint256 _value = 1 ether; - uint256 _deadline = 1000; - - uint256 _nonce = base.nonces(_owner); - - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(permitTypeHash, _owner, _spender, _value, _nonce, _deadline)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(wrongPrivateKey, typedDataHash); // sign with wrong key - - // call permit to approve _value to _spender - vm.warp(_deadline + 1); - vm.expectRevert("ERC20Permit: expired deadline"); - base.permit(_owner, _spender, _value, _deadline, v, r, s); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `delegateBySig` - //////////////////////////////////////////////////////////////*/ - - function test_state_delegateBySig() public { - // generate delegation signature - address _delegatee = address(0x789); - uint256 _nonce = base.nonces(recipient); - uint256 _expiry = 1000; - - // vote extends permit, so name-hash and version-hash are same for both - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(delegationTypeHash, _delegatee, _nonce, _expiry)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call delegationBySig to delegate votes from recipient address to delegatee address - base.delegateBySig(_delegatee, _nonce, _expiry, v, r, s); - - assertEq(base.delegates(recipient), _delegatee); - } - - function test_revert_delegateBySig_InvalidNonce() public { - // generate delegation signature - address _delegatee = address(0x789); - uint256 _nonce = base.nonces(recipient); - uint256 _expiry = 1000; - - // vote extends permit, so name-hash and version-hash are same for both - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256( - abi.encode( - delegationTypeHash, - _delegatee, - _nonce + 1, // invalid nonce - _expiry - ) - ); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call delegationBySig to delegate votes from recipient address to delegatee address - vm.expectRevert("ERC20Votes: invalid nonce"); - base.delegateBySig(_delegatee, _nonce + 1, _expiry, v, r, s); - } - - function test_revert_delegateBySig_SignatureExpired() public { - // generate delegation signature - address _delegatee = address(0x789); - uint256 _nonce = base.nonces(recipient); - uint256 _expiry = 1000; - - // vote extends permit, so name-hash and version-hash are same for both - domainSeparator = keccak256( - abi.encode(typehashEip712, permitNameHash, permitVersionHash, block.chainid, address(base)) - ); - bytes32 structHash = keccak256(abi.encode(delegationTypeHash, _delegatee, _nonce, _expiry)); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(recipientPrivateKey, typedDataHash); - - // call delegationBySig to delegate votes from recipient address to delegatee address - vm.warp(_expiry + 1); - vm.expectRevert("ERC20Votes: signature expired"); - base.delegateBySig(_delegatee, _nonce, _expiry, v, r, s); - } -} diff --git a/src/test/sdk/base/ERC721Base.t.sol b/src/test/sdk/base/ERC721Base.t.sol deleted file mode 100644 index 7eaf4b2d2..000000000 --- a/src/test/sdk/base/ERC721Base.t.sol +++ /dev/null @@ -1,192 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./BaseUtilTest.sol"; -import { ERC721Base } from "contracts/base/ERC721Base.sol"; - -contract BaseERC721BaseTest is BaseUtilTest { - ERC721Base internal base; - using Strings for uint256; - - function setUp() public override { - vm.prank(deployer); - base = new ERC721Base(deployer, NAME, SYMBOL, royaltyRecipient, royaltyBps); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintTo` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintTo() public { - address recipient = address(0x123); - string memory _tokenURI = "tokenURI"; - - uint256 nextTokenId = base.nextTokenIdToMint(); - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - vm.prank(deployer); - base.mintTo(recipient, _tokenURI); - - assertEq(base.nextTokenIdToMint(), nextTokenId + 1); - assertEq(base.tokenURI(nextTokenId), _tokenURI); - assertEq(base.totalSupply(), currentTotalSupply + 1); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + 1); - assertEq(base.ownerOf(nextTokenId), recipient); - } - - function test_revert_mintTo_NotAuthorized() public { - address recipient = address(0x123); - string memory _tokenURI = "tokenURI"; - - vm.expectRevert("Not authorized to mint."); - vm.prank(address(0x1)); - base.mintTo(recipient, _tokenURI); - } - - function test_revert_mintTo_MintToZeroAddress() public { - string memory _tokenURI = "tokenURI"; - - vm.expectRevert(bytes4(abi.encodeWithSignature("MintToZeroAddress()"))); - vm.prank(deployer); - base.mintTo(address(0), _tokenURI); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `batchMintTo` - //////////////////////////////////////////////////////////////*/ - - function test_state_batchMintTo() public { - address recipient = address(0x123); - uint256 _quantity = 100; - string memory _baseURI = "baseURI/"; - - uint256 nextTokenId = base.nextTokenIdToMint(); - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - vm.prank(deployer); - base.batchMintTo(recipient, _quantity, _baseURI, ""); - - assertEq(base.nextTokenIdToMint(), nextTokenId + _quantity); - assertEq(base.totalSupply(), currentTotalSupply + _quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _quantity); - for (uint256 i = nextTokenId; i < _quantity; i += 1) { - assertEq(base.tokenURI(i), string(abi.encodePacked(_baseURI, i.toString()))); - assertEq(base.ownerOf(i), recipient); - } - } - - function test_revert_batchMintTo_NotAuthorized() public { - address recipient = address(0x123); - uint256 _quantity = 100; - string memory _baseURI = "baseURI/"; - - vm.expectRevert("Not authorized to mint."); - vm.prank(address(0x1)); - base.batchMintTo(recipient, _quantity, _baseURI, ""); - } - - function test_revert_batchMintTo_MintToZeroAddress() public { - uint256 _quantity = 100; - string memory _baseURI = "baseURI/"; - - vm.expectRevert(bytes4(abi.encodeWithSignature("MintToZeroAddress()"))); - vm.prank(deployer); - base.batchMintTo(address(0), _quantity, _baseURI, ""); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `burn` - //////////////////////////////////////////////////////////////*/ - - function test_state_burn_Owner() public { - address recipient = address(0x123); - string memory _tokenURI = "tokenURI"; - - uint256 nextTokenId = base.nextTokenIdToMint(); - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - vm.prank(deployer); - base.mintTo(recipient, _tokenURI); - - vm.prank(recipient); - base.burn(nextTokenId); - assertEq(base.nextTokenIdToMint(), nextTokenId + 1); - assertEq(base.tokenURI(nextTokenId), _tokenURI); - assertEq(base.totalSupply(), currentTotalSupply); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient); - - vm.expectRevert(bytes4(abi.encodeWithSignature("OwnerQueryForNonexistentToken()"))); - assertEq(base.ownerOf(nextTokenId), address(0)); - } - - function test_state_burn_Approved() public { - address recipient = address(0x123); - string memory _tokenURI = "tokenURI"; - - address operator = address(0x789); - - uint256 nextTokenId = base.nextTokenIdToMint(); - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - vm.prank(deployer); - base.mintTo(recipient, _tokenURI); - - vm.prank(recipient); - base.setApprovalForAll(operator, true); - - vm.prank(operator); - base.burn(nextTokenId); - assertEq(base.nextTokenIdToMint(), nextTokenId + 1); - assertEq(base.tokenURI(nextTokenId), _tokenURI); - assertEq(base.totalSupply(), currentTotalSupply); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient); - - vm.expectRevert(bytes4(abi.encodeWithSignature("OwnerQueryForNonexistentToken()"))); - assertEq(base.ownerOf(nextTokenId), address(0)); - } - - function test_revert_burn_NotOwnerNorApproved() public { - address recipient = address(0x123); - string memory _tokenURI = "tokenURI"; - - uint256 nextTokenId = base.nextTokenIdToMint(); - - vm.prank(deployer); - base.mintTo(recipient, _tokenURI); - - vm.prank(address(0x789)); - vm.expectRevert(bytes4(abi.encodeWithSignature("TransferCallerNotOwnerNorApproved()"))); - base.burn(nextTokenId); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `isApprovedOrOwner` - //////////////////////////////////////////////////////////////*/ - - function test_isApprovedOrOwner() public { - address recipient = address(0x123); - string memory _tokenURI = "tokenURI"; - - address operator = address(0x789); - - uint256 nextTokenId = base.nextTokenIdToMint(); - - vm.prank(deployer); - base.mintTo(recipient, _tokenURI); - - assertFalse(base.isApprovedOrOwner(operator, nextTokenId)); - assertEq(base.isApprovedOrOwner(recipient, nextTokenId), true); - - vm.prank(recipient); - base.approve(operator, nextTokenId); - - assertEq(base.isApprovedOrOwner(operator, nextTokenId), true); - } -} diff --git a/src/test/sdk/base/ERC721DelayedReveal.t.sol b/src/test/sdk/base/ERC721DelayedReveal.t.sol deleted file mode 100644 index dce4da3c2..000000000 --- a/src/test/sdk/base/ERC721DelayedReveal.t.sol +++ /dev/null @@ -1,136 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./BaseUtilTest.sol"; -import { ERC721DelayedReveal, BatchMintMetadata } from "contracts/base/ERC721DelayedReveal.sol"; - -contract BaseERC721DelayedRevealTest is BaseUtilTest { - ERC721DelayedReveal internal base; - using Strings for uint256; - - function setUp() public override { - vm.prank(deployer); - base = new ERC721DelayedReveal(deployer, NAME, SYMBOL, royaltyRecipient, royaltyBps); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `lazyMint` - //////////////////////////////////////////////////////////////*/ - - function test_state_lazyMint_noEncryptedURI() public { - uint256 _amount = 100; - string memory _baseURIForTokens = "baseURI/"; - bytes memory _encryptedBaseURI = ""; - - uint256 nextTokenId = base.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = base.lazyMint(_amount, _baseURIForTokens, _encryptedBaseURI); - - assertEq(nextTokenId + _amount, base.nextTokenIdToMint()); - assertEq(nextTokenId + _amount, batchId); - - for (uint256 i = 0; i < _amount; i += 1) { - string memory _tokenURI = base.tokenURI(i); - assertEq(_tokenURI, string(abi.encodePacked(_baseURIForTokens, i.toString()))); - } - - vm.stopPrank(); - } - - function test_state_lazyMint_withEncryptedURI() public { - uint256 _amount = 100; - string memory _baseURIForTokens = "baseURI/"; - string memory secretURI = "secretURI/"; - bytes memory key = "key"; - bytes memory _encryptedBaseURI = base.encryptDecrypt(bytes(secretURI), key); - bytes32 provenanceHash = keccak256(abi.encodePacked(secretURI, key, block.chainid)); - - uint256 nextTokenId = base.nextTokenIdToMint(); - - vm.startPrank(deployer); - uint256 batchId = base.lazyMint(_amount, _baseURIForTokens, abi.encode(_encryptedBaseURI, provenanceHash)); - - assertEq(nextTokenId + _amount, base.nextTokenIdToMint()); - assertEq(nextTokenId + _amount, batchId); - - for (uint256 i = 0; i < _amount; i += 1) { - string memory _tokenURI = base.tokenURI(i); - assertEq(_tokenURI, string(abi.encodePacked(_baseURIForTokens, "0"))); - } - - vm.stopPrank(); - } - - function test_revert_lazyMint_URIForNonExistentId() public { - uint256 _amount = 100; - string memory _baseURIForTokens = "baseURI/"; - - bytes memory key = "key"; - string memory secretURI = "secretURI/"; - bytes memory _encryptedBaseURI = base.encryptDecrypt(bytes(secretURI), key); - bytes32 provenanceHash = keccak256(abi.encodePacked(secretURI, key, block.chainid)); - - vm.startPrank(deployer); - base.lazyMint(_amount, _baseURIForTokens, abi.encode(_encryptedBaseURI, provenanceHash)); - - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidTokenId.selector, 100)); - base.tokenURI(100); - - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `reveal` - //////////////////////////////////////////////////////////////*/ - - function test_state_reveal() public { - uint256 _amount = 100; - string memory _tempURIForTokens = "tempURI/"; - string memory _baseURIForTokens = "baseURI/"; - bytes memory key = "key"; - bytes memory _encryptedBaseURI = base.encryptDecrypt(bytes(_baseURIForTokens), key); - bytes32 provenanceHash = keccak256(abi.encodePacked(_baseURIForTokens, key, block.chainid)); - - vm.startPrank(deployer); - base.lazyMint(_amount, _tempURIForTokens, abi.encode(_encryptedBaseURI, provenanceHash)); - - for (uint256 i = 0; i < _amount; i += 1) { - string memory _tokenURI = base.tokenURI(i); - assertEq(_tokenURI, string(abi.encodePacked(_tempURIForTokens, "0"))); - } - - base.reveal(0, "key"); - - for (uint256 i = 0; i < _amount; i += 1) { - string memory _tokenURI = base.tokenURI(i); - assertEq(_tokenURI, string(abi.encodePacked(_baseURIForTokens, i.toString()))); - } - - vm.stopPrank(); - } - - function test_revert_reveal_NotAuthorized() public { - uint256 _amount = 100; - string memory _tempURIForTokens = "tempURI/"; - string memory _baseURIForTokens = "baseURI/"; - bytes memory key = "key"; - bytes memory _encryptedBaseURI = base.encryptDecrypt(bytes(_baseURIForTokens), key); - bytes32 provenanceHash = keccak256(abi.encodePacked(_baseURIForTokens, key, block.chainid)); - - vm.prank(deployer); - base.lazyMint(_amount, _tempURIForTokens, abi.encode(_encryptedBaseURI, provenanceHash)); - - for (uint256 i = 0; i < _amount; i += 1) { - string memory _tokenURI = base.tokenURI(i); - assertEq(_tokenURI, string(abi.encodePacked(_tempURIForTokens, "0"))); - } - - vm.prank(address(0x345)); - vm.expectRevert("Not authorized"); - base.reveal(0, "key"); - } -} diff --git a/src/test/sdk/base/ERC721Drop.t.sol b/src/test/sdk/base/ERC721Drop.t.sol deleted file mode 100644 index 663485705..000000000 --- a/src/test/sdk/base/ERC721Drop.t.sol +++ /dev/null @@ -1,189 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./BaseUtilTest.sol"; -import { ERC721Drop } from "contracts/base/ERC721Drop.sol"; - -contract BaseERC721DropTest is BaseUtilTest { - ERC721Drop internal base; - using Strings for uint256; - - address recipient; - - function setUp() public override { - super.setUp(); - - recipient = address(0x123); - - vm.prank(signer); - base = new ERC721Drop(signer, NAME, SYMBOL, royaltyRecipient, royaltyBps, saleRecipient); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `claim` - //////////////////////////////////////////////////////////////*/ - - function test_state_claim_ZeroPrice() public { - vm.warp(1); - - address receiver = address(0x123); - address claimer = address(0x345); - string memory _baseURI = "baseURI/"; - uint256 _quantity = 10; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(receiver); - - bytes32[] memory proofs = new bytes32[](0); - - ERC721Drop.AllowlistProof memory alp; - alp.proof = proofs; - - ERC721Drop.ClaimCondition[] memory conditions = new ERC721Drop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(signer); - base.lazyMint(100, _baseURI, ""); - - vm.prank(signer); - base.setClaimConditions(conditions[0], false); - - vm.prank(claimer, claimer); - base.claim(receiver, _quantity, address(0), 0, alp, ""); - - assertEq(base.totalSupply(), currentTotalSupply + _quantity); - assertEq(base.balanceOf(receiver), currentBalanceOfRecipient + _quantity); - - for (uint256 i = 0; i < _quantity; i += 1) { - string memory _tokenURI = base.tokenURI(i); - assertEq(_tokenURI, string(abi.encodePacked(_baseURI, i.toString()))); - assertEq(base.ownerOf(i), receiver); - } - } - - function test_state_claim_NonZeroPrice_ERC20() public { - vm.warp(1); - - address receiver = address(0x123); - address claimer = address(0x345); - string memory _baseURI = "baseURI/"; - uint256 _quantity = 10; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(receiver); - - bytes32[] memory proofs = new bytes32[](0); - - ERC721Drop.AllowlistProof memory alp; - alp.proof = proofs; - - ERC721Drop.ClaimCondition[] memory conditions = new ERC721Drop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - // set price and currency - conditions[0].pricePerToken = 1; - conditions[0].currency = address(erc20); - - vm.prank(signer); - base.lazyMint(100, _baseURI, ""); - - vm.prank(signer); - base.setClaimConditions(conditions[0], false); - - // mint erc20 to claimer, and approve to base - erc20.mint(claimer, 1_000); - vm.prank(claimer); - erc20.approve(address(base), 10); - - vm.prank(claimer, claimer); - base.claim(receiver, _quantity, address(erc20), 1, alp, ""); - - assertEq(base.totalSupply(), currentTotalSupply + _quantity); - assertEq(base.balanceOf(receiver), currentBalanceOfRecipient + _quantity); - - for (uint256 i = 0; i < _quantity; i += 1) { - string memory _tokenURI = base.tokenURI(i); - assertEq(_tokenURI, string(abi.encodePacked(_baseURI, i.toString()))); - assertEq(base.ownerOf(i), receiver); - } - } - - function test_state_claim_NonZeroPrice_NativeToken() public { - vm.warp(1); - - address receiver = address(0x123); - address claimer = address(0x345); - string memory _baseURI = "baseURI/"; - uint256 _quantity = 10; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(receiver); - - bytes32[] memory proofs = new bytes32[](0); - - ERC721Drop.AllowlistProof memory alp; - alp.proof = proofs; - - ERC721Drop.ClaimCondition[] memory conditions = new ERC721Drop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - // set price and currency - conditions[0].pricePerToken = 1; - conditions[0].currency = address(NATIVE_TOKEN); - - vm.prank(signer); - base.lazyMint(100, _baseURI, ""); - - vm.prank(signer); - base.setClaimConditions(conditions[0], false); - - // deal NATIVE_TOKEN to claimer - vm.deal(claimer, 1_000); - - vm.prank(claimer, claimer); - base.claim{ value: 10 }(receiver, _quantity, address(NATIVE_TOKEN), 1, alp, ""); - - assertEq(base.totalSupply(), currentTotalSupply + _quantity); - assertEq(base.balanceOf(receiver), currentBalanceOfRecipient + _quantity); - - for (uint256 i = 0; i < _quantity; i += 1) { - string memory _tokenURI = base.tokenURI(i); - assertEq(_tokenURI, string(abi.encodePacked(_baseURI, i.toString()))); - assertEq(base.ownerOf(i), receiver); - } - } - - function test_revert_claim_NotEnoughMintedTokens() public { - vm.warp(1); - - address receiver = address(0x123); - address claimer = address(0x345); - string memory _baseURI = "baseURI/"; - uint256 _quantity = 10; - - bytes32[] memory proofs = new bytes32[](0); - - ERC721Drop.AllowlistProof memory alp; - alp.proof = proofs; - - ERC721Drop.ClaimCondition[] memory conditions = new ERC721Drop.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.prank(signer); - base.lazyMint(100, _baseURI, ""); - - vm.prank(signer); - base.setClaimConditions(conditions[0], false); - - vm.expectRevert("Not enough minted tokens"); - vm.prank(claimer, claimer); - base.claim(receiver, _quantity + 1000, address(0), 0, alp, ""); - } -} diff --git a/src/test/sdk/base/ERC721LazyMint.t.sol b/src/test/sdk/base/ERC721LazyMint.t.sol deleted file mode 100644 index 241d82116..000000000 --- a/src/test/sdk/base/ERC721LazyMint.t.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./BaseUtilTest.sol"; -import { ERC721LazyMint } from "contracts/base/ERC721LazyMint.sol"; - -contract BaseERC721LazyMintTest is BaseUtilTest { - ERC721LazyMint internal base; - using Strings for uint256; - - uint256 _amount; - string _baseURIForTokens; - bytes _encryptedBaseURI; - - function setUp() public override { - vm.prank(deployer); - base = new ERC721LazyMint(deployer, NAME, SYMBOL, royaltyRecipient, royaltyBps); - - _amount = 10; - _baseURIForTokens = "baseURI/"; - _encryptedBaseURI = ""; - - vm.prank(deployer); - base.lazyMint(_amount, _baseURIForTokens, _encryptedBaseURI); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `claim` - //////////////////////////////////////////////////////////////*/ - - function test_state_claim() public { - address recipient = address(0x123); - uint256 quantity = 5; - - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - vm.startPrank(recipient); - - base.claim(recipient, quantity); - - assertEq(base.totalSupply(), currentTotalSupply + quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + quantity); - - for (uint256 i = 0; i < quantity; i += 1) { - string memory _tokenURI = base.tokenURI(i); - assertEq(_tokenURI, string(abi.encodePacked(_baseURIForTokens, i.toString()))); - assertEq(base.ownerOf(i), recipient); - } - - vm.stopPrank(); - } - - function test_revert_claim_NotEnoughTokens() public { - address recipient = address(0x123); - - vm.startPrank(recipient); - - vm.expectRevert("Not enough lazy minted tokens."); - base.claim(recipient, _amount + 1); - - vm.stopPrank(); - } -} diff --git a/src/test/sdk/base/ERC721Multiwrap.t.sol b/src/test/sdk/base/ERC721Multiwrap.t.sol deleted file mode 100644 index 30e718d23..000000000 --- a/src/test/sdk/base/ERC721Multiwrap.t.sol +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./BaseUtilTest.sol"; -import { ERC721Multiwrap } from "contracts/base/ERC721Multiwrap.sol"; -import { CurrencyTransferLib } from "contracts/lib/CurrencyTransferLib.sol"; -import { ITokenBundle } from "contracts/extension/interface/ITokenBundle.sol"; - -contract BaseERC721MultiwrapTest is BaseUtilTest { - ERC721Multiwrap internal base; - using Strings for uint256; - - Wallet internal tokenOwner; - string internal uriForWrappedToken; - ITokenBundle.Token[] internal wrappedContent; - - function setUp() public override { - super.setUp(); - - vm.prank(deployer); - base = new ERC721Multiwrap( - deployer, - NAME, - SYMBOL, - royaltyRecipient, - royaltyBps, - CurrencyTransferLib.NATIVE_TOKEN - ); - - tokenOwner = getWallet(); - uriForWrappedToken = "ipfs://baseURI/"; - - wrappedContent.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }) - ); - wrappedContent.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 0, - totalAmount: 1 - }) - ); - wrappedContent.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 0, - totalAmount: 100 - }) - ); - - erc20.mint(address(tokenOwner), 10 ether); - erc721.mint(address(tokenOwner), 1); - erc1155.mint(address(tokenOwner), 0, 100); - - tokenOwner.setAllowanceERC20(address(erc20), address(base), type(uint256).max); - tokenOwner.setApprovalForAllERC721(address(erc721), address(base), true); - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(base), true); - - vm.prank(deployer); - base.grantRole(keccak256("MINTER_ROLE"), address(tokenOwner)); - } - - function getWrappedContents(uint256 _tokenId) public view returns (ITokenBundle.Token[] memory contents) { - uint256 total = base.getTokenCountOfBundle(_tokenId); - contents = new ITokenBundle.Token[](total); - - for (uint256 i = 0; i < total; i += 1) { - contents[i] = base.getTokenOfBundle(_tokenId, i); - } - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `wrap` - //////////////////////////////////////////////////////////////*/ - - function test_state_wrap() public { - uint256 expectedIdForWrappedToken = base.nextTokenIdToMint(); - address recipient = address(0x123); - - vm.prank(address(tokenOwner)); - base.wrap(wrappedContent, uriForWrappedToken, recipient); - - assertEq(expectedIdForWrappedToken + 1, base.nextTokenIdToMint()); - - ITokenBundle.Token[] memory contentsOfWrappedToken = getWrappedContents(expectedIdForWrappedToken); - assertEq(contentsOfWrappedToken.length, wrappedContent.length); - for (uint256 i = 0; i < contentsOfWrappedToken.length; i += 1) { - assertEq(contentsOfWrappedToken[i].assetContract, wrappedContent[i].assetContract); - assertEq(uint256(contentsOfWrappedToken[i].tokenType), uint256(wrappedContent[i].tokenType)); - assertEq(contentsOfWrappedToken[i].tokenId, wrappedContent[i].tokenId); - assertEq(contentsOfWrappedToken[i].totalAmount, wrappedContent[i].totalAmount); - } - - assertEq(uriForWrappedToken, base.tokenURI(expectedIdForWrappedToken)); - } -} diff --git a/src/test/sdk/base/ERC721SignatureMint.t.sol b/src/test/sdk/base/ERC721SignatureMint.t.sol deleted file mode 100644 index 0dad51e65..000000000 --- a/src/test/sdk/base/ERC721SignatureMint.t.sol +++ /dev/null @@ -1,174 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "./BaseUtilTest.sol"; -import { ERC721SignatureMint } from "contracts/base/ERC721SignatureMint.sol"; - -contract BaseERC721SignatureMintTest is BaseUtilTest { - ERC721SignatureMint internal base; - using Strings for uint256; - - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - ERC721SignatureMint.MintRequest _mintrequest; - bytes _signature; - - address recipient; - - function setUp() public override { - super.setUp(); - vm.prank(signer); - base = new ERC721SignatureMint(signer, NAME, SYMBOL, royaltyRecipient, royaltyBps, saleRecipient); - - recipient = address(0x123); - erc20.mint(recipient, 1_000); - vm.deal(recipient, 1_000); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,string uri,uint256 quantity,uint256 pricePerToken,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("SignatureMintERC721")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(base))); - - _mintrequest.to = recipient; - _mintrequest.royaltyRecipient = royaltyRecipient; - _mintrequest.royaltyBps = royaltyBps; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.uri = "ipfs://"; - _mintrequest.quantity = 1; - _mintrequest.pricePerToken = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - ERC721SignatureMint.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - keccak256(bytes(_request.uri)), - _request.quantity, - _request.pricePerToken, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintWithSignature` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintWithSignature_ZeroPrice() public { - vm.warp(1000); - - uint256 nextTokenId = base.nextTokenIdToMint(); - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - base.mintWithSignature(_mintrequest, _signature); - - assertEq(base.nextTokenIdToMint(), nextTokenId + _mintrequest.quantity); - assertEq(base.tokenURI(nextTokenId), string(abi.encodePacked(_mintrequest.uri))); - assertEq(base.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - assertEq(base.ownerOf(nextTokenId), recipient); - } - - function test_state_mintWithSignature_NonZeroPrice_ERC20() public { - vm.warp(1000); - - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - erc20.approve(address(base), 1); - - uint256 nextTokenId = base.nextTokenIdToMint(); - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - vm.prank(recipient); - base.mintWithSignature(_mintrequest, _signature); - - assertEq(base.nextTokenIdToMint(), nextTokenId + _mintrequest.quantity); - assertEq(base.tokenURI(nextTokenId), string(abi.encodePacked(_mintrequest.uri))); - assertEq(base.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - assertEq(base.ownerOf(nextTokenId), recipient); - } - - function test_state_mintWithSignature_NonZeroPrice_NativeToken() public { - vm.warp(1000); - - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - uint256 nextTokenId = base.nextTokenIdToMint(); - uint256 currentTotalSupply = base.totalSupply(); - uint256 currentBalanceOfRecipient = base.balanceOf(recipient); - - vm.deal(recipient, 1); - - vm.prank(recipient); - base.mintWithSignature{ value: 1 }(_mintrequest, _signature); - - assertEq(base.nextTokenIdToMint(), nextTokenId + _mintrequest.quantity); - assertEq(base.tokenURI(nextTokenId), string(abi.encodePacked(_mintrequest.uri))); - assertEq(base.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(base.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - assertEq(base.ownerOf(nextTokenId), recipient); - } - - function test_revert_mintWithSignature_MustSendTotalPrice() public { - vm.warp(1000); - - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("Invalid msg value"); - base.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_QuantityNotOne() public { - vm.warp(1000); - - _mintrequest.quantity = 0; - _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("quantiy must be 1"); - base.mintWithSignature(_mintrequest, _signature); - } -} diff --git a/src/test/sdk/extension/BatchMintMetadata.t.sol b/src/test/sdk/extension/BatchMintMetadata.t.sol deleted file mode 100644 index 31da1aa4d..000000000 --- a/src/test/sdk/extension/BatchMintMetadata.t.sol +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/BatchMintMetadata.sol"; - -contract MyBatchMintMetadata is BatchMintMetadata { - function setBaseURI(uint256 _batchId, string memory _baseURI) external { - _setBaseURI(_batchId, _baseURI); - } - - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function viewBaseURI(uint256 _tokenId) external view returns (string memory) { - return _getBaseURI(_tokenId); - } - - function freezeBaseURI(uint256 _batchId) external { - _freezeBaseURI(_batchId); - } -} - -contract ExtensionBatchMintMetadata is DSTest, Test { - MyBatchMintMetadata internal ext; - - function setUp() public { - ext = new MyBatchMintMetadata(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `batchMintMetadata` - //////////////////////////////////////////////////////////////*/ - - function test_state_batchMintMetadata() public { - (uint256 nextTokenIdToMint, uint256 batchId) = ext.batchMintMetadata(0, 100, ""); - assertEq(nextTokenIdToMint, 100); - assertEq(batchId, 100); - - (nextTokenIdToMint, batchId) = ext.batchMintMetadata(100, 100, ""); - assertEq(nextTokenIdToMint, 200); - assertEq(batchId, 200); - - assertEq(2, ext.getBaseURICount()); - - assertEq(100, ext.getBatchIdAtIndex(0)); - assertEq(200, ext.getBatchIdAtIndex(1)); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `setBaseURI` - //////////////////////////////////////////////////////////////*/ - - function test_state_setBaseURI() public { - string memory baseUriOne = "one"; - string memory baseUriTwo = "two"; - - (, uint256 batchId) = ext.batchMintMetadata(0, 100, baseUriOne); - - assertEq(baseUriOne, ext.viewBaseURI(10)); - - ext.setBaseURI(batchId, baseUriTwo); - assertEq(baseUriTwo, ext.viewBaseURI(10)); - } - - function test_setBaseURI_revert_frozen() public { - string memory baseUriOne = "one"; - (, uint256 batchId) = ext.batchMintMetadata(0, 100, baseUriOne); - - ext.freezeBaseURI(batchId); - - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintMetadataFrozen.selector, batchId)); - string memory baseUri = "one"; - ext.setBaseURI(batchId, baseUri); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `freezeBaseURI` - //////////////////////////////////////////////////////////////*/ - - function test_state_freezeBaseURI() public { - string memory baseUriOne = "one"; - (, uint256 batchId) = ext.batchMintMetadata(0, 100, baseUriOne); - - ext.freezeBaseURI(batchId); - assertEq(ext.batchFrozen(batchId), true); - } - - function test_freezeBaseURI_revert_invalidBatch() public { - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidBatchId.selector, 100)); - ext.freezeBaseURI(100); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `viewBaseURI` - //////////////////////////////////////////////////////////////*/ - - function test_viewBaseURI_revert_invalidTokenId() public { - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidTokenId.selector, 100)); - ext.viewBaseURI(100); - } -} diff --git a/src/test/sdk/extension/ContractMetadata.t.sol b/src/test/sdk/extension/ContractMetadata.t.sol deleted file mode 100644 index ad6e7060d..000000000 --- a/src/test/sdk/extension/ContractMetadata.t.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { ContractMetadata } from "contracts/extension/ContractMetadata.sol"; - -contract MyContractMetadata is ContractMetadata { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSetContractURI() internal view override returns (bool) { - return condition; - } -} - -contract ExtensionContractMetadataTest is DSTest, Test { - MyContractMetadata internal ext; - event ContractURIUpdated(string prevURI, string newURI); - - function setUp() public { - ext = new MyContractMetadata(); - } - - function test_state_setContractURI() public { - ext.setCondition(true); - - string memory uri = "uri_string"; - ext.setContractURI(uri); - - string memory contractURI = ext.contractURI(); - - assertEq(contractURI, uri); - } - - function test_revert_setContractURI() public { - vm.expectRevert(abi.encodeWithSelector(ContractMetadata.ContractMetadataUnauthorized.selector)); - ext.setContractURI(""); - } - - function test_event_setContractURI() public { - ext.setCondition(true); - string memory uri = "uri_string"; - - vm.expectEmit(true, true, true, true); - emit ContractURIUpdated("", uri); - - ext.setContractURI(uri); - } -} diff --git a/src/test/sdk/extension/DelayedReveal.t.sol b/src/test/sdk/extension/DelayedReveal.t.sol deleted file mode 100644 index 12576b227..000000000 --- a/src/test/sdk/extension/DelayedReveal.t.sol +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { DelayedReveal } from "contracts/extension/DelayedReveal.sol"; - -contract MyDelayedReveal is DelayedReveal { - function setEncryptedData(uint256 _batchId, bytes memory _encryptedData) external { - _setEncryptedData(_batchId, _encryptedData); - } - - function reveal(uint256 identifier, bytes calldata key) external returns (string memory revealedURI) {} -} - -contract ExtensionDelayedReveal is DSTest, Test { - MyDelayedReveal internal ext; - - function setUp() public { - ext = new MyDelayedReveal(); - } - - function test_state_setEncryptedData() public { - string memory uriToEncrypt = "uri_string"; - bytes memory key = "key"; - - bytes memory encryptedUri = ext.encryptDecrypt(bytes(uriToEncrypt), key); - bytes32 provenanceHash = keccak256(abi.encodePacked(uriToEncrypt, key, block.chainid)); - - bytes memory data = abi.encode(encryptedUri, provenanceHash); - - ext.setEncryptedData(0, data); - - assertEq(true, ext.isEncryptedBatch(0)); - } - - function test_state_getRevealURI() public { - string memory uriToEncrypt = "uri_string"; - bytes memory key = "key"; - - bytes memory encryptedUri = ext.encryptDecrypt(bytes(uriToEncrypt), key); - bytes32 provenanceHash = keccak256(abi.encodePacked(uriToEncrypt, key, block.chainid)); - - bytes memory data = abi.encode(encryptedUri, provenanceHash); - - ext.setEncryptedData(0, data); - - string memory revealedURI = ext.getRevealURI(0, key); - - assertEq(uriToEncrypt, revealedURI); - } - - function test_revert_getRevealURI_IncorrectKey() public { - string memory uriToEncrypt = "uri_string"; - bytes memory key = "key"; - bytes memory incorrectKey = "incorrect key"; - - bytes memory encryptedUri = ext.encryptDecrypt(bytes(uriToEncrypt), key); - bytes32 provenanceHash = keccak256(abi.encodePacked(uriToEncrypt, key, block.chainid)); - string memory incorrectURI = string(ext.encryptDecrypt(encryptedUri, incorrectKey)); - - bytes memory data = abi.encode(encryptedUri, provenanceHash); - - ext.setEncryptedData(0, data); - - vm.expectRevert( - abi.encodeWithSelector( - DelayedReveal.DelayedRevealIncorrectResultHash.selector, - provenanceHash, - keccak256(abi.encodePacked(incorrectURI, incorrectKey, block.chainid)) - ) - ); - ext.getRevealURI(0, incorrectKey); - } - - function test_revert_getRevealURI_NothingToReveal() public { - string memory uriToEncrypt = "uri_string"; - bytes memory key = "key"; - - bytes memory encryptedUri = ext.encryptDecrypt(bytes(uriToEncrypt), key); - bytes32 provenanceHash = keccak256(abi.encodePacked(uriToEncrypt, key, block.chainid)); - - bytes memory data = abi.encode(encryptedUri, provenanceHash); - - ext.setEncryptedData(0, data); - assertEq(true, ext.isEncryptedBatch(0)); - - ext.setEncryptedData(0, ""); - assertFalse(ext.isEncryptedBatch(0)); - - vm.expectRevert(abi.encodeWithSelector(DelayedReveal.DelayedRevealNothingToReveal.selector)); - ext.getRevealURI(0, key); - } -} diff --git a/src/test/sdk/extension/DropSinglePhase.t.sol b/src/test/sdk/extension/DropSinglePhase.t.sol deleted file mode 100644 index 51d684eb6..000000000 --- a/src/test/sdk/extension/DropSinglePhase.t.sol +++ /dev/null @@ -1,243 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; -import { Strings } from "contracts/lib/Strings.sol"; - -import { DropSinglePhase } from "contracts/extension/DropSinglePhase.sol"; - -contract MyDropSinglePhase is DropSinglePhase { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSetClaimConditions() internal view override returns (bool) { - return condition; - } - - function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal override {} - - function _transferTokensOnClaim( - address _to, - uint256 _quantityBeingClaimed - ) internal override returns (uint256 startTokenId) {} -} - -contract ExtensionDropSinglePhase is DSTest, Test { - using Strings for uint256; - MyDropSinglePhase internal ext; - - event TokensClaimed( - address indexed claimer, - address indexed receiver, - uint256 indexed startTokenId, - uint256 quantityClaimed - ); - event ClaimConditionUpdated(MyDropSinglePhase.ClaimCondition condition, bool resetEligibility); - - function setUp() public { - ext = new MyDropSinglePhase(); - } - - /*/////////////////////////////////////////////////////////////// - Claim Tests - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing revert condition; exceed max claimable supply. - */ - function test_revert_claimCondition_exceedMaxClaimableSupply() public { - ext.setCondition(true); - vm.warp(1); - - address receiver = address(0x123); - address claimer1 = address(0x345); - address claimer2 = address(0x567); - bytes32[] memory proofs = new bytes32[](0); - - MyDropSinglePhase.AllowlistProof memory alp; - alp.proof = proofs; - - MyDropSinglePhase.ClaimCondition[] memory conditions = new MyDropSinglePhase.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - ext.setClaimConditions(conditions[0], false); - - vm.prank(claimer1, claimer1); - ext.claim(receiver, 100, address(0), 0, alp, ""); - - vm.expectRevert( - abi.encodeWithSelector( - DropSinglePhase.DropClaimExceedMaxSupply.selector, - conditions[0].maxClaimableSupply, - 1 + conditions[0].maxClaimableSupply - ) - ); - vm.prank(claimer2, claimer2); - ext.claim(receiver, 1, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction when no allowlist present. - */ - function test_fuzz_claim_noAllowlist(uint256 x) public { - ext.setCondition(true); - vm.assume(x != 0); - vm.warp(1); - - address receiver = address(0x123); - address claimer = address(0x345); - bytes32[] memory proofs = new bytes32[](0); - - MyDropSinglePhase.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - - MyDropSinglePhase.ClaimCondition[] memory conditions = new MyDropSinglePhase.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - ext.setClaimConditions(conditions[0], false); - - vm.prank(claimer, claimer); - vm.expectRevert( - abi.encodeWithSelector( - DropSinglePhase.DropClaimExceedLimit.selector, - conditions[0].quantityLimitPerWallet, - 101 - ) - ); - ext.claim(receiver, 101, address(0), 0, alp, ""); - - ext.setClaimConditions(conditions[0], true); - - vm.prank(claimer, claimer); - vm.expectRevert( - abi.encodeWithSelector( - DropSinglePhase.DropClaimExceedLimit.selector, - conditions[0].quantityLimitPerWallet, - 101 - ) - ); - ext.claim(receiver, 101, address(0), 0, alp, ""); - } - - function test_fuzz_claim_merkleProof(uint256 x) public { - ext.setCondition(true); - vm.assume(x > 10 && x < 500); - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(x); - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - MyDropSinglePhase.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - alp.pricePerToken = 0; - alp.currency = address(0); - - vm.warp(1); - - address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - // bytes32[] memory proofs = new bytes32[](0); - - MyDropSinglePhase.ClaimCondition[] memory conditions = new MyDropSinglePhase.ClaimCondition[](1); - conditions[0].maxClaimableSupply = x; - conditions[0].quantityLimitPerWallet = 1; - conditions[0].merkleRoot = root; - - ext.setClaimConditions(conditions[0], false); - - // vm.prank(getActor(5), getActor(5)); - vm.prank(receiver, receiver); - ext.claim(receiver, x - 5, address(0), 0, alp, ""); - assertEq(ext.getSupplyClaimedByWallet(receiver), x - 5); - - vm.prank(receiver, receiver); - vm.expectRevert( - abi.encodeWithSelector(DropSinglePhase.DropClaimExceedLimit.selector, alp.quantityLimitPerWallet, x + 1) - ); - ext.claim(receiver, 6, address(0), 0, alp, ""); - - vm.prank(receiver, receiver); - ext.claim(receiver, 5, address(0), 0, alp, ""); - assertEq(ext.getSupplyClaimedByWallet(receiver), x); - - vm.prank(receiver, receiver); - vm.expectRevert( - abi.encodeWithSelector(DropSinglePhase.DropClaimExceedLimit.selector, alp.quantityLimitPerWallet, x + 5) - ); - ext.claim(receiver, 5, address(0), 0, alp, ""); - } - - /** - * note: Testing event emission on setClaimConditions. - */ - function test_event_setClaimConditions() public { - ext.setCondition(true); - vm.warp(1); - - bytes32[] memory proofs = new bytes32[](0); - - MyDropSinglePhase.AllowlistProof memory alp; - alp.proof = proofs; - - MyDropSinglePhase.ClaimCondition[] memory conditions = new MyDropSinglePhase.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.expectEmit(true, true, true, true); - emit ClaimConditionUpdated(conditions[0], false); - - ext.setClaimConditions(conditions[0], false); - } - - /** - * note: Testing event emission on claim. - */ - function test_event_claim() public { - ext.setCondition(true); - vm.warp(1); - - address receiver = address(0x123); - address claimer = address(0x345); - bytes32[] memory proofs = new bytes32[](0); - - MyDropSinglePhase.AllowlistProof memory alp; - alp.proof = proofs; - - MyDropSinglePhase.ClaimCondition[] memory conditions = new MyDropSinglePhase.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - ext.setClaimConditions(conditions[0], false); - - vm.startPrank(claimer, claimer); - - vm.expectEmit(true, true, true, true); - emit TokensClaimed(claimer, receiver, 0, 1); - - ext.claim(receiver, 1, address(0), 0, alp, ""); - } -} diff --git a/src/test/sdk/extension/DropSinglePhase1155.t.sol b/src/test/sdk/extension/DropSinglePhase1155.t.sol deleted file mode 100644 index dc6e02689..000000000 --- a/src/test/sdk/extension/DropSinglePhase1155.t.sol +++ /dev/null @@ -1,230 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { DropSinglePhase1155 } from "contracts/extension/DropSinglePhase1155.sol"; - -contract MyDropSinglePhase1155 is DropSinglePhase1155 { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSetClaimConditions() internal view override returns (bool) { - return condition; - } - - function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal override {} - - function _transferTokensOnClaim(address _to, uint256 _tokenId, uint256 _quantityBeingClaimed) internal override {} -} - -contract ExtensionDropSinglePhase1155 is DSTest, Test { - MyDropSinglePhase1155 internal ext; - - event TokensClaimed( - address indexed claimer, - address indexed receiver, - uint256 indexed tokenId, - uint256 quantityClaimed - ); - event ClaimConditionUpdated( - uint256 indexed tokenId, - MyDropSinglePhase1155.ClaimCondition condition, - bool resetEligibility - ); - - function setUp() public { - ext = new MyDropSinglePhase1155(); - } - - /*/////////////////////////////////////////////////////////////// - Claim Tests - //////////////////////////////////////////////////////////////*/ - - /** - * note: Testing revert condition; exceed max claimable supply. - */ - function test_revert_claimCondition_exceedMaxClaimableSupply() public { - ext.setCondition(true); - vm.warp(1); - - address receiver = address(0x123); - address claimer1 = address(0x345); - address claimer2 = address(0x567); - bytes32[] memory proofs = new bytes32[](0); - uint256 _tokenId = 0; - - MyDropSinglePhase1155.AllowlistProof memory alp; - alp.proof = proofs; - - MyDropSinglePhase1155.ClaimCondition[] memory conditions = new MyDropSinglePhase1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - ext.setClaimConditions(_tokenId, conditions[0], false); - - vm.prank(claimer1, claimer1); - ext.claim(receiver, _tokenId, 100, address(0), 0, alp, ""); - - vm.expectRevert("!MaxSupply"); - vm.prank(claimer2, claimer2); - ext.claim(receiver, _tokenId, 1, address(0), 0, alp, ""); - } - - /** - * note: Testing quantity limit restriction when no allowlist present. - */ - function test_fuzz_claim_noAllowlist(uint256 x) public { - ext.setCondition(true); - vm.assume(x != 0); - vm.warp(1); - - address receiver = address(0x123); - address claimer = address(0x345); - bytes32[] memory proofs = new bytes32[](0); - uint256 _tokenId = 0; - - MyDropSinglePhase1155.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = x; - - MyDropSinglePhase1155.ClaimCondition[] memory conditions = new MyDropSinglePhase1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 500; - conditions[0].quantityLimitPerWallet = 100; - - ext.setClaimConditions(_tokenId, conditions[0], false); - - bytes memory errorQty = "!Qty"; - - vm.prank(claimer, claimer); - vm.expectRevert(errorQty); - ext.claim(receiver, _tokenId, 101, address(0), 0, alp, ""); - - ext.setClaimConditions(_tokenId, conditions[0], true); - - vm.prank(claimer, claimer); - vm.expectRevert(errorQty); - ext.claim(receiver, _tokenId, 101, address(0), 0, alp, ""); - } - - /** - * note: Testing event emission on setClaimConditions. - */ - function test_event_setClaimConditions() public { - ext.setCondition(true); - vm.warp(1); - - bytes32[] memory proofs = new bytes32[](0); - uint256 _tokenId = 0; - - MyDropSinglePhase1155.AllowlistProof memory alp; - alp.proof = proofs; - - MyDropSinglePhase1155.ClaimCondition[] memory conditions = new MyDropSinglePhase1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - vm.expectEmit(true, true, true, true); - emit ClaimConditionUpdated(_tokenId, conditions[0], false); - - ext.setClaimConditions(_tokenId, conditions[0], false); - } - - /** - * note: Testing event emission on claim. - */ - function test_event_claim() public { - ext.setCondition(true); - vm.warp(1); - - address receiver = address(0x123); - address claimer = address(0x345); - bytes32[] memory proofs = new bytes32[](0); - uint256 _tokenId = 0; - - MyDropSinglePhase1155.AllowlistProof memory alp; - alp.proof = proofs; - - MyDropSinglePhase1155.ClaimCondition[] memory conditions = new MyDropSinglePhase1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - ext.setClaimConditions(_tokenId, conditions[0], false); - - vm.startPrank(claimer, claimer); - - vm.expectEmit(true, true, true, true); - emit TokensClaimed(claimer, receiver, _tokenId, 1); - - ext.claim(receiver, _tokenId, 1, address(0), 0, alp, ""); - } - - function test_claimCondition_resetEligibility_quantityLimitPerWallet() public { - ext.setCondition(true); - vm.warp(1); - - address receiver = address(0x123); - bytes32[] memory proofs = new bytes32[](0); - - MyDropSinglePhase1155.AllowlistProof memory alp; - alp.proof = proofs; - - MyDropSinglePhase1155.ClaimCondition[] memory conditions = new MyDropSinglePhase1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - ext.setClaimConditions(0, conditions[0], false); - - vm.prank(receiver, receiver); - ext.claim(receiver, 0, 10, address(0), 0, alp, ""); - assertEq(ext.getSupplyClaimedByWallet(0, receiver), 10); - - vm.roll(100); - ext.setClaimConditions(0, conditions[0], true); - assertEq(ext.getSupplyClaimedByWallet(0, receiver), 0); - - vm.prank(receiver, receiver); - ext.claim(receiver, 0, 10, address(0), 0, alp, ""); - assertEq(ext.getSupplyClaimedByWallet(0, receiver), 10); - } - - /** - * note: Testing state; unique condition Id for every token. - */ - function test_state_claimCondition_uniqueConditionId() public { - ext.setCondition(true); - vm.warp(1); - - address receiver = address(0x123); - address claimer1 = address(0x345); - bytes32[] memory proofs = new bytes32[](0); - uint256 _tokenId = 0; - - MyDropSinglePhase1155.AllowlistProof memory alp; - alp.proof = proofs; - - MyDropSinglePhase1155.ClaimCondition[] memory conditions = new MyDropSinglePhase1155.ClaimCondition[](1); - conditions[0].maxClaimableSupply = 100; - conditions[0].quantityLimitPerWallet = 100; - - ext.setClaimConditions(_tokenId, conditions[0], false); - - vm.prank(claimer1, claimer1); - ext.claim(receiver, _tokenId, 100, address(0), 0, alp, ""); - - assertEq(ext.getSupplyClaimedByWallet(_tokenId, claimer1), 100); - - // supply claimed for other tokenIds should be 0 - assertEq(ext.getSupplyClaimedByWallet(1, claimer1), 0); - assertEq(ext.getSupplyClaimedByWallet(2, claimer1), 0); - } -} diff --git a/src/test/sdk/extension/ExtensionUtilTest.sol b/src/test/sdk/extension/ExtensionUtilTest.sol deleted file mode 100644 index 694ab6946..000000000 --- a/src/test/sdk/extension/ExtensionUtilTest.sol +++ /dev/null @@ -1,116 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; -import "../../utils/Wallet.sol"; -import "../../mocks/WETH9.sol"; -import "../../mocks/MockERC20.sol"; -import "../../mocks/MockERC721.sol"; -import "../../mocks/MockERC1155.sol"; -import { MockERC721NonBurnable } from "../../mocks/MockERC721NonBurnable.sol"; -import { MockERC1155NonBurnable } from "../../mocks/MockERC1155NonBurnable.sol"; -import "contracts/infra/forwarder/Forwarder.sol"; - -abstract contract ExtensionUtilTest is DSTest, Test { - string public constant NAME = "NAME"; - string public constant SYMBOL = "SYMBOL"; - string public constant CONTRACT_URI = "CONTRACT_URI"; - address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - MockERC20 public erc20; - MockERC721 public erc721; - MockERC1155 public erc1155; - MockERC721NonBurnable public erc721NonBurnable; - MockERC1155NonBurnable public erc1155NonBurnable; - WETH9 public weth; - - address public forwarder; - - address public deployer = address(0x20000); - address public saleRecipient = address(0x30000); - address public royaltyRecipient = address(0x30001); - address public platformFeeRecipient = address(0x30002); - uint128 public royaltyBps = 500; // 5% - uint128 public platformFeeBps = 500; // 5% - uint256 public constant MAX_BPS = 10_000; // 100% - - uint256 public privateKey = 1234; - address public signer; - - mapping(bytes32 => address) public contracts; - - function setUp() public virtual { - signer = vm.addr(privateKey); - - erc20 = new MockERC20(); - erc721 = new MockERC721(); - erc1155 = new MockERC1155(); - erc721NonBurnable = new MockERC721NonBurnable(); - erc1155NonBurnable = new MockERC1155NonBurnable(); - weth = new WETH9(); - forwarder = address(new Forwarder()); - } - - function getActor(uint160 _index) public pure returns (address) { - return address(uint160(0x50000 + _index)); - } - - function getWallet() public returns (Wallet wallet) { - wallet = new Wallet(); - } - - function assertIsOwnerERC721(address _token, address _owner, uint256[] memory _tokenIds) internal { - for (uint256 i = 0; i < _tokenIds.length; i += 1) { - bool isOwnerOfToken = MockERC721(_token).ownerOf(_tokenIds[i]) == _owner; - assertTrue(isOwnerOfToken); - } - } - - function assertIsNotOwnerERC721(address _token, address _owner, uint256[] memory _tokenIds) internal { - for (uint256 i = 0; i < _tokenIds.length; i += 1) { - bool isOwnerOfToken = MockERC721(_token).ownerOf(_tokenIds[i]) == _owner; - assertTrue(!isOwnerOfToken); - } - } - - function assertBalERC1155Eq( - address _token, - address _owner, - uint256[] memory _tokenIds, - uint256[] memory _amounts - ) internal { - require(_tokenIds.length == _amounts.length, "unequal lengths"); - - for (uint256 i = 0; i < _tokenIds.length; i += 1) { - assertEq(MockERC1155(_token).balanceOf(_owner, _tokenIds[i]), _amounts[i]); - } - } - - function assertBalERC1155Gte( - address _token, - address _owner, - uint256[] memory _tokenIds, - uint256[] memory _amounts - ) internal { - require(_tokenIds.length == _amounts.length, "unequal lengths"); - - for (uint256 i = 0; i < _tokenIds.length; i += 1) { - assertTrue(MockERC1155(_token).balanceOf(_owner, _tokenIds[i]) >= _amounts[i]); - } - } - - function assertBalERC20Eq(address _token, address _owner, uint256 _amount) internal { - assertEq(MockERC20(_token).balanceOf(_owner), _amount); - } - - function assertBalERC20Gte(address _token, address _owner, uint256 _amount) internal { - assertTrue(MockERC20(_token).balanceOf(_owner) >= _amount); - } - - function forwarders() public view returns (address[] memory) { - address[] memory _forwarders = new address[](1); - _forwarders[0] = forwarder; - return _forwarders; - } -} diff --git a/src/test/sdk/extension/LazyMint.t.sol b/src/test/sdk/extension/LazyMint.t.sol deleted file mode 100644 index ae152d1e8..000000000 --- a/src/test/sdk/extension/LazyMint.t.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { LazyMint } from "contracts/extension/LazyMint.sol"; - -contract MyLazyMint is LazyMint { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canLazyMint() internal view override returns (bool) { - return condition; - } -} - -contract ExtensionLazyMint is DSTest, Test { - MyLazyMint internal ext; - event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI); - - function setUp() public { - ext = new MyLazyMint(); - } - - function test_state_lazyMint() public { - ext.setCondition(true); - - string memory uri = "uri_string"; - uint256 batchId = ext.lazyMint(100, uri, ""); - - assertEq(batchId, 100); - assertEq(1, ext.getBaseURICount()); - - batchId = ext.lazyMint(200, uri, ""); - - assertEq(batchId, 300); - assertEq(2, ext.getBaseURICount()); - } - - function test_state_lazyMint_NotAuthorized() public { - vm.expectRevert(abi.encodeWithSelector(LazyMint.LazyMintUnauthorized.selector)); - ext.lazyMint(100, "", ""); - } - - function test_state_lazyMint_ZeroAmount() public { - ext.setCondition(true); - vm.expectRevert(abi.encodeWithSelector(LazyMint.LazyMintInvalidAmount.selector)); - ext.lazyMint(0, "", ""); - } - - function test_event_lazyMint() public { - ext.setCondition(true); - - vm.expectEmit(true, true, true, true); - emit TokensLazyMinted(0, 99, "", ""); - ext.lazyMint(100, "", ""); - } -} diff --git a/src/test/sdk/extension/NFTMetadata.t.sol b/src/test/sdk/extension/NFTMetadata.t.sol deleted file mode 100644 index 819921433..000000000 --- a/src/test/sdk/extension/NFTMetadata.t.sol +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { NFTMetadata } from "contracts/extension/NFTMetadata.sol"; - -contract NFTMetadataHarness is NFTMetadata { - address private authorized; - - constructor() { - authorized = msg.sender; - } - - function _canSetMetadata() internal view override returns (bool) { - if (msg.sender == authorized) return true; - return false; - } - - function _canFreezeMetadata() internal view override returns (bool) { - if (msg.sender == authorized) return true; - return false; - } - - function getTokenURI(uint256 _tokenId) external view returns (string memory) { - return _getTokenURI(_tokenId); - } - - function URIStatus() external view returns (bool) { - return uriFrozen; - } - - function supportsInterface(bytes4 interfaceId) external view override returns (bool) {} -} - -contract ExtensionNFTMetadata is DSTest, Test { - NFTMetadataHarness internal ext; - - function setUp() public { - ext = new NFTMetadataHarness(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `setTokenURI` - //////////////////////////////////////////////////////////////*/ - - function test_setTokenURI_state() public { - string memory uri = "test"; - ext.setTokenURI(0, uri); - assertEq(ext.getTokenURI(0), uri); - - string memory uri2 = "test2"; - ext.setTokenURI(0, uri2); - assertEq(ext.getTokenURI(0), uri2); - } - - function test_setTokenURI_revert_notAuthorized() public { - vm.startPrank(address(0x1)); - string memory uri = "test"; - vm.expectRevert(abi.encodeWithSelector(NFTMetadata.NFTMetadataUnauthorized.selector)); - ext.setTokenURI(1, uri); - } - - function test_setTokenURI_revert_emptyMetadata() public { - string memory uri = ""; - vm.expectRevert(abi.encodeWithSelector(NFTMetadata.NFTMetadataInvalidUrl.selector)); - ext.setTokenURI(1, uri); - } - - function test_setTokenURI_revert_frozen() public { - ext.freezeMetadata(); - string memory uri = "test"; - vm.expectRevert(abi.encodeWithSelector(NFTMetadata.NFTMetadataFrozen.selector, 2)); - ext.setTokenURI(2, uri); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `freezeMetadata` - //////////////////////////////////////////////////////////////*/ - - function test_freezeMetadata_state() public { - ext.freezeMetadata(); - assertEq(ext.URIStatus(), true); - } - - function test_freezeMetadata_revert_notAuthorized() public { - vm.startPrank(address(0x1)); - vm.expectRevert(abi.encodeWithSelector(NFTMetadata.NFTMetadataUnauthorized.selector)); - ext.freezeMetadata(); - } -} diff --git a/src/test/sdk/extension/Ownable.t.sol b/src/test/sdk/extension/Ownable.t.sol deleted file mode 100644 index ca8edbe64..000000000 --- a/src/test/sdk/extension/Ownable.t.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Ownable } from "contracts/extension/Ownable.sol"; - -contract MyOwnable is Ownable { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSetOwner() internal view override returns (bool) { - return condition; - } -} - -contract ExtensionOwnableTest is DSTest, Test { - MyOwnable internal ext; - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - - function setUp() public { - ext = new MyOwnable(); - } - - function test_state_setOwner() public { - ext.setCondition(true); - - address owner = address(0x123); - ext.setOwner(owner); - - address currentOwner = ext.owner(); - assertEq(currentOwner, owner); - } - - function test_revert_setOwner() public { - vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorized.selector)); - ext.setOwner(address(0x1234)); - } - - function test_event_setOwner() public { - ext.setCondition(true); - - address owner = address(0x123); - - vm.expectEmit(true, true, true, true); - emit OwnerUpdated(address(0), owner); - - ext.setOwner(owner); - } -} diff --git a/src/test/sdk/extension/Permissions.t.sol b/src/test/sdk/extension/Permissions.t.sol deleted file mode 100644 index 0c8bfdfbd..000000000 --- a/src/test/sdk/extension/Permissions.t.sol +++ /dev/null @@ -1,225 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Permissions, Strings } from "contracts/extension/Permissions.sol"; - -contract MyPermissions is Permissions { - constructor() { - _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); - } - - function setRoleAdmin(bytes32 role, bytes32 adminRole) external { - _setRoleAdmin(role, adminRole); - } - - function checkModifier() external view onlyRole(DEFAULT_ADMIN_ROLE) {} -} - -contract ExtensionPermissions is DSTest, Test { - MyPermissions internal ext; - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); - - address defaultAdmin; - - function setUp() public { - defaultAdmin = address(0x123); - - vm.prank(defaultAdmin); - ext = new MyPermissions(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `setRoleAdmin` - //////////////////////////////////////////////////////////////*/ - - function test_state_setRoleAdmin() public { - bytes32 role1 = "ROLE_1"; - bytes32 role2 = "ROLE_2"; - - bytes32 adminRole1 = "ADMIN_ROLE_1"; - bytes32 currentDefaultAdmin = ext.DEFAULT_ADMIN_ROLE(); - - ext.setRoleAdmin(role1, adminRole1); - - assertEq(adminRole1, ext.getRoleAdmin(role1)); - assertEq(currentDefaultAdmin, ext.getRoleAdmin(role2)); - } - - function test_event_roleAdminChanged() public { - bytes32 role1 = keccak256("ROLE_1"); - bytes32 adminRole1 = keccak256("ADMIN_ROLE_1"); - - bytes32 previousAdmin = ext.getRoleAdmin(role1); - - vm.expectEmit(true, true, true, true); - emit RoleAdminChanged(role1, previousAdmin, adminRole1); - ext.setRoleAdmin(role1, adminRole1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `grantRole` - //////////////////////////////////////////////////////////////*/ - - function test_state_grantRole() public { - bytes32 role1 = "ROLE_1"; - bytes32 role2 = "ROLE_2"; - - bytes32 adminRole1 = "ADMIN_ROLE_1"; - address adminOne = address(0x1); - - ext.setRoleAdmin(role1, adminRole1); - - vm.prank(defaultAdmin); - ext.grantRole(adminRole1, adminOne); - - vm.prank(adminOne); - ext.grantRole(role1, address(0x567)); - - vm.prank(defaultAdmin); - ext.grantRole(role2, address(0x567)); - - assertTrue(ext.hasRole(role1, address(0x567))); - assertTrue(ext.hasRole(role2, address(0x567))); - } - - function test_revert_grantRole_missingRole() public { - address caller = address(0x345); - - vm.startPrank(caller); - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - caller, - ext.DEFAULT_ADMIN_ROLE() - ) - ); - ext.grantRole(keccak256("role"), address(0x1)); - } - - function test_revert_grantRole_grantToHolder() public { - vm.startPrank(defaultAdmin); - ext.grantRole(keccak256("role"), address(0x1)); - - vm.expectRevert( - abi.encodeWithSelector(Permissions.PermissionsAlreadyGranted.selector, address(0x1), keccak256("role")) - ); - ext.grantRole(keccak256("role"), address(0x1)); - } - - function test_event_grantRole() public { - vm.startPrank(defaultAdmin); - - vm.expectEmit(true, true, true, true); - emit RoleGranted(keccak256("role"), address(0x1), defaultAdmin); - ext.grantRole(keccak256("role"), address(0x1)); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `revokeRole` - //////////////////////////////////////////////////////////////*/ - - function test_state_revokeRole() public { - vm.startPrank(defaultAdmin); - - ext.grantRole(keccak256("role"), address(0x567)); - assertTrue(ext.hasRole(keccak256("role"), address(0x567))); - - ext.revokeRole(keccak256("role"), address(0x567)); - assertFalse(ext.hasRole(keccak256("role"), address(0x567))); - } - - function test_revert_revokeRole_missingRole() public { - vm.prank(defaultAdmin); - ext.grantRole(keccak256("role"), address(0x567)); - assertTrue(ext.hasRole(keccak256("role"), address(0x567))); - - vm.startPrank(address(0x345)); - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - address(0x345), - ext.DEFAULT_ADMIN_ROLE() - ) - ); - ext.revokeRole(keccak256("role"), address(0x567)); - vm.stopPrank(); - - vm.startPrank(defaultAdmin); - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - address(0x789), - keccak256("role") - ) - ); - ext.revokeRole(keccak256("role"), address(0x789)); - vm.stopPrank(); - } - - function test_event_revokeRole() public { - vm.startPrank(defaultAdmin); - - ext.grantRole(keccak256("role"), address(0x1)); - - vm.expectEmit(true, true, true, true); - emit RoleRevoked(keccak256("role"), address(0x1), defaultAdmin); - ext.revokeRole(keccak256("role"), address(0x1)); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `renounceRole` - //////////////////////////////////////////////////////////////*/ - - function test_state_renounceRole() public { - vm.prank(defaultAdmin); - ext.grantRole(keccak256("role"), address(0x567)); - assertTrue(ext.hasRole(keccak256("role"), address(0x567))); - - vm.prank(address(0x567)); - ext.renounceRole(keccak256("role"), address(0x567)); - - assertFalse(ext.hasRole(keccak256("role"), address(0x567))); - } - - function test_revert_renounceRole_missingRole() public { - vm.startPrank(defaultAdmin); - vm.expectRevert( - abi.encodeWithSelector(Permissions.PermissionsUnauthorizedAccount.selector, defaultAdmin, keccak256("role")) - ); - ext.renounceRole(keccak256("role"), defaultAdmin); - vm.stopPrank(); - } - - function test_revert_renounceRole_renounceForOthers() public { - vm.startPrank(defaultAdmin); - ext.grantRole(keccak256("role"), address(0x567)); - assertTrue(ext.hasRole(keccak256("role"), address(0x567))); - - vm.expectRevert( - abi.encodeWithSelector(Permissions.PermissionsInvalidPermission.selector, defaultAdmin, address(0x567)) - ); - ext.renounceRole(keccak256("role"), address(0x567)); - vm.stopPrank(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `onlyRole` modifier - //////////////////////////////////////////////////////////////*/ - - function test_modifier_onlyRole() public { - vm.startPrank(address(0x345)); - vm.expectRevert( - abi.encodeWithSelector( - Permissions.PermissionsUnauthorizedAccount.selector, - address(0x345), - ext.DEFAULT_ADMIN_ROLE() - ) - ); - ext.checkModifier(); - } -} diff --git a/src/test/sdk/extension/PermissionsEnumerable.t.sol b/src/test/sdk/extension/PermissionsEnumerable.t.sol deleted file mode 100644 index 5a20187d4..000000000 --- a/src/test/sdk/extension/PermissionsEnumerable.t.sol +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { PermissionsEnumerable, Strings } from "contracts/extension/PermissionsEnumerable.sol"; - -contract MyPermissionsEnumerable is PermissionsEnumerable { - constructor() { - _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); - } -} - -contract ExtensionPermissionsEnumerable is DSTest, Test { - MyPermissionsEnumerable internal ext; - - address defaultAdmin; - - function setUp() public { - defaultAdmin = address(0x123); - - vm.prank(defaultAdmin); - ext = new MyPermissionsEnumerable(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `grantRole` - //////////////////////////////////////////////////////////////*/ - - function test_state_grantRole() public { - bytes32 role1 = keccak256("ROLE_1"); - - address[] memory members = new address[](3); - - members[0] = address(0); - members[1] = address(1); - members[2] = address(2); - - vm.startPrank(defaultAdmin); - - ext.grantRole(role1, members[0]); - assertEq(1, ext.getRoleMemberCount(role1)); - - ext.grantRole(role1, members[1]); - assertEq(2, ext.getRoleMemberCount(role1)); - - ext.grantRole(role1, members[2]); - assertEq(3, ext.getRoleMemberCount(role1)); - - for (uint256 i = 0; i < members.length; i++) { - assertEq(members[i], ext.getRoleMember(role1, i)); - } - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `revokeRole` - //////////////////////////////////////////////////////////////*/ - - function test_state_revokeRole() public { - bytes32 role1 = keccak256("ROLE_1"); - - address[] memory members = new address[](3); - - members[0] = address(0); - members[1] = address(1); - members[2] = address(2); - - vm.startPrank(defaultAdmin); - - ext.grantRole(role1, members[0]); - assertEq(1, ext.getRoleMemberCount(role1)); - - ext.grantRole(role1, members[1]); - assertEq(2, ext.getRoleMemberCount(role1)); - - ext.grantRole(role1, members[2]); - assertEq(3, ext.getRoleMemberCount(role1)); - - for (uint256 i = 0; i < members.length; i++) { - assertEq(members[i], ext.getRoleMember(role1, i)); - } - - // revoke roles, and check updated list of members - ext.revokeRole(role1, members[1]); - assertEq(2, ext.getRoleMemberCount(role1)); - assertEq(members[2], ext.getRoleMember(role1, 1)); - - ext.revokeRole(role1, members[0]); - assertEq(1, ext.getRoleMemberCount(role1)); - assertEq(members[2], ext.getRoleMember(role1, 0)); - - // re-grant roles, and check updated list of members - ext.grantRole(role1, members[0]); - assertEq(2, ext.getRoleMemberCount(role1)); - assertEq(members[2], ext.getRoleMember(role1, 0)); - assertEq(members[0], ext.getRoleMember(role1, 1)); - - ext.grantRole(role1, members[1]); - assertEq(3, ext.getRoleMemberCount(role1)); - assertEq(members[2], ext.getRoleMember(role1, 0)); - assertEq(members[0], ext.getRoleMember(role1, 1)); - assertEq(members[1], ext.getRoleMember(role1, 2)); - } -} diff --git a/src/test/sdk/extension/PlatformFee.t.sol b/src/test/sdk/extension/PlatformFee.t.sol deleted file mode 100644 index 9b5fd0128..000000000 --- a/src/test/sdk/extension/PlatformFee.t.sol +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { PlatformFee } from "contracts/extension/PlatformFee.sol"; - -contract MyPlatformFee is PlatformFee { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSetPlatformFeeInfo() internal view override returns (bool) { - return condition; - } -} - -contract ExtensionPlatformFee is DSTest, Test { - MyPlatformFee internal ext; - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - - function setUp() public { - ext = new MyPlatformFee(); - } - - function test_state_setPlatformFeeInfo() public { - ext.setCondition(true); - - address _platformFeeRecipient = address(0x123); - uint256 _platformFeeBps = 1000; - ext.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - - (address recipient, uint16 bps) = ext.getPlatformFeeInfo(); - assertEq(_platformFeeRecipient, recipient); - assertEq(_platformFeeBps, bps); - } - - function test_revert_setPlatformFeeInfo_ExceedsMaxBps() public { - ext.setCondition(true); - - address _platformFeeRecipient = address(0x123); - uint256 _platformFeeBps = 10001; - - vm.expectRevert( - abi.encodeWithSelector(PlatformFee.PlatformFeeExceededMaxFeeBps.selector, 10_000, _platformFeeBps) - ); - ext.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - function test_revert_setPlatformFeeInfo_NotAuthorized() public { - vm.expectRevert(abi.encodeWithSelector(PlatformFee.PlatformFeeUnauthorized.selector)); - ext.setPlatformFeeInfo(address(1), 1000); - } - - function test_event_platformFeeInfo() public { - ext.setCondition(true); - - address _platformFeeRecipient = address(0x123); - uint256 _platformFeeBps = 1000; - - vm.expectEmit(true, true, true, true); - emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps); - - ext.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } -} diff --git a/src/test/sdk/extension/PrimarySale.t.sol b/src/test/sdk/extension/PrimarySale.t.sol deleted file mode 100644 index acb30f37e..000000000 --- a/src/test/sdk/extension/PrimarySale.t.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { PrimarySale } from "contracts/extension/PrimarySale.sol"; - -contract MyPrimarySale is PrimarySale { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSetPrimarySaleRecipient() internal view override returns (bool) { - return condition; - } -} - -contract ExtensionPrimarySale is DSTest, Test { - MyPrimarySale internal ext; - event PrimarySaleRecipientUpdated(address indexed recipient); - - function setUp() public { - ext = new MyPrimarySale(); - } - - function test_state_setPrimarySaleRecipient() public { - ext.setCondition(true); - - address _primarySaleRecipient = address(0x123); - ext.setPrimarySaleRecipient(_primarySaleRecipient); - - address recipient = ext.primarySaleRecipient(); - assertEq(recipient, _primarySaleRecipient); - } - - function test_revert_setPrimarySaleRecipient_NotAuthorized() public { - address _primarySaleRecipient = address(0x123); - - vm.expectRevert(abi.encodeWithSelector(PrimarySale.PrimarySaleUnauthorized.selector)); - ext.setPrimarySaleRecipient(_primarySaleRecipient); - } - - function test_event_setPrimarySaleRecipient() public { - ext.setCondition(true); - - address _primarySaleRecipient = address(0x123); - - vm.expectEmit(true, true, true, true); - emit PrimarySaleRecipientUpdated(_primarySaleRecipient); - - ext.setPrimarySaleRecipient(_primarySaleRecipient); - } -} diff --git a/src/test/sdk/extension/Royalty.t.sol b/src/test/sdk/extension/Royalty.t.sol deleted file mode 100644 index 1c3a888a7..000000000 --- a/src/test/sdk/extension/Royalty.t.sol +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Royalty } from "contracts/extension/Royalty.sol"; - -contract MyRoyalty is Royalty { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSetRoyaltyInfo() internal view override returns (bool) { - return condition; - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { - return interfaceId == 0x01ffc9a7; - } -} - -contract ExtensionRoyaltyTest is DSTest, Test { - MyRoyalty internal ext; - event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); - event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps); - - function setUp() public { - ext = new MyRoyalty(); - } - - function test_state_setDefaultRoyaltyInfo() public { - ext.setCondition(true); - - address _royaltyRecipient = address(0x123); - uint256 _royaltyBps = 1000; - ext.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - - (address royaltyRecipient, uint256 royaltyBps) = ext.getDefaultRoyaltyInfo(); - assertEq(royaltyRecipient, _royaltyRecipient); - assertEq(royaltyBps, _royaltyBps); - - (address receiver, uint256 royaltyAmount) = ext.royaltyInfo(0, 100); - assertEq(receiver, _royaltyRecipient); - assertEq(royaltyAmount, (100 * 1000) / 10_000); - } - - function test_revert_setDefaultRoyaltyInfo_ExceedsMaxBps() public { - ext.setCondition(true); - - address _royaltyRecipient = address(0x123); - uint256 _royaltyBps = 10001; - - vm.expectRevert(abi.encodeWithSelector(Royalty.RoyaltyExceededMaxFeeBps.selector, 10_000, _royaltyBps)); - ext.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - } - - function test_state_setRoyaltyInfoForToken() public { - ext.setCondition(true); - - uint256 _tokenId = 1; - address _recipient = address(0x123); - uint256 _bps = 1000; - ext.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); - - (address receiver, uint256 royaltyAmount) = ext.royaltyInfo(_tokenId, 100); - assertEq(receiver, _recipient); - assertEq(royaltyAmount, (100 * 1000) / 10_000); - } - - function test_revert_setRoyaltyInfo_NotAuthorized() public { - vm.expectRevert(abi.encodeWithSelector(Royalty.RoyaltyUnauthorized.selector)); - ext.setRoyaltyInfoForToken(0, address(1), 1000); - } - - function test_revert_setRoyaltyInfoForToken_ExceedsMaxBps() public { - ext.setCondition(true); - - uint256 _tokenId = 1; - address _recipient = address(0x123); - uint256 _bps = 10001; - - vm.expectRevert(abi.encodeWithSelector(Royalty.RoyaltyExceededMaxFeeBps.selector, 10_000, _bps)); - ext.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); - } - - function test_event_defaultRoyalty() public { - ext.setCondition(true); - - address _royaltyRecipient = address(0x123); - uint256 _royaltyBps = 1000; - - vm.expectEmit(true, true, true, true); - emit DefaultRoyalty(_royaltyRecipient, _royaltyBps); - - ext.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - } - - function test_event_royaltyForToken() public { - ext.setCondition(true); - - uint256 _tokenId = 1; - address _recipient = address(0x123); - uint256 _bps = 1000; - - vm.expectEmit(true, true, true, true); - emit RoyaltyForToken(_tokenId, _recipient, _bps); - - ext.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); - } -} diff --git a/src/test/sdk/extension/SignatureMintERC1155.t.sol b/src/test/sdk/extension/SignatureMintERC1155.t.sol deleted file mode 100644 index 9de73c188..000000000 --- a/src/test/sdk/extension/SignatureMintERC1155.t.sol +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { SignatureMintERC1155 } from "contracts/extension/SignatureMintERC1155.sol"; - -contract MySigMint1155 is SignatureMintERC1155 { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSignMintRequest(address) internal view override returns (bool) { - return condition; - } - - function mintWithSignature( - MintRequest calldata req, - bytes calldata signature - ) external payable returns (address signer) { - if (!_canSignMintRequest(msg.sender)) { - revert("not authorized"); - } - - signer = _processRequest(req, signature); - } -} - -contract ExtensionSignatureMintERC1155 is DSTest, Test { - MySigMint1155 internal ext; - - uint256 public privateKey = 1234; - address public signer; - - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - MySigMint1155.MintRequest _mintrequest; - bytes _signature; - - function setUp() public { - ext = new MySigMint1155(); - - signer = vm.addr(privateKey); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,uint256 tokenId,string uri,uint256 quantity,uint256 pricePerToken,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("SignatureMintERC1155")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(ext))); - - _mintrequest.to = address(1); - _mintrequest.royaltyRecipient = address(2); - _mintrequest.royaltyBps = 0; - _mintrequest.primarySaleRecipient = address(2); - _mintrequest.tokenId = 0; - _mintrequest.uri = "ipfs://"; - _mintrequest.quantity = 1; - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(0x111); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - MySigMint1155.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = bytes.concat( - abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - _request.tokenId, - keccak256(bytes(_request.uri)) - ), - abi.encode( - _request.quantity, - _request.pricePerToken, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ) - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - function test_state_mintWithSignature() public { - vm.warp(1000); - ext.setCondition(true); - vm.prank(signer); - address recoveredSigner = ext.mintWithSignature(_mintrequest, _signature); - - assertEq(signer, recoveredSigner); - } - - function test_revert_mintWithSignature_NotAuthorized() public { - vm.expectRevert("not authorized"); - ext.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_InvalidReq() public { - vm.warp(1000); - ext.setCondition(true); - - vm.prank(signer); - ext.mintWithSignature(_mintrequest, _signature); - - vm.expectRevert("Invalid request"); - ext.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_RequestExpired() public { - vm.warp(3000); - ext.setCondition(true); - - vm.prank(signer); - vm.expectRevert("Request expired"); - ext.mintWithSignature(_mintrequest, _signature); - } -} diff --git a/src/test/sdk/extension/SignatureMintERC20.t.sol b/src/test/sdk/extension/SignatureMintERC20.t.sol deleted file mode 100644 index 452c28a29..000000000 --- a/src/test/sdk/extension/SignatureMintERC20.t.sol +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { SignatureMintERC20 } from "contracts/extension/SignatureMintERC20.sol"; - -contract MySigMint20 is SignatureMintERC20 { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSignMintRequest(address) internal view override returns (bool) { - return condition; - } - - function mintWithSignature( - MintRequest calldata req, - bytes calldata signature - ) external payable returns (address signer) { - if (!_canSignMintRequest(msg.sender)) { - revert("not authorized"); - } - - signer = _processRequest(req, signature); - } -} - -contract ExtensionSignatureMintERC20 is DSTest, Test { - MySigMint20 internal ext; - - uint256 public privateKey = 1234; - address public signer; - - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - MySigMint20.MintRequest _mintrequest; - bytes _signature; - - function setUp() public { - ext = new MySigMint20(); - - signer = vm.addr(privateKey); - - typehashMintRequest = keccak256( - "MintRequest(address to,address primarySaleRecipient,uint256 quantity,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("SignatureMintERC20")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(ext))); - - _mintrequest.to = address(1); - _mintrequest.primarySaleRecipient = address(2); - _mintrequest.quantity = 1; - _mintrequest.price = 1; - _mintrequest.currency = address(0x111); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - MySigMint20.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.primarySaleRecipient, - _request.quantity, - _request.price, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - function test_state_mintWithSignature() public { - vm.warp(1000); - ext.setCondition(true); - vm.prank(signer); - address recoveredSigner = ext.mintWithSignature(_mintrequest, _signature); - - assertEq(signer, recoveredSigner); - } - - function test_revert_mintWithSignature_NotAuthorized() public { - vm.expectRevert("not authorized"); - ext.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_InvalidReq() public { - vm.warp(1000); - ext.setCondition(true); - - vm.prank(signer); - ext.mintWithSignature(_mintrequest, _signature); - - vm.expectRevert("Invalid request"); - ext.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_RequestExpired() public { - vm.warp(3000); - ext.setCondition(true); - - vm.prank(signer); - vm.expectRevert("Request expired"); - ext.mintWithSignature(_mintrequest, _signature); - } -} diff --git a/src/test/sdk/extension/SignatureMintERC721.t.sol b/src/test/sdk/extension/SignatureMintERC721.t.sol deleted file mode 100644 index 85c0e7db1..000000000 --- a/src/test/sdk/extension/SignatureMintERC721.t.sol +++ /dev/null @@ -1,144 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { SignatureMintERC721 } from "contracts/extension/SignatureMintERC721.sol"; - -contract MySigMint721 is SignatureMintERC721 { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSignMintRequest(address) internal view override returns (bool) { - return condition; - } - - function mintWithSignature( - MintRequest calldata req, - bytes calldata signature - ) external payable returns (address signer) { - if (!_canSignMintRequest(msg.sender)) { - revert("not authorized"); - } - - signer = _processRequest(req, signature); - } -} - -contract ExtensionSignatureMintERC721 is DSTest, Test { - MySigMint721 internal ext; - - uint256 public privateKey = 1234; - address public signer; - - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - MySigMint721.MintRequest _mintrequest; - bytes _signature; - - function setUp() public { - ext = new MySigMint721(); - - signer = vm.addr(privateKey); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,string uri,uint256 quantity,uint256 pricePerToken,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("SignatureMintERC721")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(ext))); - - _mintrequest.to = address(1); - _mintrequest.royaltyRecipient = address(2); - _mintrequest.royaltyBps = 0; - _mintrequest.primarySaleRecipient = address(2); - _mintrequest.uri = "ipfs://"; - _mintrequest.quantity = 1; - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(0x111); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - MySigMint721.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - keccak256(bytes(_request.uri)), - _request.quantity, - _request.pricePerToken, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - function test_state_mintWithSignature() public { - vm.warp(1000); - ext.setCondition(true); - vm.prank(signer); - address recoveredSigner = ext.mintWithSignature(_mintrequest, _signature); - - assertEq(signer, recoveredSigner); - } - - function test_revert_mintWithSignature_NotAuthorized() public { - vm.expectRevert("not authorized"); - ext.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_InvalidReq() public { - vm.warp(1000); - ext.setCondition(true); - - vm.prank(signer); - ext.mintWithSignature(_mintrequest, _signature); - - vm.expectRevert(abi.encodeWithSelector(SignatureMintERC721.SignatureMintInvalidSigner.selector)); - ext.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_RequestExpired() public { - vm.warp(3000); - ext.setCondition(true); - - vm.prank(signer); - vm.expectRevert( - abi.encodeWithSelector( - SignatureMintERC721.SignatureMintInvalidTime.selector, - _mintrequest.validityStartTimestamp, - _mintrequest.validityEndTimestamp, - block.timestamp - ) - ); - ext.mintWithSignature(_mintrequest, _signature); - } -} diff --git a/src/test/sdk/extension/StakingExtension.t.sol b/src/test/sdk/extension/StakingExtension.t.sol deleted file mode 100644 index 2805ba210..000000000 --- a/src/test/sdk/extension/StakingExtension.t.sol +++ /dev/null @@ -1,499 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Staking721 } from "contracts/extension/Staking721.sol"; -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "contracts/eip/interface/IERC721.sol"; -import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; - -import { MockERC721 } from "../../mocks/MockERC721.sol"; - -contract MyStakingContract is ERC20, Staking721, IERC721Receiver { - bool condition; - - constructor( - string memory _name, - string memory _symbol, - address _nftCollection, - uint256 _timeUnit, - uint256 _rewardsPerUnitTime - ) ERC20(_name, _symbol) Staking721(_nftCollection) { - condition = true; - _setStakingCondition(_timeUnit, _rewardsPerUnitTime); - } - - /// @notice View total rewards available in the staking contract. - function getRewardTokenBalance() external view override returns (uint256) {} - - /*/////////////////////////////////////////////////////////////// - ERC 165 / 721 logic - //////////////////////////////////////////////////////////////*/ - - function onERC721Received(address, address, uint256, bytes calldata) external view override returns (bytes4) { - require(isStaking == 2, "Direct transfer"); - return this.onERC721Received.selector; - } - - function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { - return interfaceId == type(IERC721Receiver).interfaceId; - } - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSetStakeConditions() internal view override returns (bool) { - return condition; - } - - function _mintRewards(address _staker, uint256 _rewards) internal override { - _mint(_staker, _rewards); - } -} - -contract StakingExtensionTest is DSTest, Test { - MyStakingContract internal ext; - MockERC721 public erc721; - - uint256 timeUnit; - uint256 rewardsPerUnitTime; - - address deployer; - address stakerOne; - address stakerTwo; - - function setUp() public { - erc721 = new MockERC721(); - timeUnit = 1 hours; - rewardsPerUnitTime = 100; - - deployer = address(0x123); - stakerOne = address(0x345); - stakerTwo = address(0x567); - - erc721.mint(stakerOne, 5); // mint token id 0 to 4 - erc721.mint(stakerTwo, 5); // mint token id 5 to 9 - - vm.prank(deployer); - ext = new MyStakingContract("Test Staking Contract", "TSC", address(erc721), timeUnit, rewardsPerUnitTime); - - // set approvals - vm.prank(stakerOne); - erc721.setApprovalForAll(address(ext), true); - - vm.prank(stakerTwo); - erc721.setApprovalForAll(address(ext), true); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: Stake - //////////////////////////////////////////////////////////////*/ - - function test_state_stake() public { - //================ first staker ====================== - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - ext.stake(_tokenIdsOne); - uint256 timeOfLastUpdate_one = block.timestamp; - - // check balances/ownership of staked tokens - for (uint256 i = 0; i < _tokenIdsOne.length; i++) { - assertEq(erc721.ownerOf(_tokenIdsOne[i]), address(ext)); - assertEq(ext.stakerAddress(_tokenIdsOne[i]), stakerOne); - } - assertEq(erc721.balanceOf(stakerOne), 2); - assertEq(erc721.balanceOf(address(ext)), _tokenIdsOne.length); - - // check available rewards right after staking - (uint256[] memory _amountStaked, uint256 _availableRewards) = ext.getStakeInfo(stakerOne); - - assertEq(_amountStaked.length, _tokenIdsOne.length); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(100); - vm.warp(1000); - - // check available rewards after warp - (, _availableRewards) = ext.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * _tokenIdsOne.length) * rewardsPerUnitTime) / timeUnit) - ); - - //================ second staker ====================== - vm.roll(200); - vm.warp(2000); - uint256[] memory _tokenIdsTwo = new uint256[](2); - _tokenIdsTwo[0] = 5; - _tokenIdsTwo[1] = 6; - - // stake 2 tokens - vm.prank(stakerTwo); - ext.stake(_tokenIdsTwo); - uint256 timeOfLastUpdate_two = block.timestamp; - - // check balances/ownership of staked tokens - for (uint256 i = 0; i < _tokenIdsTwo.length; i++) { - assertEq(erc721.ownerOf(_tokenIdsTwo[i]), address(ext)); - assertEq(ext.stakerAddress(_tokenIdsTwo[i]), stakerTwo); - } - assertEq(erc721.balanceOf(stakerTwo), 3); - assertEq(erc721.balanceOf(address(ext)), _tokenIdsTwo.length + _tokenIdsOne.length); - - // check available rewards right after staking - (_amountStaked, _availableRewards) = ext.getStakeInfo(stakerTwo); - - assertEq(_amountStaked.length, _tokenIdsTwo.length); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(300); - vm.warp(3000); - - // check available rewards for stakerOne - (, _availableRewards) = ext.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * _tokenIdsOne.length) * rewardsPerUnitTime) / timeUnit) - ); - - // check available rewards for stakerTwo - (, _availableRewards) = ext.getStakeInfo(stakerTwo); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_two) * _tokenIdsTwo.length) * rewardsPerUnitTime) / timeUnit) - ); - } - - function test_revert_stake_stakingZeroTokens() public { - // stake 0 tokens - uint256[] memory _tokenIds; - - vm.prank(stakerOne); - vm.expectRevert("Staking 0 tokens"); - ext.stake(_tokenIds); - } - - function test_revert_stake_notStaker() public { - // stake unowned tokens - uint256[] memory _tokenIds = new uint256[](1); - _tokenIds[0] = 6; - - vm.prank(stakerOne); - vm.expectRevert("ERC721: transfer from incorrect owner"); - ext.stake(_tokenIds); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: claimRewards - //////////////////////////////////////////////////////////////*/ - - function test_state_claimRewards() public { - //================ first staker ====================== - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - ext.stake(_tokenIdsOne); - uint256 timeOfLastUpdate_one = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - - vm.prank(stakerOne); - ext.claimRewards(); - - // check reward balances - assertEq( - ext.balanceOf(stakerOne), - ((((block.timestamp - timeOfLastUpdate_one) * _tokenIdsOne.length) * rewardsPerUnitTime) / timeUnit) - ); - - // check available rewards after claiming - (uint256[] memory _amountStaked, uint256 _availableRewards) = ext.getStakeInfo(stakerOne); - - assertEq(_amountStaked.length, _tokenIdsOne.length); - assertEq(_availableRewards, 0); - } - - function test_revert_claimRewards_noRewards() public { - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - ext.stake(_tokenIdsOne); - - //=================== try to claim rewards in same block - - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - ext.claimRewards(); - - //======= withdraw tokens and claim rewards - vm.roll(100); - vm.warp(1000); - - vm.prank(stakerOne); - ext.withdraw(_tokenIdsOne); - vm.prank(stakerOne); - ext.claimRewards(); - - //===== try to claim rewards again - vm.roll(200); - vm.warp(2000); - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - ext.claimRewards(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - //////////////////////////////////////////////////////////////*/ - - function test_state_setRewardsPerUnitTime() public { - // check current value - assertEq(rewardsPerUnitTime, ext.getRewardsPerUnitTime()); - - // set new value and check - uint256 newRewardsPerUnitTime = 50; - ext.setRewardsPerUnitTime(newRewardsPerUnitTime); - assertEq(newRewardsPerUnitTime, ext.getRewardsPerUnitTime()); - - //================ stake tokens - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - ext.stake(_tokenIdsOne); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - ext.setRewardsPerUnitTime(200); - assertEq(200, ext.getRewardsPerUnitTime()); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = ext.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * _tokenIdsOne.length) * newRewardsPerUnitTime) / timeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = ext.getStakeInfo(stakerOne); - - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * _tokenIdsOne.length) * 200) / timeUnit) - ); - } - - function test_revert_setRewardsPerUnitTime_notAuthorized() public { - ext.setCondition(false); - - vm.expectRevert("Not authorized"); - ext.setRewardsPerUnitTime(1); - } - - function test_state_setTimeUnit() public { - // check current value - assertEq(timeUnit, ext.getTimeUnit()); - - // set new value and check - uint256 newTimeUnit = 1 minutes; - ext.setTimeUnit(newTimeUnit); - assertEq(newTimeUnit, ext.getTimeUnit()); - - //================ stake tokens - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - ext.stake(_tokenIdsOne); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - ext.setTimeUnit(1 seconds); - assertEq(1 seconds, ext.getTimeUnit()); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = ext.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * _tokenIdsOne.length) * rewardsPerUnitTime) / newTimeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = ext.getStakeInfo(stakerOne); - - assertEq( - _newRewards, - _availableRewards + - ((((block.timestamp - newTimeOfLastUpdate) * _tokenIdsOne.length) * rewardsPerUnitTime) / (1 seconds)) - ); - } - - function test_revert_setTimeUnit_notAuthorized() public { - ext.setCondition(false); - - vm.expectRevert("Not authorized"); - ext.setTimeUnit(1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: withdraw - //////////////////////////////////////////////////////////////*/ - - function test_state_withdraw() public { - //================ first staker ====================== - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - ext.stake(_tokenIdsOne); - uint256 timeOfLastUpdate = block.timestamp; - - // check balances/ownership of staked tokens - for (uint256 i = 0; i < _tokenIdsOne.length; i++) { - assertEq(erc721.ownerOf(_tokenIdsOne[i]), address(ext)); - assertEq(ext.stakerAddress(_tokenIdsOne[i]), stakerOne); - } - assertEq(erc721.balanceOf(stakerOne), 2); - assertEq(erc721.balanceOf(address(ext)), _tokenIdsOne.length); - - // check available rewards right after staking - (uint256[] memory _amountStaked, uint256 _availableRewards) = ext.getStakeInfo(stakerOne); - - assertEq(_amountStaked.length, _tokenIdsOne.length); - assertEq(_availableRewards, 0); - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - uint256[] memory _tokensToWithdraw = new uint256[](2); - _tokensToWithdraw[0] = 2; - _tokensToWithdraw[1] = 0; - - vm.prank(stakerOne); - ext.withdraw(_tokensToWithdraw); - - // check balances/ownership after withdraw - for (uint256 i = 0; i < _tokensToWithdraw.length; i++) { - assertEq(erc721.ownerOf(_tokensToWithdraw[i]), stakerOne); - assertEq(ext.stakerAddress(_tokensToWithdraw[i]), address(0)); - } - assertEq(erc721.balanceOf(stakerOne), 4); - assertEq(erc721.balanceOf(address(ext)), 1); - - // check available rewards after withdraw - (, _availableRewards) = ext.getStakeInfo(stakerOne); - assertEq(_availableRewards, ((((block.timestamp - timeOfLastUpdate) * 3) * rewardsPerUnitTime) / timeUnit)); - - uint256 timeOfLastUpdateLatest = block.timestamp; - - // check available rewards some time after withdraw - vm.roll(200); - vm.warp(2000); - - (, _availableRewards) = ext.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - (((((timeOfLastUpdateLatest - timeOfLastUpdate) * 3)) * rewardsPerUnitTime) / timeUnit) + - (((((block.timestamp - timeOfLastUpdateLatest) * 1)) * rewardsPerUnitTime) / timeUnit) - ); - } - - function test_revert_withdraw_withdrawingZeroTokens() public { - uint256[] memory _tokensToWithdraw; - - vm.expectRevert("Withdrawing 0 tokens"); - ext.withdraw(_tokensToWithdraw); - } - - function test_revert_withdraw_notStaker() public { - // stake tokens - uint256[] memory _tokenIds = new uint256[](2); - _tokenIds[0] = 0; - _tokenIds[1] = 1; - - vm.prank(stakerOne); - ext.stake(_tokenIds); - - // trying to withdraw zero tokens - uint256[] memory _tokensToWithdraw = new uint256[](1); - _tokensToWithdraw[0] = 2; - - vm.prank(stakerOne); - vm.expectRevert("Not staker"); - ext.withdraw(_tokensToWithdraw); - } - - function test_revert_withdraw_withdrawingMoreThanStaked() public { - // stake tokens - uint256[] memory _tokenIds = new uint256[](1); - _tokenIds[0] = 0; - - vm.prank(stakerOne); - ext.stake(_tokenIds); - - // trying to withdraw tokens not staked by caller - uint256[] memory _tokensToWithdraw = new uint256[](2); - _tokensToWithdraw[0] = 0; - _tokensToWithdraw[1] = 1; - - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - ext.withdraw(_tokensToWithdraw); - } -} diff --git a/src/test/sdk/extension/TokenBundle.t.sol b/src/test/sdk/extension/TokenBundle.t.sol deleted file mode 100644 index a357f1b35..000000000 --- a/src/test/sdk/extension/TokenBundle.t.sol +++ /dev/null @@ -1,322 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "../../mocks/WETH9.sol"; -import "../../mocks/MockERC20.sol"; -import "../../mocks/MockERC721.sol"; -import "../../mocks/MockERC1155.sol"; - -import { TokenBundle, ITokenBundle } from "contracts/extension/TokenBundle.sol"; - -contract MyTokenBundle is TokenBundle { - function createBundle(Token[] calldata _tokensToBind, uint256 _bundleId) external { - _createBundle(_tokensToBind, _bundleId); - } - - function updateBundle(Token[] calldata _tokensToBind, uint256 _bundleId) external { - _updateBundle(_tokensToBind, _bundleId); - } - - function addTokenInBundle(Token memory _tokenToBind, uint256 _bundleId) external { - _addTokenInBundle(_tokenToBind, _bundleId); - } - - function updateTokenInBundle(Token memory _tokenToBind, uint256 _bundleId, uint256 _index) external { - _updateTokenInBundle(_tokenToBind, _bundleId, _index); - } - - function setUriOfBundle(string calldata _uri, uint256 _bundleId) external { - _setUriOfBundle(_uri, _bundleId); - } - - function deleteBundle(uint256 _bundleId) external { - _deleteBundle(_bundleId); - } -} - -contract ExtensionTokenBundle is DSTest, Test { - MyTokenBundle internal ext; - - MockERC20 public erc20; - MockERC721 public erc721; - MockERC1155 public erc1155; - WETH9 public weth; - - ITokenBundle.Token[] internal bundleContent; - - function setUp() public { - ext = new MyTokenBundle(); - - erc20 = new MockERC20(); - erc721 = new MockERC721(); - erc1155 = new MockERC1155(); - weth = new WETH9(); - - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }) - ); - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 0, - totalAmount: 1 - }) - ); - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 0, - totalAmount: 100 - }) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `createBundle` - //////////////////////////////////////////////////////////////*/ - - function test_state_createBundle() public { - ext.createBundle(bundleContent, 0); - - uint256 tokenCountOfBundle = ext.getTokenCountOfBundle(0); - assertEq(bundleContent.length, tokenCountOfBundle); - - for (uint256 i = 0; i < tokenCountOfBundle; i += 1) { - ITokenBundle.Token memory tokenOfBundle = ext.getTokenOfBundle(0, i); - assertEq(bundleContent[i].assetContract, tokenOfBundle.assetContract); - assertEq(uint256(bundleContent[i].tokenType), uint256(tokenOfBundle.tokenType)); - assertEq(bundleContent[i].tokenId, tokenOfBundle.tokenId); - assertEq(bundleContent[i].totalAmount, tokenOfBundle.totalAmount); - } - } - - function test_revert_createBundle_emptyBundle() public { - ITokenBundle.Token[] memory emptyBundle; - - vm.expectRevert("!Tokens"); - ext.createBundle(emptyBundle, 0); - } - - function test_revert_createBundle_existingBundleId() public { - ext.createBundle(bundleContent, 0); - - vm.expectRevert("id exists"); - ext.createBundle(bundleContent, 0); - } - - function test_revert_createBundle_tokenTypeMismatch() public { - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 0, - totalAmount: 0 - }) - ); - - vm.expectRevert("!TokenType"); - ext.createBundle(bundleContent, 0); - - bundleContent.pop(); - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 0, - totalAmount: 0 - }) - ); - - vm.expectRevert("!TokenType"); - ext.createBundle(bundleContent, 0); - - bundleContent.pop(); - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 0 - }) - ); - - vm.expectRevert("!TokenType"); - ext.createBundle(bundleContent, 0); - - bundleContent.pop(); - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 0, - totalAmount: 0 - }) - ); - - vm.expectRevert("!TokenType"); - ext.createBundle(bundleContent, 0); - - bundleContent.pop(); - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 0 - }) - ); - - vm.expectRevert("!TokenType"); - ext.createBundle(bundleContent, 0); - - bundleContent.pop(); - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 0, - totalAmount: 0 - }) - ); - - vm.expectRevert("!TokenType"); - ext.createBundle(bundleContent, 0); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `updateBundle` - //////////////////////////////////////////////////////////////*/ - - function test_state_updateBundle() public { - ext.createBundle(bundleContent, 0); - - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 1, - totalAmount: 200 - }) - ); - - ext.updateBundle(bundleContent, 0); - - uint256 tokenCountOfBundle = ext.getTokenCountOfBundle(0); - assertEq(bundleContent.length, tokenCountOfBundle); - - for (uint256 i = 0; i < tokenCountOfBundle; i += 1) { - ITokenBundle.Token memory tokenOfBundle = ext.getTokenOfBundle(0, i); - assertEq(bundleContent[i].assetContract, tokenOfBundle.assetContract); - assertEq(uint256(bundleContent[i].tokenType), uint256(tokenOfBundle.tokenType)); - assertEq(bundleContent[i].tokenId, tokenOfBundle.tokenId); - assertEq(bundleContent[i].totalAmount, tokenOfBundle.totalAmount); - } - - bundleContent.pop(); - bundleContent.pop(); - ext.updateBundle(bundleContent, 0); - - tokenCountOfBundle = ext.getTokenCountOfBundle(0); - assertEq(bundleContent.length, tokenCountOfBundle); - - for (uint256 i = 0; i < tokenCountOfBundle; i += 1) { - ITokenBundle.Token memory tokenOfBundle = ext.getTokenOfBundle(0, i); - assertEq(bundleContent[i].assetContract, tokenOfBundle.assetContract); - assertEq(uint256(bundleContent[i].tokenType), uint256(tokenOfBundle.tokenType)); - assertEq(bundleContent[i].tokenId, tokenOfBundle.tokenId); - assertEq(bundleContent[i].totalAmount, tokenOfBundle.totalAmount); - } - } - - function test_revert_updateBundle_emptyBundle() public { - ext.createBundle(bundleContent, 0); - - ITokenBundle.Token[] memory emptyBundle; - vm.expectRevert("!Tokens"); - ext.updateBundle(emptyBundle, 0); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `addTokenInBundle` - //////////////////////////////////////////////////////////////*/ - - function test_state_addTokenInBundle() public { - ext.createBundle(bundleContent, 0); - - ITokenBundle.Token memory newToken = ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 1, - totalAmount: 200 - }); - - ext.addTokenInBundle(newToken, 0); - - uint256 tokenCountOfBundle = ext.getTokenCountOfBundle(0); - assertEq(bundleContent.length + 1, tokenCountOfBundle); - - for (uint256 i = 0; i < tokenCountOfBundle - 1; i += 1) { - ITokenBundle.Token memory tokenOfBundle_ = ext.getTokenOfBundle(0, i); - assertEq(bundleContent[i].assetContract, tokenOfBundle_.assetContract); - assertEq(uint256(bundleContent[i].tokenType), uint256(tokenOfBundle_.tokenType)); - assertEq(bundleContent[i].tokenId, tokenOfBundle_.tokenId); - assertEq(bundleContent[i].totalAmount, tokenOfBundle_.totalAmount); - } - - ITokenBundle.Token memory tokenOfBundle = ext.getTokenOfBundle(0, tokenCountOfBundle - 1); - assertEq(newToken.assetContract, tokenOfBundle.assetContract); - assertEq(uint256(newToken.tokenType), uint256(tokenOfBundle.tokenType)); - assertEq(newToken.tokenId, tokenOfBundle.tokenId); - assertEq(newToken.totalAmount, tokenOfBundle.totalAmount); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `updateTokenInBundle` - //////////////////////////////////////////////////////////////*/ - - function test_state_updateTokenInBundle() public { - ext.createBundle(bundleContent, 0); - - ITokenBundle.Token memory newToken = ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 1, - totalAmount: 200 - }); - - ext.updateTokenInBundle(newToken, 0, 1); - - uint256 tokenCountOfBundle = ext.getTokenCountOfBundle(0); - assertEq(bundleContent.length, tokenCountOfBundle); - - ITokenBundle.Token memory tokenOfBundle = ext.getTokenOfBundle(0, 1); - assertEq(newToken.assetContract, tokenOfBundle.assetContract); - assertEq(uint256(newToken.tokenType), uint256(tokenOfBundle.tokenType)); - assertEq(newToken.tokenId, tokenOfBundle.tokenId); - assertEq(newToken.totalAmount, tokenOfBundle.totalAmount); - } - - function test_revert_updateTokenInBundle_indexDNE() public { - ext.createBundle(bundleContent, 0); - - ITokenBundle.Token memory newToken = ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 1, - totalAmount: 200 - }); - - vm.expectRevert("index DNE"); - ext.updateTokenInBundle(newToken, 0, 3); - } -} diff --git a/src/test/sdk/extension/TokenStore.t.sol b/src/test/sdk/extension/TokenStore.t.sol deleted file mode 100644 index c60531719..000000000 --- a/src/test/sdk/extension/TokenStore.t.sol +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import "../../mocks/WETH9.sol"; -import "../../mocks/MockERC20.sol"; -import "../../mocks/MockERC721.sol"; -import "../../mocks/MockERC1155.sol"; -import "../../utils/Wallet.sol"; - -import { TokenStore, TokenBundle, ITokenBundle, CurrencyTransferLib } from "contracts/extension/TokenStore.sol"; - -contract MyTokenStore is TokenStore { - constructor(address _nativeTokenWrapper) TokenStore(_nativeTokenWrapper) {} - - receive() external payable {} - - function storeTokens( - address _tokenOwner, - Token[] calldata _tokens, - string calldata _uriForTokens, - uint256 _idForTokens - ) external { - _storeTokens(_tokenOwner, _tokens, _uriForTokens, _idForTokens); - } - - function releaseTokens(address _recipient, uint256 _idForContent) external { - _releaseTokens(_recipient, _idForContent); - } -} - -contract ExtensionTokenStore is DSTest, Test { - MyTokenStore internal ext; - - MockERC20 public erc20; - MockERC721 public erc721; - MockERC1155 public erc1155; - WETH9 public weth; - - ITokenBundle.Token[] internal bundleContent; - - Wallet internal tokenOwner; - - function setUp() public { - ext = new MyTokenStore(CurrencyTransferLib.NATIVE_TOKEN); - - erc20 = new MockERC20(); - erc721 = new MockERC721(); - erc1155 = new MockERC1155(); - weth = new WETH9(); - - tokenOwner = new Wallet(); - - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc20), - tokenType: ITokenBundle.TokenType.ERC20, - tokenId: 0, - totalAmount: 10 ether - }) - ); - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc721), - tokenType: ITokenBundle.TokenType.ERC721, - tokenId: 0, - totalAmount: 1 - }) - ); - bundleContent.push( - ITokenBundle.Token({ - assetContract: address(erc1155), - tokenType: ITokenBundle.TokenType.ERC1155, - tokenId: 0, - totalAmount: 100 - }) - ); - - erc20.mint(address(tokenOwner), 10 ether); - erc721.mint(address(tokenOwner), 1); - erc1155.mint(address(tokenOwner), 0, 100); - - tokenOwner.setAllowanceERC20(address(erc20), address(ext), type(uint256).max); - tokenOwner.setApprovalForAllERC721(address(erc721), address(ext), true); - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(ext), true); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `storeTokens` - //////////////////////////////////////////////////////////////*/ - - function test_balances_storeTokens() public { - assertEq(erc20.balanceOf(address(tokenOwner)), 10 ether); - assertEq(erc20.balanceOf(address(ext)), 0); - - assertEq(erc721.ownerOf(0), address(tokenOwner)); - - assertEq(erc1155.balanceOf(address(tokenOwner), 0), 100); - assertEq(erc1155.balanceOf(address(ext), 0), 0); - - vm.prank(address(tokenOwner)); - ext.storeTokens(address(tokenOwner), bundleContent, "", 0); - - assertEq(erc20.balanceOf(address(tokenOwner)), 0); - assertEq(erc20.balanceOf(address(ext)), 10 ether); - - assertEq(erc721.ownerOf(0), address(ext)); - - assertEq(erc1155.balanceOf(address(tokenOwner), 0), 0); - assertEq(erc1155.balanceOf(address(ext), 0), 100); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `releaseTokens` - //////////////////////////////////////////////////////////////*/ - - function test_balances_releaseTokens() public { - vm.prank(address(tokenOwner)); - ext.storeTokens(address(tokenOwner), bundleContent, "", 0); - - assertEq(erc20.balanceOf(address(tokenOwner)), 0); - assertEq(erc20.balanceOf(address(ext)), 10 ether); - - assertEq(erc721.ownerOf(0), address(ext)); - - assertEq(erc1155.balanceOf(address(tokenOwner), 0), 0); - assertEq(erc1155.balanceOf(address(ext), 0), 100); - - ext.releaseTokens(address(0x345), 0); - - assertEq(erc20.balanceOf(address(0x345)), 10 ether); - assertEq(erc20.balanceOf(address(ext)), 0); - - assertEq(erc721.ownerOf(0), address(0x345)); - - assertEq(erc1155.balanceOf(address(0x345), 0), 100); - assertEq(erc1155.balanceOf(address(ext), 0), 0); - } -} diff --git a/src/test/sdk/extension/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.t.sol b/src/test/sdk/extension/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.t.sol deleted file mode 100644 index 63dd48e7f..000000000 --- a/src/test/sdk/extension/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.t.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/BatchMintMetadata.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyBatchMintMetadata is BatchMintMetadata { - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function getBaseURI(uint256 _tokenId) public view returns (string memory) { - return _getBaseURI(_tokenId); - } -} - -contract BatchMintMetadata_BatchMintMetadata is ExtensionUtilTest { - MyBatchMintMetadata internal ext; - uint256 internal startId; - uint256 internal amountToMint; - string internal baseURI; - - function setUp() public override { - super.setUp(); - - ext = new MyBatchMintMetadata(); - startId = 0; - amountToMint = 100; - baseURI = "ipfs://baseURI"; - } - - function test_batchMintMetadata() public { - uint256 prevBaseURICount = ext.getBaseURICount(); - uint256 batchId = startId + amountToMint; - - ext.batchMintMetadata(startId, amountToMint, baseURI); - uint256 newBaseURICount = ext.getBaseURICount(); - assertEq(ext.getBaseURI(amountToMint - 1), baseURI); - assertEq(newBaseURICount, prevBaseURICount + 1); - assertEq(ext.getBatchIdAtIndex(newBaseURICount - 1), batchId); - - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidBatchId.selector, newBaseURICount)); - ext.getBatchIdAtIndex(newBaseURICount); - } -} diff --git a/src/test/sdk/extension/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.tree b/src/test/sdk/extension/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.tree deleted file mode 100644 index 572dd5203..000000000 --- a/src/test/sdk/extension/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.tree +++ /dev/null @@ -1,7 +0,0 @@ -_batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens -) -├── it should store batch id equal to the sum of `_startId` and `_amountToMint` in batchIds array ✅ -├── it should map the new batch id to `_baseURIForTokens` ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.t.sol b/src/test/sdk/extension/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.t.sol deleted file mode 100644 index 147820874..000000000 --- a/src/test/sdk/extension/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.t.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/BatchMintMetadata.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyBatchMintMetadata is BatchMintMetadata { - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function freezeBaseURI(uint256 _batchId) external { - _freezeBaseURI(_batchId); - } -} - -contract BatchMintMetadata_FreezeBaseURI is ExtensionUtilTest { - MyBatchMintMetadata internal ext; - uint256 internal startId; - uint256[] internal batchIds; - uint256 internal indexToFreeze; - - event MetadataFrozen(); - - function setUp() public override { - super.setUp(); - - ext = new MyBatchMintMetadata(); - - startId = 0; - // mint 5 batches - for (uint256 i = 0; i < 5; i++) { - uint256 amount = (i + 1) * 10; - uint256 batchId = startId + amount; - batchIds.push(batchId); - - (startId, ) = ext.batchMintMetadata(startId, amount, "ipfs://"); - assertEq(ext.batchFrozen(batchId), false); - } - - indexToFreeze = 3; - } - - function test_freezeBaseURI_invalidBatch() public { - vm.expectRevert( - abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidBatchId.selector, batchIds[indexToFreeze] * 10) - ); - ext.freezeBaseURI(batchIds[indexToFreeze] * 10); // non-existent batchId - } - - modifier whenBatchIdValid() { - _; - } - - function test_freezeBaseURI() public whenBatchIdValid { - ext.freezeBaseURI(batchIds[indexToFreeze]); - - assertEq(ext.batchFrozen(batchIds[indexToFreeze]), true); - } - - function test_freezeBaseURI_event() public whenBatchIdValid { - vm.expectEmit(false, false, false, false); - emit MetadataFrozen(); - ext.freezeBaseURI(batchIds[indexToFreeze]); - } -} diff --git a/src/test/sdk/extension/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.tree b/src/test/sdk/extension/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.tree deleted file mode 100644 index 4dd87edef..000000000 --- a/src/test/sdk/extension/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.tree +++ /dev/null @@ -1,6 +0,0 @@ -_freezeBaseURI(uint256 _batchId) -├── when there is no baseURI for given `_batchId` - │ └── it should revert ✅ - └── when there is a baseURI present for given `_batchId` - └── it should freeze the `batchId` by setting `frozen[_batchId]` to `true` ✅ - └── it should emit MetadataFrozen event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/batch-mint-metadata/get-base-uri/_getBaseURI.t.sol b/src/test/sdk/extension/batch-mint-metadata/get-base-uri/_getBaseURI.t.sol deleted file mode 100644 index afa7aa6de..000000000 --- a/src/test/sdk/extension/batch-mint-metadata/get-base-uri/_getBaseURI.t.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/BatchMintMetadata.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyBatchMintMetadata is BatchMintMetadata { - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function getBaseURI(uint256 _tokenId) external view returns (string memory) { - return _getBaseURI(_tokenId); - } -} - -contract BatchMintMetadata_GetBaseURI is ExtensionUtilTest { - MyBatchMintMetadata internal ext; - uint256 internal startId; - uint256[] internal batchIds; - - function setUp() public override { - super.setUp(); - - ext = new MyBatchMintMetadata(); - - startId = 0; - // mint 5 batches - for (uint256 i = 0; i < 5; i++) { - uint256 amount = (i + 1) * 10; - uint256 batchId = startId + amount; - batchIds.push(batchId); - - string memory baseURI = Strings.toString(batchId); - (startId, ) = ext.batchMintMetadata(startId, amount, baseURI); - } - } - - function test_getBaseURI_invalidTokenId() public { - uint256 tokenId = batchIds[4]; // tokenId greater than the last batchId - - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidTokenId.selector, tokenId)); - ext.getBaseURI(tokenId); - } - - modifier whenValidTokenId() { - _; - } - - function test_getBaseURI() public whenValidTokenId { - for (uint256 i = 0; i < 5; i++) { - uint256 start = i == 0 ? 0 : batchIds[i - 1]; - for (uint256 j = start; j < batchIds[i]; j++) { - string memory _baseURI = ext.getBaseURI(j); - - assertEq(_baseURI, Strings.toString(batchIds[i])); - } - } - } -} diff --git a/src/test/sdk/extension/batch-mint-metadata/get-base-uri/_getBaseURI.tree b/src/test/sdk/extension/batch-mint-metadata/get-base-uri/_getBaseURI.tree deleted file mode 100644 index c4ee674bf..000000000 --- a/src/test/sdk/extension/batch-mint-metadata/get-base-uri/_getBaseURI.tree +++ /dev/null @@ -1,6 +0,0 @@ -_getBaseURI(uint256 _tokenId) -├── when `_tokenId` doesn't belong to any batch, i.e. greater than the last batchId - │ └── it should revert ✅ - └── when `_tokenId` belongs to some batch, i.e. less than that batchId - └── it should return correct baseURI for the `_tokenId` ✅ -(note: all batches are assumed to be contiguous, i.e. start id of one batch is the end id of the previous batch) \ No newline at end of file diff --git a/src/test/sdk/extension/batch-mint-metadata/get-batch-id/_getBatchId.t.sol b/src/test/sdk/extension/batch-mint-metadata/get-batch-id/_getBatchId.t.sol deleted file mode 100644 index a4f16687e..000000000 --- a/src/test/sdk/extension/batch-mint-metadata/get-batch-id/_getBatchId.t.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/BatchMintMetadata.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyBatchMintMetadata is BatchMintMetadata { - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function getBatchId(uint256 _tokenId) external view returns (uint256 batchId, uint256 index) { - return _getBatchId(_tokenId); - } -} - -contract BatchMintMetadata_GetBatchId is ExtensionUtilTest { - MyBatchMintMetadata internal ext; - uint256 internal startId; - uint256[] internal batchIds; - - function setUp() public override { - super.setUp(); - - ext = new MyBatchMintMetadata(); - - startId = 0; - // mint 5 batches - for (uint256 i = 0; i < 5; i++) { - uint256 amount = (i + 1) * 10; - batchIds.push(startId + amount); - (startId, ) = ext.batchMintMetadata(startId, amount, "ipfs://"); - } - } - - function test_getBatchId_invalidTokenId() public { - uint256 tokenId = batchIds[4]; // tokenId greater than the last batchId - - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidTokenId.selector, tokenId)); - ext.getBatchId(tokenId); - } - - modifier whenValidTokenId() { - _; - } - - function test_getBatchId() public whenValidTokenId { - for (uint256 i = 0; i < 5; i++) { - uint256 start = i == 0 ? 0 : batchIds[i - 1]; - for (uint256 j = start; j < batchIds[i]; j++) { - (uint256 batchId, uint256 index) = ext.getBatchId(j); - - assertEq(batchId, batchIds[i]); - assertEq(index, i); - } - } - } -} diff --git a/src/test/sdk/extension/batch-mint-metadata/get-batch-id/_getBatchId.tree b/src/test/sdk/extension/batch-mint-metadata/get-batch-id/_getBatchId.tree deleted file mode 100644 index 2e6dd366e..000000000 --- a/src/test/sdk/extension/batch-mint-metadata/get-batch-id/_getBatchId.tree +++ /dev/null @@ -1,6 +0,0 @@ -_getBatchId(uint256 _tokenId) -├── when `_tokenId` doesn't belong to any batch, i.e. greater than the last batchId - │ └── it should revert ✅ - └── when `_tokenId` belongs to some batch, i.e. less than that batchId - └── it should return correct batchId and batch index for the `_tokenId` ✅ -(note: all batches are assumed to be contiguous, i.e. start id of one batch is the end id of the previous batch) \ No newline at end of file diff --git a/src/test/sdk/extension/batch-mint-metadata/get-batch-start-id/_getBatchStartId.t.sol b/src/test/sdk/extension/batch-mint-metadata/get-batch-start-id/_getBatchStartId.t.sol deleted file mode 100644 index 18d1fc954..000000000 --- a/src/test/sdk/extension/batch-mint-metadata/get-batch-start-id/_getBatchStartId.t.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/BatchMintMetadata.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyBatchMintMetadata is BatchMintMetadata { - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function getBatchStartId(uint256 _batchId) external view returns (uint256) { - return _getBatchStartId(_batchId); - } -} - -contract BatchMintMetadata_GetBatchStartId is ExtensionUtilTest { - MyBatchMintMetadata internal ext; - uint256 internal startId; - uint256[] internal batchIds; - - function setUp() public override { - super.setUp(); - - ext = new MyBatchMintMetadata(); - - startId = 0; - // mint 5 batches - for (uint256 i = 0; i < 5; i++) { - uint256 amount = (i + 1) * 10; - batchIds.push(startId + amount); - (startId, ) = ext.batchMintMetadata(startId, amount, "ipfs://"); - } - } - - function test_getBatchStartId_invalidBatchId() public { - uint256 batchId = batchIds[4] + 1; // non-existent batchId - - vm.expectRevert(abi.encodeWithSelector(BatchMintMetadata.BatchMintInvalidBatchId.selector, batchId)); - ext.getBatchStartId(batchId); - } - - modifier whenValidBatchId() { - _; - } - - function test_getBatchStartId() public whenValidBatchId { - for (uint256 i = 0; i < 5; i++) { - uint256 start = i == 0 ? 0 : batchIds[i - 1]; - uint256 _batchStartId = ext.getBatchStartId(batchIds[i]); - - assertEq(start, _batchStartId); - } - } -} diff --git a/src/test/sdk/extension/batch-mint-metadata/get-batch-start-id/_getBatchStartId.tree b/src/test/sdk/extension/batch-mint-metadata/get-batch-start-id/_getBatchStartId.tree deleted file mode 100644 index 7e303ab46..000000000 --- a/src/test/sdk/extension/batch-mint-metadata/get-batch-start-id/_getBatchStartId.tree +++ /dev/null @@ -1,6 +0,0 @@ -_getBatchStartId(uint256 _batchID) -├── when `_batchID` doesn't exist - │ └── it should revert ✅ - └── when `_batchID` exists - └── it should return the starting tokenId for that batch ✅ -(note: all batches are assumed to be contiguous, i.e. start id of one batch is the end id of the previous batch) \ No newline at end of file diff --git a/src/test/sdk/extension/batch-mint-metadata/set-base-uri/_setBaseURI.t.sol b/src/test/sdk/extension/batch-mint-metadata/set-base-uri/_setBaseURI.t.sol deleted file mode 100644 index 1ec55e8a8..000000000 --- a/src/test/sdk/extension/batch-mint-metadata/set-base-uri/_setBaseURI.t.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/BatchMintMetadata.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyBatchMintMetadata is BatchMintMetadata { - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function setBaseURI(uint256 _batchId, string memory _baseURI) external { - _setBaseURI(_batchId, _baseURI); - } - - function freezeBaseURI(uint256 _batchId, bool _freeze) public { - batchFrozen[_batchId] = _freeze; - } - - function getBaseURI(uint256 _tokenId) external view returns (string memory) { - return _getBaseURI(_tokenId); - } -} - -contract BatchMintMetadata_SetBaseURI is ExtensionUtilTest { - MyBatchMintMetadata internal ext; - string internal newBaseURI; - uint256 internal startId; - uint256[] internal batchIds; - uint256 internal indexToUpdate; - - event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); - - function setUp() public override { - super.setUp(); - - ext = new MyBatchMintMetadata(); - - startId = 0; - // mint 5 batches - for (uint256 i = 0; i < 5; i++) { - uint256 amount = (i + 1) * 10; - uint256 batchId = startId + amount; - batchIds.push(batchId); - - (startId, ) = ext.batchMintMetadata(startId, amount, "ipfs://"); - ext.freezeBaseURI(batchId, true); - } - - indexToUpdate = 3; - newBaseURI = "ipfs://baseURI"; - } - - function test_setBaseURI_frozenBatchId() public { - vm.expectRevert( - abi.encodeWithSelector(BatchMintMetadata.BatchMintMetadataFrozen.selector, batchIds[indexToUpdate]) - ); - ext.setBaseURI(batchIds[indexToUpdate], newBaseURI); - } - - modifier whenBatchIdNotFrozen() { - ext.freezeBaseURI(batchIds[indexToUpdate], false); - _; - } - - function test_setBaseURI() public whenBatchIdNotFrozen { - ext.setBaseURI(batchIds[indexToUpdate], newBaseURI); - - string memory _baseURI = ext.getBaseURI(batchIds[indexToUpdate] - 1); - - assertEq(_baseURI, newBaseURI); - } - - function test_setBaseURI_event() public whenBatchIdNotFrozen { - vm.expectEmit(false, false, false, true); - emit BatchMetadataUpdate(batchIds[indexToUpdate - 1], batchIds[indexToUpdate]); - ext.setBaseURI(batchIds[indexToUpdate], newBaseURI); - } -} diff --git a/src/test/sdk/extension/batch-mint-metadata/set-base-uri/_setBaseURI.tree b/src/test/sdk/extension/batch-mint-metadata/set-base-uri/_setBaseURI.tree deleted file mode 100644 index 3df76f653..000000000 --- a/src/test/sdk/extension/batch-mint-metadata/set-base-uri/_setBaseURI.tree +++ /dev/null @@ -1,6 +0,0 @@ -_setBaseURI(uint256 _batchId, string memory _baseURI) -├── when the `_batchId` is frozen - │ └── it should revert ✅ - └── when the `_batchId` is not frozen - └── it should map the `_batchId` to `_baseURI` param ✅ - └── it should emit BatchMetadataUpdate event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.t.sol b/src/test/sdk/extension/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.t.sol deleted file mode 100644 index e01939b3b..000000000 --- a/src/test/sdk/extension/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.t.sol +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BurnToClaim, IBurnToClaim } from "contracts/extension/BurnToClaim.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyBurnToClaim is BurnToClaim { - function burnTokensOnOrigin(address _tokenOwner, uint256 _tokenId, uint256 _quantity) public { - _burnTokensOnOrigin(_tokenOwner, _tokenId, _quantity); - } - - function _canSetBurnToClaim() internal view override returns (bool) { - return true; - } -} - -contract BurnToClaim_BurnTokensOnOrigin is ExtensionUtilTest { - MyBurnToClaim internal ext; - Wallet internal tokenOwner; - uint256 internal tokenId; - uint256 internal quantity; - - function setUp() public override { - super.setUp(); - - ext = new MyBurnToClaim(); - - tokenOwner = getWallet(); - erc721.mint(address(tokenOwner), 10); - erc1155.mint(address(tokenOwner), 1, 10); - - erc721NonBurnable.mint(address(tokenOwner), 10); - erc1155NonBurnable.mint(address(tokenOwner), 1, 10); - - tokenOwner.setApprovalForAllERC721(address(erc721), address(ext), true); - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(ext), true); - } - - // ================== - // ======= Test branch: token type is ERC721 - // ================== - - modifier whenNotBurnableERC721() { - ext.setBurnToClaimInfo( - IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc721NonBurnable), - tokenType: IBurnToClaim.TokenType.ERC721, - tokenId: 0, - mintPriceForNewToken: 0, - currency: address(erc20) - }) - ); - _; - } - - function test_burnTokensOnOrigin_ERC721_nonBurnable() public whenNotBurnableERC721 { - vm.expectRevert(); - ext.burnTokensOnOrigin(address(tokenOwner), tokenId, quantity); - } - - modifier whenBurnableERC721() { - ext.setBurnToClaimInfo( - IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc721), - tokenType: IBurnToClaim.TokenType.ERC721, - tokenId: 0, - mintPriceForNewToken: 0, - currency: address(erc20) - }) - ); - _; - } - - function test_burnTokensOnOrigin_ERC721() public whenBurnableERC721 { - ext.burnTokensOnOrigin(address(tokenOwner), tokenId, quantity); - - assertEq(erc721.balanceOf(address(tokenOwner)), 9); - - vm.expectRevert(); - erc721.ownerOf(tokenId); // token doesn't exist after burning - } - - // ================== - // ======= Test branch: token type is ERC71155 - // ================== - - modifier whenNotBurnableERC1155() { - ext.setBurnToClaimInfo( - IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc1155NonBurnable), - tokenType: IBurnToClaim.TokenType.ERC1155, - tokenId: 1, - mintPriceForNewToken: 0, - currency: address(erc20) - }) - ); - _; - } - - function test_burnTokensOnOrigin_ERC1155_nonBurnable() public whenNotBurnableERC1155 { - vm.expectRevert(); - ext.burnTokensOnOrigin(address(tokenOwner), tokenId, quantity); - } - - modifier whenBurnableERC1155() { - ext.setBurnToClaimInfo( - IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc1155), - tokenType: IBurnToClaim.TokenType.ERC1155, - tokenId: 1, - mintPriceForNewToken: 0, - currency: address(erc20) - }) - ); - _; - } - - function test_burnTokensOnOrigin_ERC1155() public whenBurnableERC1155 { - ext.burnTokensOnOrigin(address(tokenOwner), tokenId, quantity); - - assertEq(erc1155.balanceOf(address(tokenOwner), tokenId), 0); - } -} diff --git a/src/test/sdk/extension/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.tree b/src/test/sdk/extension/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.tree deleted file mode 100644 index a2a3911ac..000000000 --- a/src/test/sdk/extension/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.tree +++ /dev/null @@ -1,15 +0,0 @@ -_burnTokensOnOrigin( - address _tokenOwner, - uint256 _tokenId, - uint256 _quantity -) -├── when burn-to-claim info has token type ERC721 - ├── when the origin ERC721 contract is not burnable - │ └── it should revert ✅ - └── when the origin ERC721 contract is burnable - └── it should successfully burn the token with given tokenId for the token owner ✅ -├── when burn-to-claim info has token type ERC1155 - ├── when the origin ERC1155 contract is not burnable - │ └── it should revert ✅ - └── when the origin ERC1155 contract is burnable - └── it should successfully burn tokens with given tokenId and quantity for the token owner ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.t.sol b/src/test/sdk/extension/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.t.sol deleted file mode 100644 index b4e721145..000000000 --- a/src/test/sdk/extension/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.t.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BurnToClaim, IBurnToClaim } from "contracts/extension/BurnToClaim.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyBurnToClaim is BurnToClaim { - bool condition; - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function _canSetBurnToClaim() internal view override returns (bool) { - return msg.sender == admin; - } -} - -contract BurnToClaim_SetBurnToClaimInfo is ExtensionUtilTest { - MyBurnToClaim internal ext; - address internal admin; - address internal caller; - IBurnToClaim.BurnToClaimInfo internal info; - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - caller = getActor(1); - - ext = new MyBurnToClaim(address(admin)); - info = IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(0), - tokenType: IBurnToClaim.TokenType.ERC721, - tokenId: 0, - mintPriceForNewToken: 0, - currency: address(0) - }); - } - - function test_setBurnToClaimInfo_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert("Not authorized."); - ext.setBurnToClaimInfo(info); - } - - modifier whenCallerAuthorized() { - caller = admin; - _; - } - - function test_setBurnToClaimInfo_invalidOriginContract_addressZero() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectRevert("Origin contract not set."); - ext.setBurnToClaimInfo(info); - } - - modifier whenValidOriginContract() { - info.originContractAddress = address(erc721); - _; - } - - function test_setBurnToClaimInfo_invalidCurrency_addressZero() public whenCallerAuthorized whenValidOriginContract { - vm.prank(address(caller)); - vm.expectRevert("Currency not set."); - ext.setBurnToClaimInfo(info); - } - - modifier whenValidCurrency() { - info.currency = address(erc20); - _; - } - - function test_setBurnToClaimInfo() public whenCallerAuthorized whenValidOriginContract whenValidCurrency { - vm.prank(address(caller)); - ext.setBurnToClaimInfo(info); - - IBurnToClaim.BurnToClaimInfo memory _info = ext.getBurnToClaimInfo(); - - assertEq(_info.originContractAddress, info.originContractAddress); - assertEq(_info.currency, info.currency); - } -} diff --git a/src/test/sdk/extension/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.tree b/src/test/sdk/extension/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.tree deleted file mode 100644 index d6e347f5e..000000000 --- a/src/test/sdk/extension/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.tree +++ /dev/null @@ -1,11 +0,0 @@ -setBurnToClaimInfo(BurnToClaimInfo calldata _burnToClaimInfo) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when input originContractAddress is address(0) - │ └── it should revert ✅ - └── when input originContractAddress is not address(0) - ├── when input currency is address(0) - │ └── it should revert ✅ - └── when input currency is not address(0) - └── it should save incoming struct values into burnToClaimInfo state ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.t.sol b/src/test/sdk/extension/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.t.sol deleted file mode 100644 index b985d473d..000000000 --- a/src/test/sdk/extension/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.t.sol +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BurnToClaim, IBurnToClaim } from "contracts/extension/BurnToClaim.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyBurnToClaim is BurnToClaim { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSetBurnToClaim() internal view override returns (bool) { - return condition; - } -} - -contract BurnToClaim_VerifyBurnToClaim is ExtensionUtilTest { - MyBurnToClaim internal ext; - address internal tokenOwner; - uint256 internal tokenId; - uint256 internal quantity; - - function setUp() public override { - super.setUp(); - - ext = new MyBurnToClaim(); - ext.setCondition(true); - - tokenOwner = getActor(1); - erc721.mint(address(tokenOwner), 10); - erc1155.mint(address(tokenOwner), 1, 10); - } - - function test_verifyBurnToClaim_infoNotSet() public { - vm.expectRevert(); - ext.verifyBurnToClaim(tokenOwner, tokenId, 1); - } - - // ================== - // ======= Test branch: token type is ERC721 - // ================== - - modifier whenBurnToClaimInfoSetERC721() { - ext.setBurnToClaimInfo( - IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc721), - tokenType: IBurnToClaim.TokenType.ERC721, - tokenId: 0, - mintPriceForNewToken: 0, - currency: address(erc20) - }) - ); - IBurnToClaim.BurnToClaimInfo memory info = ext.getBurnToClaimInfo(); - _; - } - - function test_verifyBurnToClaim_ERC721_quantity_not_1() public whenBurnToClaimInfoSetERC721 { - quantity = 10; - vm.expectRevert("Invalid amount"); - ext.verifyBurnToClaim(tokenOwner, tokenId, quantity); - } - - modifier whenQuantityParamisOne() { - quantity = 1; - _; - } - - function test_verifyBurnToClaim_ERC721_notOwnerOfToken() - public - whenBurnToClaimInfoSetERC721 - whenQuantityParamisOne - { - vm.expectRevert("!Owner"); - ext.verifyBurnToClaim(address(0x123), tokenId, quantity); // random address as owner - } - - modifier whenCorrectOwner() { - _; - } - - function test_verifyBurnToClaim_ERC721() - public - whenBurnToClaimInfoSetERC721 - whenQuantityParamisOne - whenCorrectOwner - { - ext.verifyBurnToClaim(tokenOwner, tokenId, quantity); - } - - // ================== - // ======= Test branch: token type is ERC1155 - // ================== - - modifier whenBurnToClaimInfoSetERC1155() { - ext.setBurnToClaimInfo( - IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc1155), - tokenType: IBurnToClaim.TokenType.ERC1155, - tokenId: 1, - mintPriceForNewToken: 0, - currency: address(erc20) - }) - ); - IBurnToClaim.BurnToClaimInfo memory info = ext.getBurnToClaimInfo(); - _; - } - - function test_verifyBurnToClaim_ERC1155_invalidTokenId() public whenBurnToClaimInfoSetERC1155 { - vm.expectRevert("Invalid token Id"); - ext.verifyBurnToClaim(tokenOwner, tokenId, quantity); // the tokenId here is 0, but eligible one is set as 1 above - } - - modifier whenCorrectTokenId() { - tokenId = 1; - _; - } - - function test_verifyBurnToClaim_ERC1155_balanceLessThanQuantity() - public - whenBurnToClaimInfoSetERC1155 - whenCorrectTokenId - { - quantity = 100; - vm.expectRevert("!Balance"); - ext.verifyBurnToClaim(tokenOwner, tokenId, quantity); // available balance is 10 - } - - modifier whenSufficientBalance() { - quantity = 10; - _; - } - - function test_verifyBurnToClaim_ERC1155() - public - whenBurnToClaimInfoSetERC1155 - whenCorrectTokenId - whenSufficientBalance - { - ext.verifyBurnToClaim(tokenOwner, tokenId, quantity); - } -} diff --git a/src/test/sdk/extension/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.tree b/src/test/sdk/extension/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.tree deleted file mode 100644 index ffc4dba9d..000000000 --- a/src/test/sdk/extension/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.tree +++ /dev/null @@ -1,23 +0,0 @@ -verifyBurnToClaim( - address tokenOwner, - uint256 tokenId, - uint256 quantity -) -├── when burn-to-claim info is not set - │ └── it should revert ✅ - └── when burn-to-claim info is set, with token type ERC721 - │ ├── when quantity param is not 1 - │ │ └── it should revert ✅ - │ └── when quantity param is 1 - │ ├── when token owner param is not the actual token owner - │ │ └── it should revert ✅ - │ └── when token owner param is the correct token owner - │ │ └── execution completes -- exit function ✅ - └── when burn-to-claim info is set, with token type ERC1155 - ├── when tokenId param doesn't match eligible tokenId - │ └── it should revert ✅ - └── when tokenId param matches eligible tokenId - ├── when token owner has balance less than quantity param - │ └── it should revert ✅ - └── when token owner has balance greater than or equal to quantity param - └── execution completes -- exit function ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/contract-metadata/set-contract-uri/setContractURI.t.sol b/src/test/sdk/extension/contract-metadata/set-contract-uri/setContractURI.t.sol deleted file mode 100644 index 3395c0850..000000000 --- a/src/test/sdk/extension/contract-metadata/set-contract-uri/setContractURI.t.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { ContractMetadata, IContractMetadata } from "contracts/extension/ContractMetadata.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyContractMetadata is ContractMetadata { - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function _canSetContractURI() internal view override returns (bool) { - return msg.sender == admin; - } -} - -contract ContractMetadata_SetContractURI is ExtensionUtilTest { - MyContractMetadata internal ext; - address internal admin; - address internal caller; - string internal uri; - - event ContractURIUpdated(string prevURI, string newURI); - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - caller = getActor(1); - uri = "ipfs://newUri"; - - ext = new MyContractMetadata(address(admin)); - } - - function test_setContractURI_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert(abi.encodeWithSelector(ContractMetadata.ContractMetadataUnauthorized.selector)); - ext.setContractURI(uri); - } - - modifier whenCallerAuthorized() { - caller = admin; - _; - } - - function test_setContractURI() public whenCallerAuthorized { - vm.prank(address(caller)); - ext.setContractURI(uri); - - string memory _updatedUri = ext.contractURI(); - assertEq(_updatedUri, uri); - } - - function test_setContractURI_event() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectEmit(false, false, false, true); - emit ContractURIUpdated("", uri); - ext.setContractURI(uri); - } -} diff --git a/src/test/sdk/extension/contract-metadata/set-contract-uri/setContractURI.tree b/src/test/sdk/extension/contract-metadata/set-contract-uri/setContractURI.tree deleted file mode 100644 index e626d76e4..000000000 --- a/src/test/sdk/extension/contract-metadata/set-contract-uri/setContractURI.tree +++ /dev/null @@ -1,6 +0,0 @@ -setContractURI(string memory uri) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── it should update contract URI to the new URI value ✅ - └── it should emit ContractURIUpdated event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/delayed-reveal/get-reveal-uri/getRevealURI.t.sol b/src/test/sdk/extension/delayed-reveal/get-reveal-uri/getRevealURI.t.sol deleted file mode 100644 index 0e97a5eb4..000000000 --- a/src/test/sdk/extension/delayed-reveal/get-reveal-uri/getRevealURI.t.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { DelayedReveal, IDelayedReveal } from "contracts/extension/DelayedReveal.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyDelayedReveal is DelayedReveal { - function setEncryptedData(uint256 _batchId, bytes memory _encryptedData) external { - _setEncryptedData(_batchId, _encryptedData); - } - - function reveal(uint256 identifier, bytes calldata key) external returns (string memory revealedURI) {} -} - -contract DelayedReveal_GetRevealURI is ExtensionUtilTest { - MyDelayedReveal internal ext; - string internal originalURI; - bytes internal encryptionKey; - bytes internal encryptedURI; - bytes internal encryptedData; - uint256 internal batchId; - bytes32 internal provenanceHash; - - function setUp() public override { - super.setUp(); - - ext = new MyDelayedReveal(); - originalURI = "ipfs://original"; - encryptionKey = "key123"; - batchId = 1; - - provenanceHash = keccak256(abi.encodePacked(originalURI, encryptionKey, block.chainid)); - encryptedURI = ext.encryptDecrypt(bytes(originalURI), encryptionKey); - encryptedData = abi.encode(encryptedURI, provenanceHash); - } - - function test_getRevealURI_encryptedDataNotSet() public { - vm.expectRevert(abi.encodeWithSelector(DelayedReveal.DelayedRevealNothingToReveal.selector)); - ext.getRevealURI(batchId, encryptionKey); - } - - modifier whenEncryptedDataIsSet() { - ext.setEncryptedData(batchId, encryptedData); - _; - } - - function test_getRevealURI_incorrectKey() public whenEncryptedDataIsSet { - bytes memory incorrectKey = "incorrect key"; - string memory incorrectURI = string(ext.encryptDecrypt(encryptedURI, incorrectKey)); - - vm.expectRevert( - abi.encodeWithSelector( - DelayedReveal.DelayedRevealIncorrectResultHash.selector, - provenanceHash, - keccak256(abi.encodePacked(incorrectURI, incorrectKey, block.chainid)) - ) - ); - ext.getRevealURI(batchId, incorrectKey); - } - - modifier whenCorrectKey() { - _; - } - - function test_getRevealURI() public whenEncryptedDataIsSet whenCorrectKey { - string memory revealedURI = ext.getRevealURI(batchId, encryptionKey); - - assertEq(originalURI, revealedURI); - } -} diff --git a/src/test/sdk/extension/delayed-reveal/get-reveal-uri/getRevealURI.tree b/src/test/sdk/extension/delayed-reveal/get-reveal-uri/getRevealURI.tree deleted file mode 100644 index acb580468..000000000 --- a/src/test/sdk/extension/delayed-reveal/get-reveal-uri/getRevealURI.tree +++ /dev/null @@ -1,8 +0,0 @@ -getRevealURI(uint256 _batchId, bytes calldata _key) -├── when there is no encrypted data set for the given batch id - │ └── it should revert ✅ - └── when there is an associated encrypted data present for the given batch id - ├── when the encryption key provided is incorrect - │ └── it should revert ✅ - └── when the encryption key provided is correct - └── it should correctly decrypt and return the original URI ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/delayed-reveal/set-encrypted-data/_setEncryptedData.t.sol b/src/test/sdk/extension/delayed-reveal/set-encrypted-data/_setEncryptedData.t.sol deleted file mode 100644 index 096e33568..000000000 --- a/src/test/sdk/extension/delayed-reveal/set-encrypted-data/_setEncryptedData.t.sol +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { DelayedReveal, IDelayedReveal } from "contracts/extension/DelayedReveal.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyDelayedReveal is DelayedReveal { - function setEncryptedData(uint256 _batchId, bytes memory _encryptedData) external { - _setEncryptedData(_batchId, _encryptedData); - } - - function reveal(uint256 identifier, bytes calldata key) external returns (string memory revealedURI) {} -} - -contract DelayedReveal_SetEncryptedData is ExtensionUtilTest { - MyDelayedReveal internal ext; - uint256 internal batchId; - bytes internal data; - - function setUp() public override { - super.setUp(); - - ext = new MyDelayedReveal(); - batchId = 1; - data = "test"; - } - - function test_setEncryptedData() public { - ext.setEncryptedData(batchId, data); - - assertEq(true, ext.isEncryptedBatch(batchId)); - assertEq(ext.encryptedData(batchId), data); - } -} diff --git a/src/test/sdk/extension/delayed-reveal/set-encrypted-data/_setEncryptedData.tree b/src/test/sdk/extension/delayed-reveal/set-encrypted-data/_setEncryptedData.tree deleted file mode 100644 index 68f99a2c8..000000000 --- a/src/test/sdk/extension/delayed-reveal/set-encrypted-data/_setEncryptedData.tree +++ /dev/null @@ -1,3 +0,0 @@ -_setEncryptedData(uint256 _batchId, bytes memory _encryptedData) -├── it should store input bytes data for the given batch id param ✅ -├── isEncryptedBatch should return true for this batch id ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/drop/claim/claim.t.sol b/src/test/sdk/extension/drop/claim/claim.t.sol deleted file mode 100644 index f1dbab680..000000000 --- a/src/test/sdk/extension/drop/claim/claim.t.sol +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Drop, IDrop, IClaimConditionMultiPhase, IClaimCondition } from "contracts/extension/Drop.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyDrop is Drop { - function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal override {} - - function _transferTokensOnClaim( - address _to, - uint256 _quantityBeingClaimed - ) internal override returns (uint256 startTokenId) {} - - function _canSetClaimConditions() internal view override returns (bool) { - return true; - } - - function verifyClaim( - uint256 _conditionId, - address _claimer, - uint256 _quantity, - address _currency, - uint256 _pricePerToken, - AllowlistProof calldata _allowlistProof - ) public view override returns (bool isOverride) {} -} - -contract Drop_Claim is ExtensionUtilTest { - MyDrop internal ext; - - address internal _claimer; - uint256 internal _quantity; - address internal _currency; - uint256 internal _pricePerToken; - IDrop.AllowlistProof internal _allowlistProof; - - IClaimCondition.ClaimCondition[] internal claimConditions; - - function setUp() public override { - super.setUp(); - - ext = new MyDrop(); - _claimer = getActor(1); - _quantity = 10; - } - - function _setConditionsState() public { - // values here are not important (except timestamp), since we won't be verifying claim params - - claimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 0, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - ext.setClaimConditions(claimConditions, false); - } - - function test_claim_noConditionsSet() public { - vm.expectRevert(abi.encodeWithSelector(Drop.DropNoActiveCondition.selector)); - ext.claim(_claimer, _quantity, _currency, _pricePerToken, _allowlistProof, ""); - } - - modifier whenConditionsAreSet() { - _setConditionsState(); - _; - } - - function test_claim() public whenConditionsAreSet { - // claim - vm.prank(_claimer); - ext.claim(_claimer, _quantity, _currency, _pricePerToken, _allowlistProof, ""); - - uint256 supplyClaimedByWallet_1 = ext.getSupplyClaimedByWallet(0, _claimer); - uint256 supplyClaimed_1 = (ext.getClaimConditionById(0)).supplyClaimed; - - // claim again - vm.prank(_claimer); - ext.claim(_claimer, _quantity, _currency, _pricePerToken, _allowlistProof, ""); - - uint256 supplyClaimedByWallet_2 = ext.getSupplyClaimedByWallet(0, _claimer); - uint256 supplyClaimed_2 = (ext.getClaimConditionById(0)).supplyClaimed; - - // check state - assertEq(supplyClaimedByWallet_1, _quantity); - assertEq(supplyClaimedByWallet_2, supplyClaimedByWallet_1 + _quantity); - - assertEq(supplyClaimed_1, _quantity); - assertEq(supplyClaimed_2, supplyClaimed_1 + _quantity); - } -} diff --git a/src/test/sdk/extension/drop/claim/claim.tree b/src/test/sdk/extension/drop/claim/claim.tree deleted file mode 100644 index 4ca1d3187..000000000 --- a/src/test/sdk/extension/drop/claim/claim.tree +++ /dev/null @@ -1,15 +0,0 @@ -claim( - address _receiver, - uint256 _quantity, - address _currency, - uint256 _pricePerToken, - AllowlistProof calldata _allowlistProof, - bytes memory _data -) -├── when no active condition - │ └── it should revert ✅ - └── when there's an active condition - └── it should increase the supplyClaimed for that condition by quantity param input ✅ - └── it should increase the supplyClaimedByWallet for that condition and msg.sender by quantity param input ✅ - -(Note: verifyClaim function has been tested separately, and hence not being tested here) \ No newline at end of file diff --git a/src/test/sdk/extension/drop/get-active-claim-condition-id/getActiveClaimConditionId.t.sol b/src/test/sdk/extension/drop/get-active-claim-condition-id/getActiveClaimConditionId.t.sol deleted file mode 100644 index 833e4f4fd..000000000 --- a/src/test/sdk/extension/drop/get-active-claim-condition-id/getActiveClaimConditionId.t.sol +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Drop, IDrop, IClaimConditionMultiPhase, IClaimCondition } from "contracts/extension/upgradeable/Drop.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyDrop is Drop { - function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal override {} - - function _transferTokensOnClaim( - address _to, - uint256 _quantityBeingClaimed - ) internal override returns (uint256 startTokenId) {} - - function _canSetClaimConditions() internal view override returns (bool) { - return true; - } -} - -contract Drop_GetActiveClaimConditionId is ExtensionUtilTest { - MyDrop internal ext; - - IClaimCondition.ClaimCondition[] internal claimConditions; - - function setUp() public override { - super.setUp(); - - ext = new MyDrop(); - } - - function _setConditionsState() public { - claimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 100, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - claimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 200, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - claimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 300, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - ext.setClaimConditions(claimConditions, false); - } - - function test_getActiveClaimConditionId_noConditionsSet() public { - vm.expectRevert("!CONDITION."); - ext.getActiveClaimConditionId(); - } - - modifier whenConditionsAreSet() { - _setConditionsState(); - _; - } - - function test_getActiveClaimConditionId_noActiveCondition() public whenConditionsAreSet { - vm.expectRevert("!CONDITION."); - ext.getActiveClaimConditionId(); - } - - modifier whenActiveConditions() { - _; - } - - function test_getActiveClaimConditionId_activeConditions() public whenConditionsAreSet whenActiveConditions { - vm.warp(claimConditions[0].startTimestamp); - - uint256 id = ext.getActiveClaimConditionId(); - assertEq(id, 0); - - vm.warp(claimConditions[1].startTimestamp); - - id = ext.getActiveClaimConditionId(); - assertEq(id, 1); - - vm.warp(claimConditions[2].startTimestamp); - - id = ext.getActiveClaimConditionId(); - assertEq(id, 2); - } -} diff --git a/src/test/sdk/extension/drop/get-active-claim-condition-id/getActiveClaimConditionId.tree b/src/test/sdk/extension/drop/get-active-claim-condition-id/getActiveClaimConditionId.tree deleted file mode 100644 index 8b8a94d99..000000000 --- a/src/test/sdk/extension/drop/get-active-claim-condition-id/getActiveClaimConditionId.tree +++ /dev/null @@ -1,8 +0,0 @@ -getActiveClaimConditionId() -├── when no conditions are set - │ └── it should revert ✅ - └── when condition(s) are set - ├── when no active condition, i.e. start timestamps of all conditions greater than block timestamp - │ └── it should revert ✅ - └── when conditions active, i.e. start timestamps at least one condition is less than or equal to the block timestamp - └── it should return the latest active claim condition id (i.e. with highest start timestamp among those active) ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/drop/set-claim-conditions/setClaimConditions.t.sol b/src/test/sdk/extension/drop/set-claim-conditions/setClaimConditions.t.sol deleted file mode 100644 index 476155352..000000000 --- a/src/test/sdk/extension/drop/set-claim-conditions/setClaimConditions.t.sol +++ /dev/null @@ -1,379 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Drop, IDrop, IClaimConditionMultiPhase, IClaimCondition } from "contracts/extension/Drop.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyDrop is Drop { - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal override {} - - function _transferTokensOnClaim( - address _to, - uint256 _quantityBeingClaimed - ) internal override returns (uint256 startTokenId) {} - - function _canSetClaimConditions() internal view override returns (bool) { - return msg.sender == admin; - } - - /** - * note: the functions below are dummy functions for test purposes, - * to directly access and set/reset state without going through the actual functions and checks - */ - - function setCondition(ClaimCondition calldata condition, uint256 _conditionId) public { - claimCondition.conditions[_conditionId] = condition; - } - - function setSupplyClaimedForCondition(uint256 _conditionId, uint256 _supplyClaimed) public { - claimCondition.conditions[_conditionId].supplyClaimed = _supplyClaimed; - } -} - -contract Drop_SetClaimConditions is ExtensionUtilTest { - MyDrop internal ext; - address internal admin; - - IClaimCondition.ClaimCondition[] internal newClaimConditions; - IClaimCondition.ClaimCondition[] internal oldClaimConditions; - - event ClaimConditionsUpdated(IClaimCondition.ClaimCondition[] claimConditions, bool resetEligibility); - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - ext = new MyDrop(admin); - - _setOldConditionsState(); - - newClaimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 100, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - newClaimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 200, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - } - - function _setOldConditionsState() public { - oldClaimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 10, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - oldClaimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 20, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - oldClaimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 30, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - vm.prank(admin); - ext.setClaimConditions(oldClaimConditions, false); - (, uint256 count) = ext.claimCondition(); - assertEq(count, oldClaimConditions.length); - - ext.setSupplyClaimedForCondition(0, 5); - ext.setSupplyClaimedForCondition(0, 20); - ext.setSupplyClaimedForCondition(0, 100); - } - - function test_setClaimConditions_notAuthorized() public { - vm.expectRevert(abi.encodeWithSelector(Drop.DropUnauthorized.selector)); - ext.setClaimConditions(newClaimConditions, false); - - vm.expectRevert(abi.encodeWithSelector(Drop.DropUnauthorized.selector)); - ext.setClaimConditions(newClaimConditions, true); - } - - modifier whenCallerAuthorized() { - vm.startPrank(admin); - _; - vm.stopPrank(); - } - - function test_setClaimConditions_incorrectStartTimestamps() public whenCallerAuthorized { - // reverse the order of timestamps - newClaimConditions[0].startTimestamp = newClaimConditions[1].startTimestamp + 100; - - vm.expectRevert(bytes("ST")); - ext.setClaimConditions(newClaimConditions, false); - - vm.expectRevert(bytes("ST")); - ext.setClaimConditions(newClaimConditions, true); - } - - modifier whenCorrectTimestamps() { - _; - } - - // ================== - // ======= Test branch: claim eligibility reset - // ================== - - function test_setClaimConditions_resetEligibility_startIndex() public whenCallerAuthorized whenCorrectTimestamps { - (, uint256 oldCount) = ext.claimCondition(); - - ext.setClaimConditions(newClaimConditions, true); - - (uint256 newStartIndex, ) = ext.claimCondition(); - assertEq(newStartIndex, oldCount); - } - - function test_setClaimConditions_resetEligibility_conditionCount() - public - whenCallerAuthorized - whenCorrectTimestamps - { - (, uint256 oldCount) = ext.claimCondition(); - assertEq(oldCount, oldClaimConditions.length); - - ext.setClaimConditions(newClaimConditions, true); - - (uint256 newStartIndex, uint256 newCount) = ext.claimCondition(); - assertEq(newCount, newClaimConditions.length); - } - - function test_setClaimConditions_resetEligibility_conditionState() - public - whenCallerAuthorized - whenCorrectTimestamps - { - ext.setClaimConditions(newClaimConditions, true); - - (uint256 newStartIndex, uint256 newCount) = ext.claimCondition(); - - for (uint256 i = 0; i < newCount; i++) { - IClaimCondition.ClaimCondition memory _claimCondition = ext.getClaimConditionById(i + newStartIndex); - - assertEq(_claimCondition.startTimestamp, newClaimConditions[i].startTimestamp); - assertEq(_claimCondition.maxClaimableSupply, newClaimConditions[i].maxClaimableSupply); - assertEq(_claimCondition.supplyClaimed, 0); - } - } - - function test_setClaimConditions_resetEligibility_oldConditionsDeleted() - public - whenCallerAuthorized - whenCorrectTimestamps - { - (uint256 oldStartIndex, uint256 oldCount) = ext.claimCondition(); - assertEq(oldCount, oldClaimConditions.length); - - ext.setClaimConditions(newClaimConditions, true); - - for (uint256 i = 0; i < oldCount; i++) { - IClaimCondition.ClaimCondition memory _claimCondition = ext.getClaimConditionById(i + oldStartIndex); - - assertEq(_claimCondition.startTimestamp, 0); - assertEq(_claimCondition.maxClaimableSupply, 0); - assertEq(_claimCondition.supplyClaimed, 0); - assertEq(_claimCondition.quantityLimitPerWallet, 0); - assertEq(_claimCondition.merkleRoot, bytes32(0)); - assertEq(_claimCondition.pricePerToken, 0); - assertEq(_claimCondition.currency, address(0)); - assertEq(_claimCondition.metadata, ""); - } - } - - function test_setClaimConditions_resetEligibility_event() public whenCallerAuthorized whenCorrectTimestamps { - // TODO: fix/review event data check by setting last param true - vm.expectEmit(false, false, false, false); - emit ClaimConditionsUpdated(newClaimConditions, true); - ext.setClaimConditions(newClaimConditions, true); - } - - // ================== - // ======= Test branch: claim eligibility not reset - // ================== - - function test_setClaimConditions_noReset_maxClaimableLessThanClaimed() - public - whenCallerAuthorized - whenCorrectTimestamps - { - IClaimCondition.ClaimCondition memory _oldCondition = ext.getClaimConditionById(0); - - // set new maxClaimableSupply less than supplyClaimed of the old condition - newClaimConditions[0].maxClaimableSupply = _oldCondition.supplyClaimed - 1; - - vm.expectRevert(abi.encodeWithSelector(Drop.DropExceedMaxSupply.selector)); - ext.setClaimConditions(newClaimConditions, false); - } - - modifier whenMaxClaimableNotLessThanClaimed() { - _; - } - - function test_setClaimConditions_noReset_startIndex() - public - whenCallerAuthorized - whenCorrectTimestamps - whenMaxClaimableNotLessThanClaimed - { - (uint256 oldStartIndex, ) = ext.claimCondition(); - - ext.setClaimConditions(newClaimConditions, false); - - (uint256 newStartIndex, ) = ext.claimCondition(); - assertEq(newStartIndex, oldStartIndex); - } - - function test_setClaimConditions_noReset_conditionCount() - public - whenCallerAuthorized - whenCorrectTimestamps - whenMaxClaimableNotLessThanClaimed - { - (, uint256 oldCount) = ext.claimCondition(); - assertEq(oldCount, oldClaimConditions.length); - - ext.setClaimConditions(newClaimConditions, false); - - (uint256 newStartIndex, uint256 newCount) = ext.claimCondition(); - assertEq(newCount, newClaimConditions.length); - } - - function test_setClaimConditions_noReset_conditionState() - public - whenCallerAuthorized - whenCorrectTimestamps - whenMaxClaimableNotLessThanClaimed - { - (, uint256 oldCount) = ext.claimCondition(); - - // setting array size as this way to avoid out-of-bound error in the second loop - uint256 length = newClaimConditions.length > oldCount ? newClaimConditions.length : oldCount; - IClaimCondition.ClaimCondition[] memory _oldConditions = new IClaimCondition.ClaimCondition[](length); - - for (uint256 i = 0; i < oldCount; i++) { - _oldConditions[i] = ext.getClaimConditionById(i); - } - - ext.setClaimConditions(newClaimConditions, false); - - (uint256 newStartIndex, uint256 newCount) = ext.claimCondition(); - - for (uint256 i = 0; i < newCount; i++) { - IClaimCondition.ClaimCondition memory _claimCondition = ext.getClaimConditionById(i + newStartIndex); - - assertEq(_claimCondition.startTimestamp, newClaimConditions[i].startTimestamp); - assertEq(_claimCondition.maxClaimableSupply, newClaimConditions[i].maxClaimableSupply); - assertEq(_claimCondition.supplyClaimed, _oldConditions[i].supplyClaimed); - } - } - - function test_setClaimConditions_resetEligibility_oldConditionsDeletedOrReplaced() - public - whenCallerAuthorized - whenCorrectTimestamps - whenMaxClaimableNotLessThanClaimed - { - (uint256 oldStartIndex, uint256 oldCount) = ext.claimCondition(); - assertEq(oldCount, oldClaimConditions.length); - - ext.setClaimConditions(newClaimConditions, false); - (, uint256 newCount) = ext.claimCondition(); - - for (uint256 i = 0; i < oldCount; i++) { - IClaimCondition.ClaimCondition memory _claimCondition = ext.getClaimConditionById(i + oldStartIndex); - - if (i >= newCount) { - // case where deleted - - assertEq(_claimCondition.startTimestamp, 0); - assertEq(_claimCondition.maxClaimableSupply, 0); - assertEq(_claimCondition.supplyClaimed, 0); - assertEq(_claimCondition.quantityLimitPerWallet, 0); - assertEq(_claimCondition.merkleRoot, bytes32(0)); - assertEq(_claimCondition.pricePerToken, 0); - assertEq(_claimCondition.currency, address(0)); - assertEq(_claimCondition.metadata, ""); - } else { - // case where replaced - - // supply claimed should be same as old condition, hence not checked below - - assertEq(_claimCondition.startTimestamp, newClaimConditions[i].startTimestamp); - assertEq(_claimCondition.maxClaimableSupply, newClaimConditions[i].maxClaimableSupply); - assertEq(_claimCondition.quantityLimitPerWallet, newClaimConditions[i].quantityLimitPerWallet); - assertEq(_claimCondition.merkleRoot, newClaimConditions[i].merkleRoot); - assertEq(_claimCondition.pricePerToken, newClaimConditions[i].pricePerToken); - assertEq(_claimCondition.currency, newClaimConditions[i].currency); - assertEq(_claimCondition.metadata, newClaimConditions[i].metadata); - } - } - } - - function test_setClaimConditions_noReset_event() - public - whenCallerAuthorized - whenCorrectTimestamps - whenMaxClaimableNotLessThanClaimed - { - // TODO: fix/review event data check by setting last param true - vm.expectEmit(false, false, false, false); - emit ClaimConditionsUpdated(newClaimConditions, false); - ext.setClaimConditions(newClaimConditions, false); - } -} diff --git a/src/test/sdk/extension/drop/set-claim-conditions/setClaimConditions.tree b/src/test/sdk/extension/drop/set-claim-conditions/setClaimConditions.tree deleted file mode 100644 index dbf6297d3..000000000 --- a/src/test/sdk/extension/drop/set-claim-conditions/setClaimConditions.tree +++ /dev/null @@ -1,24 +0,0 @@ -setClaimConditions(ClaimCondition[] calldata _conditions, bool _resetClaimEligibility) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when start timestamps of new conditions aren't in ascending order - │ └── it should revert ✅ - └── when start timestamps of new conditions are in ascending order - ├── when claim eligibility is reset - │ └── it should set new conditions start index as the count of old conditions ✅ - │ └── it should set claim condition count equal to the count of new conditions ✅ - │ └── it should correctly save all new conditions at right index ✅ - │ └── it should set supply claimed for each condition equal to 0 ✅ - │ └── it should delete all old conditions (i.e. all conditions with index less than new start index) ✅ - │ └── it should emit ClaimConditionsUpdated event ✅ - └── when claim eligibility is not reset - ├── when maxClaimableSupply of a new condition is less than supplyClaimed of the old condition (at that index) - │ └── it should revert ✅ - └── when maxClaimableSupply of a new condition is greater than or equal to supplyClaimed of the old condition (at that index) - └── it should set new conditions start index same as old start index ✅ - └── it should set claim condition count equal to the count of new conditions ✅ - └── it should correctly save all new conditions at right index ✅ - └── it should set supply claimed for each condition equal to what it was in old condition (at that index) ✅ - └── it should delete all old conditions with index exceeding new count, in case new count is less than previous count ✅ - └── it should emit ClaimConditionsUpdated event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol b/src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol deleted file mode 100644 index 0057f6220..000000000 --- a/src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol +++ /dev/null @@ -1,481 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Drop, IDrop, IClaimConditionMultiPhase, IClaimCondition } from "contracts/extension/Drop.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyDrop is Drop { - function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal override {} - - function _transferTokensOnClaim( - address _to, - uint256 _quantityBeingClaimed - ) internal override returns (uint256 startTokenId) {} - - function _canSetClaimConditions() internal view override returns (bool) { - return true; - } - - /** - * note: the functions below are dummy functions for test purposes, - * to directly access and set/reset state without going through the actual functions and checks - */ - - function setCondition(ClaimCondition calldata condition, uint256 _conditionId) public { - claimCondition.conditions[_conditionId] = condition; - } - - function setSupplyClaimedByWallet(uint256 _conditionId, address _wallet, uint256 _supplyClaimed) public { - claimCondition.supplyClaimedByWallet[_conditionId][_wallet] = _supplyClaimed; - } -} - -contract Drop_VerifyClaim is ExtensionUtilTest { - MyDrop internal ext; - - uint256 internal _conditionId; - address internal _claimer; - address internal _allowlistClaimer; - uint256 internal _quantity; - address internal _currency; - uint256 internal _pricePerToken; - IDrop.AllowlistProof internal _allowlistProof; - IDrop.AllowlistProof internal _allowlistProofEmpty; // will leave uninitialized - - IClaimCondition.ClaimCondition internal claimCondition; - IClaimCondition.ClaimCondition internal claimConditionWithAllowlist; - - function setUp() public override { - super.setUp(); - - ext = new MyDrop(); - - _claimer = getActor(1); - _allowlistClaimer = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - // claim condition without allowlist - claimCondition = IClaimCondition.ClaimCondition({ - startTimestamp: 1000, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }); - - // claim condition with allowlist -- set defaults for now - claimConditionWithAllowlist = claimCondition; - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs( - 0, // default - type(uint256).max, // default - address(0) // default - ); - } - - function _setAllowlistAndProofs( - uint256 _quantity, - uint256 _price, - address _currency - ) internal returns (IDrop.AllowlistProof memory, bytes32) { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(_quantity); - inputs[3] = Strings.toString(_price); - inputs[4] = Strings.toHexString(uint160(_currency)); - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = _quantity; - alp.pricePerToken = _price; - alp.currency = address(_currency); - - return (alp, root); - } - - // ================== - // ======= Test branch: when no allowlist - // ================== - - function test_verifyClaim_noAllowlist_invalidCurrency() public { - ext.setCondition(claimCondition, _conditionId); - - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimInvalidTokenPrice.selector, - _currency, - _pricePerToken, - claimCondition.currency, - claimCondition.pricePerToken - ) - ); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - modifier whenValidCurrency_open() { - _currency = claimCondition.currency; - _; - } - - function test_verifyClaim_noAllowlist_invalidPrice() public whenValidCurrency_open { - ext.setCondition(claimCondition, _conditionId); - - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimInvalidTokenPrice.selector, - _currency, - _pricePerToken, - claimCondition.currency, - claimCondition.pricePerToken - ) - ); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - modifier whenValidPrice_open() { - _pricePerToken = claimCondition.pricePerToken; - _; - } - - function test_verifyClaim_noAllowlist_zeroQuantity() public whenValidCurrency_open whenValidPrice_open { - ext.setCondition(claimCondition, _conditionId); - - _quantity = 0; - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, claimCondition.quantityLimitPerWallet, 0) - ); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - modifier whenNonZeroQuantity() { - _quantity = claimCondition.quantityLimitPerWallet + 1234; - _; - } - - function test_verifyClaim_noAllowlist_nonZeroInvalidQuantity() - public - whenValidCurrency_open - whenValidPrice_open - whenNonZeroQuantity - { - ext.setCondition(claimCondition, _conditionId); - ext.setSupplyClaimedByWallet(_conditionId, _claimer, claimCondition.quantityLimitPerWallet); - - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimExceedLimit.selector, - claimCondition.quantityLimitPerWallet, - _quantity + claimCondition.quantityLimitPerWallet - ) - ); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - modifier whenValidQuantity_open() { - _quantity = 1; - _; - } - - function test_verifyClaim_noAllowlist_quantityMoreThanMaxClaimableSupply() - public - whenValidCurrency_open - whenValidPrice_open - whenNonZeroQuantity - whenValidQuantity_open - { - claimCondition.supplyClaimed = claimCondition.maxClaimableSupply; - ext.setCondition(claimCondition, _conditionId); - - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimExceedMaxSupply.selector, - claimCondition.maxClaimableSupply, - _quantity + claimCondition.maxClaimableSupply - ) - ); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - modifier whenQuantityWithinMaxLimit() { - _; - } - - function test_verifyClaim_noAllowlist_beforeStartTimestamp() - public - whenValidCurrency_open - whenValidPrice_open - whenNonZeroQuantity - whenValidQuantity_open - whenQuantityWithinMaxLimit - { - ext.setCondition(claimCondition, _conditionId); - - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimNotStarted.selector, claimCondition.startTimestamp, block.timestamp) - ); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - modifier whenValidTimestamp() { - vm.warp(claimCondition.startTimestamp); - _; - } - - function test_verifyClaim_noAllowlist() - public - whenValidCurrency_open - whenValidPrice_open - whenNonZeroQuantity - whenValidQuantity_open - whenQuantityWithinMaxLimit - whenValidTimestamp - { - ext.setCondition(claimCondition, _conditionId); - - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - // ================== - // ======= Test branch: allowlist but incorrect proof -- open limits should apply - // ================== - - function test_verifyClaim_incorrectProof_invalidCurrency() public { - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimInvalidTokenPrice.selector, - _currency, - _pricePerToken, - claimCondition.currency, - claimCondition.pricePerToken - ) - ); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - function test_verifyClaim_incorrectProof_invalidPrice() public whenValidCurrency_open { - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimInvalidTokenPrice.selector, - _currency, - _pricePerToken, - claimCondition.currency, - claimCondition.pricePerToken - ) - ); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - function test_verifyClaim_incorrectProof_zeroQuantity() public whenValidCurrency_open whenValidPrice_open { - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - _quantity = 0; - vm.expectRevert( - abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, claimCondition.quantityLimitPerWallet, _quantity) - ); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - function test_verifyClaim_incorrectProof_nonZeroInvalidQuantity() - public - whenValidCurrency_open - whenValidPrice_open - whenNonZeroQuantity - { - ext.setCondition(claimConditionWithAllowlist, _conditionId); - ext.setSupplyClaimedByWallet(_conditionId, _claimer, claimCondition.quantityLimitPerWallet); - - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimExceedLimit.selector, - claimCondition.quantityLimitPerWallet, - claimCondition.quantityLimitPerWallet + _quantity - ) - ); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - function test_verifyClaim_incorrectProof() - public - whenValidCurrency_open - whenValidPrice_open - whenNonZeroQuantity - whenValidQuantity_open - whenQuantityWithinMaxLimit - whenValidTimestamp - { - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - // ================== - // ======= Test branch: allowlist with correct proof - // ================== - - function test_verifyClaim_allowlist_defaultPriceAndCurrency_invalidCurrencyParam() public { - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimInvalidTokenPrice.selector, - _currency, - _pricePerToken, - claimCondition.currency, - claimCondition.pricePerToken - ) - ); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist_defaultPriceNonDefaultCurrenct_invalidCurrencyParam() public { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs( - 0, // default - type(uint256).max, // default - address(weth) - ); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimInvalidTokenPrice.selector, - _currency, - _pricePerToken, - claimCondition.currency, - claimCondition.pricePerToken - ) - ); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist_nonDefaultPriceAndCurrency_invalidCurrencyParam() public { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs( - 0, // default - 2, - address(weth) - ); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimInvalidTokenPrice.selector, - _currency, - _pricePerToken, - address(weth), - 2 - ) - ); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist_defaultQuantity_invalidQuantityParam() public { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs( - 0, // default - 2, - address(weth) - ); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - ext.setSupplyClaimedByWallet( - _conditionId, - _allowlistClaimer, - claimConditionWithAllowlist.quantityLimitPerWallet - ); - - _currency = address(weth); - _pricePerToken = 2; - _quantity = 1; - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimExceedLimit.selector, - claimCondition.quantityLimitPerWallet, - claimCondition.quantityLimitPerWallet + _quantity - ) - ); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist_nonDefaultQuantity_invalidQuantityParam() public { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs(5, 2, address(weth)); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - ext.setSupplyClaimedByWallet(_conditionId, _allowlistClaimer, 5); - - _currency = address(weth); - _pricePerToken = 2; - _quantity = 1; - vm.expectRevert(abi.encodeWithSelector(Drop.DropClaimExceedLimit.selector, 5, 5 + _quantity)); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist_defaultPrice_invalidPriceParam() public { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs( - 5, - type(uint256).max, // default - address(weth) - ); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - _currency = address(weth); - _quantity = 1; - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimInvalidTokenPrice.selector, - _currency, - _pricePerToken, - claimCondition.currency, - claimCondition.pricePerToken - ) - ); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist_nonDefaultPrice_invalidPriceParam() public { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs(5, 1, address(weth)); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - _currency = address(weth); - _quantity = 1; - _pricePerToken = 2; - vm.expectRevert( - abi.encodeWithSelector( - Drop.DropClaimInvalidTokenPrice.selector, - _currency, - _pricePerToken, - address(weth), - 1 - ) - ); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist() public whenQuantityWithinMaxLimit whenValidTimestamp { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs(5, 1, address(weth)); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - _currency = address(weth); - _quantity = 1; - _pricePerToken = 1; - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } -} diff --git a/src/test/sdk/extension/drop/verify-claim/verifyClaim.tree b/src/test/sdk/extension/drop/verify-claim/verifyClaim.tree deleted file mode 100644 index 64553ab90..000000000 --- a/src/test/sdk/extension/drop/verify-claim/verifyClaim.tree +++ /dev/null @@ -1,67 +0,0 @@ -verifyClaim( - uint256 conditionId, - address claimer, - uint256 quantity, - address currency, - uint256 pricePerToken, - AllowlistProof calldata allowlistProof -) -├── when no allowlist - └── when currency param not equal to open claim currency - │ └── it should revert ✅ - └── when currency param is equal to open claim currency - └── when pricePerToken param not equal to open claim price - │ └── it should revert ✅ - └── when pricePerToken param is equal to open claim price - └── when quantity param is 0 - │ └── it should revert ✅ - └── when quantity param is not 0 - └── when quantity param plus supply claimed is more than open claim limit - │ └── it should revert ✅ - └── when quantity param plus supply claimed is within open claim limit - └── when quantity param plus claimed supply is more than max claimable supply - │ └── it should revert ✅ - └── when quantity param plus claimed supply is within max claimable supply limit - └── when block timestamp is less than start timestamp of claim phase - │ └── it should revert ✅ - └── when block timestamp is greater than or equal to start timestamp of claim phase - └── execution completes -- exit function ✅ - -├── when allowlist but incorrect merkle proof - └── when currency param not equal to open claim currency - │ └── it should revert ✅ - └── when currency param is equal to open claim currency - └── when pricePerToken param not equal to open claim price - │ └── it should revert ✅ - └── when pricePerToken param is equal to open claim price - └── when quantity param is 0 - │ └── it should revert ✅ - └── when quantity param is not 0 - └── when quantity param plus supply claimed is more than open claim limit - │ └── it should revert ✅ - -├── when allowlist and correct merkle proof - └── when allowlist price is default max uint256 and allowlist currency is default address(0) - │ └── when currency param not equal to open claim currency - │ └── it should revert ✅ - └── when allowlist price is default max uint256 and allowlist currency is not default - │ └── when currency param not equal to open claim currency - │ └── it should revert ✅ - └── when allowlist price is not default and allowlist currency is default address(0) - │ └── when currency param not equal to open claim currency - │ └── it should revert ✅ - └── when allowlist price is not default and allowlist currency is not default - │ └── when currency param not equal to allowlist claim currency - │ └── it should revert ✅ - └── when allowlist quantity is default 0 - │ └── when nonzero quantity param plus supply claimed is more than open claim limit - │ └── it should revert ✅ - └── when allowlist quantity is not default - │ └── when nonzero quantity param plus supply claimed is more than allowlist claim limit - │ └── it should revert ✅ - └── when allowlist price is default max uint256 - │ └── when pricePerToken param not equal to open claim price - │ └── it should revert ✅ - └── when allowlist price is not default - │ └── when pricePerToken param not equal to allowlist claim price - │ └── it should revert ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/lazy-mint/lazy-mint/lazyMint.t.sol b/src/test/sdk/extension/lazy-mint/lazy-mint/lazyMint.t.sol deleted file mode 100644 index ae6774d9e..000000000 --- a/src/test/sdk/extension/lazy-mint/lazy-mint/lazyMint.t.sol +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { LazyMint, BatchMintMetadata } from "contracts/extension/LazyMint.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyLazyMint is LazyMint { - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function _canLazyMint() internal view override returns (bool) { - return msg.sender == admin; - } - - function getBaseURI(uint256 _tokenId) external view returns (string memory) { - return _getBaseURI(_tokenId); - } - - function getBatchStartId(uint256 _batchID) public view returns (uint256) { - return _getBatchStartId(_batchID); - } - - function nextTokenIdToMint() public view returns (uint256) { - return nextTokenIdToLazyMint; - } -} - -contract LazyMint_LazyMint is ExtensionUtilTest { - MyLazyMint internal ext; - uint256 internal startId; - uint256 internal amount; - uint256[] internal batchIds; - address internal admin; - address internal caller; - - event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI); - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - caller = getActor(1); - - ext = new MyLazyMint(address(admin)); - - startId = 0; - // mint 5 batches - vm.startPrank(admin); - for (uint256 i = 0; i < 5; i++) { - uint256 _amount = (i + 1) * 10; - uint256 batchId = startId + _amount; - batchIds.push(batchId); - - string memory baseURI = Strings.toString(batchId); - startId = ext.lazyMint(_amount, baseURI, ""); - } - vm.stopPrank(); - } - - function test_lazyMint_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert(abi.encodeWithSelector(LazyMint.LazyMintUnauthorized.selector)); - ext.lazyMint(amount, "", ""); - } - - modifier whenCallerAuthorized() { - caller = admin; - _; - } - - function test_lazyMint_zeroAmount() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectRevert(abi.encodeWithSelector(LazyMint.LazyMintInvalidAmount.selector)); - ext.lazyMint(amount, "", ""); - } - - modifier whenAmountNotZero() { - amount = 50; - _; - } - - function test_lazyMint() public whenCallerAuthorized whenAmountNotZero { - // check previous state - uint256 _nextTokenIdToLazyMintOld = ext.nextTokenIdToMint(); - assertEq(_nextTokenIdToLazyMintOld, batchIds[4]); - - string memory baseURI = "ipfs://baseURI"; - - // lazy mint next batch - vm.prank(address(caller)); - uint256 _batchId = ext.lazyMint(amount, baseURI, ""); - - // check new state - uint256 _batchStartId = ext.getBatchStartId(_batchId); - assertEq(_nextTokenIdToLazyMintOld, _batchStartId); - assertEq(_batchId, _nextTokenIdToLazyMintOld + amount); - for (uint256 i = _batchStartId; i < _batchId; i++) { - assertEq(ext.getBaseURI(i), baseURI); - } - assertEq(ext.nextTokenIdToMint(), _nextTokenIdToLazyMintOld + amount); - } - - function test_lazyMint_event() public whenCallerAuthorized whenAmountNotZero { - string memory baseURI = "ipfs://baseURI"; - uint256 _nextTokenIdToLazyMintOld = ext.nextTokenIdToMint(); - - // lazy mint next batch - vm.prank(address(caller)); - vm.expectEmit(); - emit TokensLazyMinted(_nextTokenIdToLazyMintOld, _nextTokenIdToLazyMintOld + amount - 1, baseURI, ""); - ext.lazyMint(amount, baseURI, ""); - } -} diff --git a/src/test/sdk/extension/lazy-mint/lazy-mint/lazyMint.tree b/src/test/sdk/extension/lazy-mint/lazy-mint/lazyMint.tree deleted file mode 100644 index 72ac4ddb3..000000000 --- a/src/test/sdk/extension/lazy-mint/lazy-mint/lazyMint.tree +++ /dev/null @@ -1,17 +0,0 @@ -lazyMint( - uint256 _amount, - string calldata _baseURIForTokens, - bytes calldata _data -) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── when amount to lazy mint is 0 - │ └── it should revert ✅ - └── when amount to lazy mint is not 0 - └── it should save the batch of tokens starting at `nextTokenIdToLazyMint` ✅ - └── it should store batch id equal to the sum of `nextTokenIdToLazyMint` and `_amount` ✅ - └── it should map the new batch id to `_baseURIForTokens` ✅ - └── it should increase `nextTokenIdToLazyMint` by `_amount` ✅ - └── it should return the new `batchId` ✅ - └── it should emit TokensLazyMinted event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/ownable/set-owner/setOwner.t.sol b/src/test/sdk/extension/ownable/set-owner/setOwner.t.sol deleted file mode 100644 index 7f6d136d6..000000000 --- a/src/test/sdk/extension/ownable/set-owner/setOwner.t.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Ownable, IOwnable } from "contracts/extension/Ownable.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyOwnable is Ownable { - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function _canSetOwner() internal view override returns (bool) { - return msg.sender == admin; - } -} - -contract Ownable_SetOwner is ExtensionUtilTest { - MyOwnable internal ext; - address internal admin; - address internal caller; - address internal oldOwner; - address internal newOwner; - - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - caller = getActor(1); - - oldOwner = getActor(2); - newOwner = getActor(3); - - ext = new MyOwnable(address(admin)); - - vm.prank(address(admin)); - ext.setOwner(oldOwner); - - assertEq(oldOwner, ext.owner()); - } - - function test_setOwner_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorized.selector)); - ext.setOwner(newOwner); - } - - modifier whenCallerAuthorized() { - caller = admin; - _; - } - - function test_setOwner() public whenCallerAuthorized { - vm.prank(address(caller)); - ext.setOwner(newOwner); - - assertEq(newOwner, ext.owner()); - } - - function test_setOwner_event() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectEmit(true, true, false, false); - emit OwnerUpdated(oldOwner, newOwner); - ext.setOwner(newOwner); - } -} diff --git a/src/test/sdk/extension/ownable/set-owner/setOwner.tree b/src/test/sdk/extension/ownable/set-owner/setOwner.tree deleted file mode 100644 index 9db2c0a70..000000000 --- a/src/test/sdk/extension/ownable/set-owner/setOwner.tree +++ /dev/null @@ -1,6 +0,0 @@ -setContractURI(string memory uri) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── it should update owner by replacing old owner with the new owner input ✅ - └── it should emit OwnerUpdated event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol b/src/test/sdk/extension/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol deleted file mode 100644 index 792f1c52e..000000000 --- a/src/test/sdk/extension/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Royalty, IRoyalty } from "contracts/extension/Royalty.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyRoyalty is Royalty { - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {} - - function _canSetRoyaltyInfo() internal view override returns (bool) { - return msg.sender == admin; - } -} - -contract Royalty_SetDefaultRoyaltyInfo is ExtensionUtilTest { - MyRoyalty internal ext; - address internal admin; - address internal caller; - address internal defaultRoyaltyRecipient; - uint256 internal defaultRoyaltyBps; - - event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - caller = getActor(1); - defaultRoyaltyRecipient = getActor(2); - - ext = new MyRoyalty(address(admin)); - } - - function test_setDefaultRoyaltyInfo_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert(abi.encodeWithSelector(Royalty.RoyaltyUnauthorized.selector)); - ext.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } - - modifier whenCallerAuthorized() { - caller = admin; - _; - } - - function test_setDefaultRoyaltyInfo_exceedMaxBps() public whenCallerAuthorized { - defaultRoyaltyBps = 10_001; - vm.prank(address(caller)); - vm.expectRevert(abi.encodeWithSelector(Royalty.RoyaltyExceededMaxFeeBps.selector, 10_000, defaultRoyaltyBps)); - ext.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } - - modifier whenNotExceedMaxBps() { - defaultRoyaltyBps = 500; - _; - } - - function test_setDefaultRoyaltyInfo() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - ext.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - - // get default royalty info - (address _recipient, uint16 _royaltyBps) = ext.getDefaultRoyaltyInfo(); - assertEq(_recipient, defaultRoyaltyRecipient); - assertEq(_royaltyBps, uint16(defaultRoyaltyBps)); - - // get royalty info for token - uint256 tokenId = 0; - (_recipient, _royaltyBps) = ext.getRoyaltyInfoForToken(tokenId); - assertEq(_recipient, defaultRoyaltyRecipient); - assertEq(_royaltyBps, uint16(defaultRoyaltyBps)); - - // royaltyInfo - ERC2981 - uint256 salePrice = 1000; - (address _royaltyRecipient, uint256 _royaltyAmount) = ext.royaltyInfo(tokenId, salePrice); - assertEq(_royaltyRecipient, defaultRoyaltyRecipient); - assertEq(_royaltyAmount, (salePrice * defaultRoyaltyBps) / 10_000); - } - - function test_setDefaultRoyaltyInfo_event() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - vm.expectEmit(true, false, false, true); - emit DefaultRoyalty(defaultRoyaltyRecipient, defaultRoyaltyBps); - ext.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } -} diff --git a/src/test/sdk/extension/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.tree b/src/test/sdk/extension/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.tree deleted file mode 100644 index 78a4312de..000000000 --- a/src/test/sdk/extension/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.tree +++ /dev/null @@ -1,11 +0,0 @@ -setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when royalty bps input is greater than MAX_BPS - │ └── it should revert ✅ - └── when royalty bps input is less than or equal to MAX_BPS - └── it should update default royalty recipient ✅ - └── it should update default royalty bps ✅ - └── it should calculate royalty amount for a token-id based on default royalty info ✅ - └── it should emit DefaultRoyalty event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol b/src/test/sdk/extension/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol deleted file mode 100644 index 997697a5e..000000000 --- a/src/test/sdk/extension/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Royalty, IRoyalty } from "contracts/extension/Royalty.sol"; -import "../../ExtensionUtilTest.sol"; - -contract MyRoyalty is Royalty { - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {} - - function _canSetRoyaltyInfo() internal view override returns (bool) { - return msg.sender == admin; - } -} - -contract Royalty_SetRoyaltyInfoForToken is ExtensionUtilTest { - MyRoyalty internal ext; - address internal admin; - address internal caller; - address internal defaultRoyaltyRecipient; - uint256 internal defaultRoyaltyBps; - - address internal royaltyRecipientForToken; - uint256 internal royaltyBpsForToken; - uint256 internal tokenId; - - event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps); - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - caller = getActor(1); - defaultRoyaltyRecipient = getActor(2); - royaltyRecipientForToken = getActor(3); - defaultRoyaltyBps = 500; - tokenId = 1; - - ext = new MyRoyalty(address(admin)); - - vm.prank(address(admin)); - ext.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } - - function test_setRoyaltyInfoForToken_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert(abi.encodeWithSelector(Royalty.RoyaltyUnauthorized.selector)); - ext.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - } - - modifier whenCallerAuthorized() { - caller = admin; - _; - } - - function test_setRoyaltyInfoForToken_exceedMaxBps() public whenCallerAuthorized { - royaltyBpsForToken = 10_001; - vm.prank(address(caller)); - vm.expectRevert(abi.encodeWithSelector(Royalty.RoyaltyExceededMaxFeeBps.selector, 10_000, royaltyBpsForToken)); - ext.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - } - - modifier whenNotExceedMaxBps() { - royaltyBpsForToken = 1000; - _; - } - - function test_setRoyaltyInfoForToken() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - ext.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - - // get default royalty info - (address _defaultRecipient, uint16 _defaultRoyaltyBps) = ext.getDefaultRoyaltyInfo(); - assertEq(_defaultRecipient, defaultRoyaltyRecipient); - assertEq(_defaultRoyaltyBps, uint16(defaultRoyaltyBps)); - - // get royalty info for token - (address _royaltyRecipientForToken, uint16 _royaltyBpsForToken) = ext.getRoyaltyInfoForToken(tokenId); - assertEq(_royaltyRecipientForToken, royaltyRecipientForToken); - assertEq(_royaltyBpsForToken, uint16(royaltyBpsForToken)); - - // royaltyInfo - ERC2981: calculate for default - uint256 salePrice = 1000; - (address _royaltyRecipient, uint256 _royaltyAmount) = ext.royaltyInfo(0, salePrice); - assertEq(_royaltyRecipient, defaultRoyaltyRecipient); - assertEq(_royaltyAmount, (salePrice * defaultRoyaltyBps) / 10_000); - - // royaltyInfo - ERC2981: calculate for specific tokenId we set the royalty info for - (_royaltyRecipient, _royaltyAmount) = ext.royaltyInfo(tokenId, salePrice); - assertEq(_royaltyRecipient, royaltyRecipientForToken); - assertEq(_royaltyAmount, (salePrice * royaltyBpsForToken) / 10_000); - } - - function test_setRoyaltyInfoForToken_event() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - vm.expectEmit(true, true, false, true); - emit RoyaltyForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - ext.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - } -} diff --git a/src/test/sdk/extension/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.tree b/src/test/sdk/extension/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.tree deleted file mode 100644 index e28295634..000000000 --- a/src/test/sdk/extension/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.tree +++ /dev/null @@ -1,15 +0,0 @@ -function setRoyaltyInfoForToken( - uint256 _tokenId, - address _recipient, - uint256 _bps -) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when royalty bps input is greater than MAX_BPS - │ └── it should revert ✅ - └── when royalty bps input is less than or equal to MAX_BPS - └── it should update default royalty recipient ✅ - └── it should update default royalty bps ✅ - └── it should calculate royalty amount for a token-id based on default royalty info ✅ - └── it should emit DefaultRoyalty event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.t.sol b/src/test/sdk/extension/upgradeable/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.t.sol deleted file mode 100644 index 6ca105a6f..000000000 --- a/src/test/sdk/extension/upgradeable/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.t.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/upgradeable/BatchMintMetadata.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyBatchMintMetadataUpg is BatchMintMetadata { - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function getBaseURI(uint256 _batchId) external view returns (string memory) { - return _batchMintMetadataStorage().baseURI[_batchId]; - } -} - -contract UpgradeableBatchMintMetadata_BatchMintMetadata is ExtensionUtilTest { - MyBatchMintMetadataUpg internal ext; - uint256 internal startId; - uint256 internal amountToMint; - string internal baseURI; - - function setUp() public override { - super.setUp(); - - ext = new MyBatchMintMetadataUpg(); - startId = 20; - amountToMint = 100; - baseURI = "ipfs://baseURI"; - } - - function test_batchMintMetadata() public { - uint256 prevBaseURICount = ext.getBaseURICount(); - uint256 batchId = startId + amountToMint; - - ext.batchMintMetadata(startId, amountToMint, baseURI); - uint256 newBaseURICount = ext.getBaseURICount(); - assertEq(ext.getBaseURI(batchId), baseURI); - assertEq(newBaseURICount, prevBaseURICount + 1); - assertEq(ext.getBatchIdAtIndex(newBaseURICount - 1), batchId); - - vm.expectRevert("Invalid index"); - ext.getBatchIdAtIndex(newBaseURICount); - } -} diff --git a/src/test/sdk/extension/upgradeable/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.tree b/src/test/sdk/extension/upgradeable/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.tree deleted file mode 100644 index 572dd5203..000000000 --- a/src/test/sdk/extension/upgradeable/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.tree +++ /dev/null @@ -1,7 +0,0 @@ -_batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens -) -├── it should store batch id equal to the sum of `_startId` and `_amountToMint` in batchIds array ✅ -├── it should map the new batch id to `_baseURIForTokens` ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.t.sol b/src/test/sdk/extension/upgradeable/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.t.sol deleted file mode 100644 index 9bc777bf4..000000000 --- a/src/test/sdk/extension/upgradeable/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.t.sol +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/upgradeable/BatchMintMetadata.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyBatchMintMetadataUpg is BatchMintMetadata { - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function freezeBaseURI(uint256 _batchId) external { - _freezeBaseURI(_batchId); - } - - function batchFrozen(uint256 _batchId) external view returns (bool) { - return _batchMintMetadataStorage().batchFrozen[_batchId]; - } -} - -contract UpgradeableBatchMintMetadata_FreezeBaseURI is ExtensionUtilTest { - MyBatchMintMetadataUpg internal ext; - uint256 internal startId; - uint256[] internal batchIds; - uint256 internal indexToFreeze; - - event MetadataFrozen(); - - function setUp() public override { - super.setUp(); - - ext = new MyBatchMintMetadataUpg(); - - startId = 0; - // mint 5 batches - for (uint256 i = 0; i < 5; i++) { - uint256 amount = (i + 1) * 10; - uint256 batchId = startId + amount; - batchIds.push(batchId); - - (startId, ) = ext.batchMintMetadata(startId, amount, "ipfs://"); - assertEq(ext.batchFrozen(batchId), false); - } - - indexToFreeze = 3; - } - - function test_freezeBaseURI_invalidBatch() public { - vm.expectRevert("Invalid batch"); - ext.freezeBaseURI(batchIds[indexToFreeze] * 10); // non-existent batchId - } - - modifier whenBatchIdValid() { - _; - } - - function test_freezeBaseURI() public whenBatchIdValid { - ext.freezeBaseURI(batchIds[indexToFreeze]); - - assertEq(ext.batchFrozen(batchIds[indexToFreeze]), true); - } - - function test_freezeBaseURI_event() public whenBatchIdValid { - vm.expectEmit(false, false, false, false); - emit MetadataFrozen(); - ext.freezeBaseURI(batchIds[indexToFreeze]); - } -} diff --git a/src/test/sdk/extension/upgradeable/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.tree b/src/test/sdk/extension/upgradeable/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.tree deleted file mode 100644 index 4dd87edef..000000000 --- a/src/test/sdk/extension/upgradeable/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.tree +++ /dev/null @@ -1,6 +0,0 @@ -_freezeBaseURI(uint256 _batchId) -├── when there is no baseURI for given `_batchId` - │ └── it should revert ✅ - └── when there is a baseURI present for given `_batchId` - └── it should freeze the `batchId` by setting `frozen[_batchId]` to `true` ✅ - └── it should emit MetadataFrozen event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-base-uri/_getBaseURI.t.sol b/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-base-uri/_getBaseURI.t.sol deleted file mode 100644 index 153915408..000000000 --- a/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-base-uri/_getBaseURI.t.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/upgradeable/BatchMintMetadata.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyBatchMintMetadataUpg is BatchMintMetadata { - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function getBaseURI(uint256 _tokenId) external view returns (string memory) { - return _getBaseURI(_tokenId); - } -} - -contract UpgradeableBatchMintMetadata_GetBaseURI is ExtensionUtilTest { - MyBatchMintMetadataUpg internal ext; - uint256 internal startId; - uint256[] internal batchIds; - - function setUp() public override { - super.setUp(); - - ext = new MyBatchMintMetadataUpg(); - - startId = 0; - // mint 5 batches - for (uint256 i = 0; i < 5; i++) { - uint256 amount = (i + 1) * 10; - uint256 batchId = startId + amount; - batchIds.push(batchId); - - string memory baseURI = Strings.toString(batchId); - (startId, ) = ext.batchMintMetadata(startId, amount, baseURI); - } - } - - function test_getBaseURI_invalidTokenId() public { - uint256 tokenId = batchIds[4]; // tokenId greater than the last batchId - - vm.expectRevert("Invalid tokenId"); - ext.getBaseURI(tokenId); - } - - modifier whenValidTokenId() { - _; - } - - function test_getBaseURI() public whenValidTokenId { - for (uint256 i = 0; i < 5; i++) { - uint256 start = i == 0 ? 0 : batchIds[i - 1]; - for (uint256 j = start; j < batchIds[i]; j++) { - string memory _baseURI = ext.getBaseURI(j); - - assertEq(_baseURI, Strings.toString(batchIds[i])); - } - } - } -} diff --git a/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-base-uri/_getBaseURI.tree b/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-base-uri/_getBaseURI.tree deleted file mode 100644 index c4ee674bf..000000000 --- a/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-base-uri/_getBaseURI.tree +++ /dev/null @@ -1,6 +0,0 @@ -_getBaseURI(uint256 _tokenId) -├── when `_tokenId` doesn't belong to any batch, i.e. greater than the last batchId - │ └── it should revert ✅ - └── when `_tokenId` belongs to some batch, i.e. less than that batchId - └── it should return correct baseURI for the `_tokenId` ✅ -(note: all batches are assumed to be contiguous, i.e. start id of one batch is the end id of the previous batch) \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-id/_getBatchId.t.sol b/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-id/_getBatchId.t.sol deleted file mode 100644 index 9c8ec1136..000000000 --- a/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-id/_getBatchId.t.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/upgradeable/BatchMintMetadata.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyBatchMintMetadataUpg is BatchMintMetadata { - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function getBatchId(uint256 _tokenId) external view returns (uint256 batchId, uint256 index) { - return _getBatchId(_tokenId); - } -} - -contract UpgradeableBatchMintMetadata_GetBatchId is ExtensionUtilTest { - MyBatchMintMetadataUpg internal ext; - uint256 internal startId; - uint256[] internal batchIds; - - function setUp() public override { - super.setUp(); - - ext = new MyBatchMintMetadataUpg(); - - startId = 0; - // mint 5 batches - for (uint256 i = 0; i < 5; i++) { - uint256 amount = (i + 1) * 10; - batchIds.push(startId + amount); - (startId, ) = ext.batchMintMetadata(startId, amount, "ipfs://"); - } - } - - function test_getBatchId_invalidTokenId() public { - uint256 tokenId = batchIds[4]; // tokenId greater than the last batchId - - vm.expectRevert("Invalid tokenId"); - ext.getBatchId(tokenId); - } - - modifier whenValidTokenId() { - _; - } - - function test_getBatchId() public whenValidTokenId { - for (uint256 i = 0; i < 5; i++) { - uint256 start = i == 0 ? 0 : batchIds[i - 1]; - for (uint256 j = start; j < batchIds[i]; j++) { - (uint256 batchId, uint256 index) = ext.getBatchId(j); - - assertEq(batchId, batchIds[i]); - assertEq(index, i); - } - } - } -} diff --git a/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-id/_getBatchId.tree b/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-id/_getBatchId.tree deleted file mode 100644 index 2e6dd366e..000000000 --- a/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-id/_getBatchId.tree +++ /dev/null @@ -1,6 +0,0 @@ -_getBatchId(uint256 _tokenId) -├── when `_tokenId` doesn't belong to any batch, i.e. greater than the last batchId - │ └── it should revert ✅ - └── when `_tokenId` belongs to some batch, i.e. less than that batchId - └── it should return correct batchId and batch index for the `_tokenId` ✅ -(note: all batches are assumed to be contiguous, i.e. start id of one batch is the end id of the previous batch) \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-start-id/_getBatchStartId.t.sol b/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-start-id/_getBatchStartId.t.sol deleted file mode 100644 index 6a6182a3b..000000000 --- a/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-start-id/_getBatchStartId.t.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/upgradeable/BatchMintMetadata.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyBatchMintMetadataUpg is BatchMintMetadata { - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function getBatchStartId(uint256 _batchId) external view returns (uint256) { - return _getBatchStartId(_batchId); - } -} - -contract UpgradeableBatchMintMetadata_GetBatchStartId is ExtensionUtilTest { - MyBatchMintMetadataUpg internal ext; - uint256 internal startId; - uint256[] internal batchIds; - - function setUp() public override { - super.setUp(); - - ext = new MyBatchMintMetadataUpg(); - - startId = 0; - // mint 5 batches - for (uint256 i = 0; i < 5; i++) { - uint256 amount = (i + 1) * 10; - batchIds.push(startId + amount); - (startId, ) = ext.batchMintMetadata(startId, amount, "ipfs://"); - } - } - - function test_getBatchStartId_invalidBatchId() public { - uint256 batchId = batchIds[4] + 1; // non-existent batchId - - vm.expectRevert("Invalid batchId"); - ext.getBatchStartId(batchId); - } - - modifier whenValidBatchId() { - _; - } - - function test_getBatchStartId() public whenValidBatchId { - for (uint256 i = 0; i < 5; i++) { - uint256 start = i == 0 ? 0 : batchIds[i - 1]; - uint256 _batchStartId = ext.getBatchStartId(batchIds[i]); - - assertEq(start, _batchStartId); - } - } -} diff --git a/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-start-id/_getBatchStartId.tree b/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-start-id/_getBatchStartId.tree deleted file mode 100644 index 7e303ab46..000000000 --- a/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-start-id/_getBatchStartId.tree +++ /dev/null @@ -1,6 +0,0 @@ -_getBatchStartId(uint256 _batchID) -├── when `_batchID` doesn't exist - │ └── it should revert ✅ - └── when `_batchID` exists - └── it should return the starting tokenId for that batch ✅ -(note: all batches are assumed to be contiguous, i.e. start id of one batch is the end id of the previous batch) \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/batch-mint-metadata/set-base-uri/_setBaseURI.t.sol b/src/test/sdk/extension/upgradeable/batch-mint-metadata/set-base-uri/_setBaseURI.t.sol deleted file mode 100644 index 28a351d76..000000000 --- a/src/test/sdk/extension/upgradeable/batch-mint-metadata/set-base-uri/_setBaseURI.t.sol +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BatchMintMetadata } from "contracts/extension/upgradeable/BatchMintMetadata.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyBatchMintMetadataUpg is BatchMintMetadata { - function batchMintMetadata( - uint256 _startId, - uint256 _amountToMint, - string memory _baseURIForTokens - ) external returns (uint256 nextTokenIdToMint, uint256 batchId) { - (nextTokenIdToMint, batchId) = _batchMintMetadata(_startId, _amountToMint, _baseURIForTokens); - } - - function setBaseURI(uint256 _batchId, string memory _baseURI) external { - _setBaseURI(_batchId, _baseURI); - } - - function freezeBaseURI(uint256 _batchId, bool _freeze) external { - _batchMintMetadataStorage().batchFrozen[_batchId] = _freeze; - } - - function getBaseURI(uint256 _batchId) external view returns (string memory) { - return _batchMintMetadataStorage().baseURI[_batchId]; - } -} - -contract UpgradeableBatchMintMetadata_SetBaseURI is ExtensionUtilTest { - MyBatchMintMetadataUpg internal ext; - string internal newBaseURI; - uint256 internal startId; - uint256[] internal batchIds; - uint256 internal indexToUpdate; - - event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId); - - function setUp() public override { - super.setUp(); - - ext = new MyBatchMintMetadataUpg(); - - startId = 0; - // mint 5 batches - for (uint256 i = 0; i < 5; i++) { - uint256 amount = (i + 1) * 10; - uint256 batchId = startId + amount; - batchIds.push(batchId); - - (startId, ) = ext.batchMintMetadata(startId, amount, "ipfs://"); - ext.freezeBaseURI(batchId, true); - } - - indexToUpdate = 3; - newBaseURI = "ipfs://baseURI"; - } - - function test_setBaseURI_frozenBatchId() public { - vm.expectRevert("Batch frozen"); - ext.setBaseURI(batchIds[indexToUpdate], newBaseURI); - } - - modifier whenBatchIdNotFrozen() { - ext.freezeBaseURI(batchIds[indexToUpdate], false); - _; - } - - function test_setBaseURI() public whenBatchIdNotFrozen { - ext.setBaseURI(batchIds[indexToUpdate], newBaseURI); - - string memory _baseURI = ext.getBaseURI(batchIds[indexToUpdate]); - - assertEq(_baseURI, newBaseURI); - } - - function test_setBaseURI_event() public whenBatchIdNotFrozen { - vm.expectEmit(false, false, false, true); - emit BatchMetadataUpdate(batchIds[indexToUpdate - 1], batchIds[indexToUpdate]); - ext.setBaseURI(batchIds[indexToUpdate], newBaseURI); - } -} diff --git a/src/test/sdk/extension/upgradeable/batch-mint-metadata/set-base-uri/_setBaseURI.tree b/src/test/sdk/extension/upgradeable/batch-mint-metadata/set-base-uri/_setBaseURI.tree deleted file mode 100644 index 3df76f653..000000000 --- a/src/test/sdk/extension/upgradeable/batch-mint-metadata/set-base-uri/_setBaseURI.tree +++ /dev/null @@ -1,6 +0,0 @@ -_setBaseURI(uint256 _batchId, string memory _baseURI) -├── when the `_batchId` is frozen - │ └── it should revert ✅ - └── when the `_batchId` is not frozen - └── it should map the `_batchId` to `_baseURI` param ✅ - └── it should emit BatchMetadataUpdate event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.t.sol b/src/test/sdk/extension/upgradeable/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.t.sol deleted file mode 100644 index bc530cd78..000000000 --- a/src/test/sdk/extension/upgradeable/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.t.sol +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BurnToClaim, IBurnToClaim } from "contracts/extension/upgradeable/BurnToClaim.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyBurnToClaimUpg is BurnToClaim { - function burnTokensOnOrigin(address _tokenOwner, uint256 _tokenId, uint256 _quantity) public { - _burnTokensOnOrigin(_tokenOwner, _tokenId, _quantity); - } - - function _canSetBurnToClaim() internal view override returns (bool) { - return true; - } -} - -contract UpgradeableBurnToClaim_BurnTokensOnOrigin is ExtensionUtilTest { - MyBurnToClaimUpg internal ext; - Wallet internal tokenOwner; - uint256 internal tokenId; - uint256 internal quantity; - - function setUp() public override { - super.setUp(); - - ext = new MyBurnToClaimUpg(); - - tokenOwner = getWallet(); - erc721.mint(address(tokenOwner), 10); - erc1155.mint(address(tokenOwner), 1, 10); - - erc721NonBurnable.mint(address(tokenOwner), 10); - erc1155NonBurnable.mint(address(tokenOwner), 1, 10); - - tokenOwner.setApprovalForAllERC721(address(erc721), address(ext), true); - tokenOwner.setApprovalForAllERC1155(address(erc1155), address(ext), true); - } - - // ================== - // ======= Test branch: token type is ERC721 - // ================== - - modifier whenNotBurnableERC721() { - ext.setBurnToClaimInfo( - IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc721NonBurnable), - tokenType: IBurnToClaim.TokenType.ERC721, - tokenId: 0, - mintPriceForNewToken: 0, - currency: address(erc20) - }) - ); - _; - } - - function test_burnTokensOnOrigin_ERC721_nonBurnable() public whenNotBurnableERC721 { - vm.expectRevert(); - ext.burnTokensOnOrigin(address(tokenOwner), tokenId, quantity); - } - - modifier whenBurnableERC721() { - ext.setBurnToClaimInfo( - IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc721), - tokenType: IBurnToClaim.TokenType.ERC721, - tokenId: 0, - mintPriceForNewToken: 0, - currency: address(erc20) - }) - ); - _; - } - - function test_burnTokensOnOrigin_ERC721() public whenBurnableERC721 { - ext.burnTokensOnOrigin(address(tokenOwner), tokenId, quantity); - - assertEq(erc721.balanceOf(address(tokenOwner)), 9); - - vm.expectRevert(); - erc721.ownerOf(tokenId); // token doesn't exist after burning - } - - // ================== - // ======= Test branch: token type is ERC71155 - // ================== - - modifier whenNotBurnableERC1155() { - ext.setBurnToClaimInfo( - IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc1155NonBurnable), - tokenType: IBurnToClaim.TokenType.ERC1155, - tokenId: 1, - mintPriceForNewToken: 0, - currency: address(erc20) - }) - ); - _; - } - - function test_burnTokensOnOrigin_ERC1155_nonBurnable() public whenNotBurnableERC1155 { - vm.expectRevert(); - ext.burnTokensOnOrigin(address(tokenOwner), tokenId, quantity); - } - - modifier whenBurnableERC1155() { - ext.setBurnToClaimInfo( - IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc1155), - tokenType: IBurnToClaim.TokenType.ERC1155, - tokenId: 1, - mintPriceForNewToken: 0, - currency: address(erc20) - }) - ); - _; - } - - function test_burnTokensOnOrigin_ERC1155() public whenBurnableERC1155 { - ext.burnTokensOnOrigin(address(tokenOwner), tokenId, quantity); - - assertEq(erc1155.balanceOf(address(tokenOwner), tokenId), 0); - } -} diff --git a/src/test/sdk/extension/upgradeable/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.tree b/src/test/sdk/extension/upgradeable/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.tree deleted file mode 100644 index a2a3911ac..000000000 --- a/src/test/sdk/extension/upgradeable/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.tree +++ /dev/null @@ -1,15 +0,0 @@ -_burnTokensOnOrigin( - address _tokenOwner, - uint256 _tokenId, - uint256 _quantity -) -├── when burn-to-claim info has token type ERC721 - ├── when the origin ERC721 contract is not burnable - │ └── it should revert ✅ - └── when the origin ERC721 contract is burnable - └── it should successfully burn the token with given tokenId for the token owner ✅ -├── when burn-to-claim info has token type ERC1155 - ├── when the origin ERC1155 contract is not burnable - │ └── it should revert ✅ - └── when the origin ERC1155 contract is burnable - └── it should successfully burn tokens with given tokenId and quantity for the token owner ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.t.sol b/src/test/sdk/extension/upgradeable/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.t.sol deleted file mode 100644 index cb3cf0133..000000000 --- a/src/test/sdk/extension/upgradeable/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.t.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BurnToClaim, IBurnToClaim } from "contracts/extension/upgradeable/BurnToClaim.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyBurnToClaimUpg is BurnToClaim { - bool condition; - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function _canSetBurnToClaim() internal view override returns (bool) { - return msg.sender == admin; - } -} - -contract UpgradeableBurnToClaim_SetBurnToClaimInfo is ExtensionUtilTest { - MyBurnToClaimUpg internal ext; - address internal admin; - address internal caller; - IBurnToClaim.BurnToClaimInfo internal info; - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - caller = getActor(1); - - ext = new MyBurnToClaimUpg(address(admin)); - info = IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(0), - tokenType: IBurnToClaim.TokenType.ERC721, - tokenId: 0, - mintPriceForNewToken: 0, - currency: address(0) - }); - } - - function test_setBurnToClaimInfo_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert("Not authorized."); - ext.setBurnToClaimInfo(info); - } - - modifier whenCallerAuthorized() { - caller = admin; - _; - } - - function test_setBurnToClaimInfo_invalidOriginContract_addressZero() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectRevert("Origin contract not set."); - ext.setBurnToClaimInfo(info); - } - - modifier whenValidOriginContract() { - info.originContractAddress = address(erc721); - _; - } - - function test_setBurnToClaimInfo_invalidCurrency_addressZero() public whenCallerAuthorized whenValidOriginContract { - vm.prank(address(caller)); - vm.expectRevert("Currency not set."); - ext.setBurnToClaimInfo(info); - } - - modifier whenValidCurrency() { - info.currency = address(erc20); - _; - } - - function test_setBurnToClaimInfo() public whenCallerAuthorized whenValidOriginContract whenValidCurrency { - vm.prank(address(caller)); - ext.setBurnToClaimInfo(info); - - IBurnToClaim.BurnToClaimInfo memory _info = ext.getBurnToClaimInfo(); - - assertEq(_info.originContractAddress, info.originContractAddress); - assertEq(_info.currency, info.currency); - } -} diff --git a/src/test/sdk/extension/upgradeable/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.tree b/src/test/sdk/extension/upgradeable/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.tree deleted file mode 100644 index d6e347f5e..000000000 --- a/src/test/sdk/extension/upgradeable/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.tree +++ /dev/null @@ -1,11 +0,0 @@ -setBurnToClaimInfo(BurnToClaimInfo calldata _burnToClaimInfo) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when input originContractAddress is address(0) - │ └── it should revert ✅ - └── when input originContractAddress is not address(0) - ├── when input currency is address(0) - │ └── it should revert ✅ - └── when input currency is not address(0) - └── it should save incoming struct values into burnToClaimInfo state ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.t.sol b/src/test/sdk/extension/upgradeable/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.t.sol deleted file mode 100644 index 030ea0bb2..000000000 --- a/src/test/sdk/extension/upgradeable/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.t.sol +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { BurnToClaim, IBurnToClaim } from "contracts/extension/upgradeable/BurnToClaim.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyBurnToClaimUpg is BurnToClaim { - bool condition; - - function setCondition(bool _condition) external { - condition = _condition; - } - - function _canSetBurnToClaim() internal view override returns (bool) { - return condition; - } -} - -contract UpgradeableBurnToClaim_VerifyBurnToClaim is ExtensionUtilTest { - MyBurnToClaimUpg internal ext; - address internal tokenOwner; - uint256 internal tokenId; - uint256 internal quantity; - - function setUp() public override { - super.setUp(); - - ext = new MyBurnToClaimUpg(); - ext.setCondition(true); - - tokenOwner = getActor(1); - erc721.mint(address(tokenOwner), 10); - erc1155.mint(address(tokenOwner), 1, 10); - } - - function test_verifyBurnToClaim_infoNotSet() public { - vm.expectRevert(); - ext.verifyBurnToClaim(tokenOwner, tokenId, 1); - } - - // ================== - // ======= Test branch: token type is ERC721 - // ================== - - modifier whenBurnToClaimInfoSetERC721() { - ext.setBurnToClaimInfo( - IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc721), - tokenType: IBurnToClaim.TokenType.ERC721, - tokenId: 0, - mintPriceForNewToken: 0, - currency: address(erc20) - }) - ); - IBurnToClaim.BurnToClaimInfo memory info = ext.getBurnToClaimInfo(); - _; - } - - function test_verifyBurnToClaim_ERC721_quantity_not_1() public whenBurnToClaimInfoSetERC721 { - quantity = 10; - vm.expectRevert("Invalid amount"); - ext.verifyBurnToClaim(tokenOwner, tokenId, quantity); - } - - modifier whenQuantityParamisOne() { - quantity = 1; - _; - } - - function test_verifyBurnToClaim_ERC721_notOwnerOfToken() - public - whenBurnToClaimInfoSetERC721 - whenQuantityParamisOne - { - vm.expectRevert("!Owner"); - ext.verifyBurnToClaim(address(0x123), tokenId, quantity); // random address as owner - } - - modifier whenCorrectOwner() { - _; - } - - function test_verifyBurnToClaim_ERC721() - public - whenBurnToClaimInfoSetERC721 - whenQuantityParamisOne - whenCorrectOwner - { - ext.verifyBurnToClaim(tokenOwner, tokenId, quantity); - } - - // ================== - // ======= Test branch: token type is ERC1155 - // ================== - - modifier whenBurnToClaimInfoSetERC1155() { - ext.setBurnToClaimInfo( - IBurnToClaim.BurnToClaimInfo({ - originContractAddress: address(erc1155), - tokenType: IBurnToClaim.TokenType.ERC1155, - tokenId: 1, - mintPriceForNewToken: 0, - currency: address(erc20) - }) - ); - IBurnToClaim.BurnToClaimInfo memory info = ext.getBurnToClaimInfo(); - _; - } - - function test_verifyBurnToClaim_ERC1155_invalidTokenId() public whenBurnToClaimInfoSetERC1155 { - vm.expectRevert("Invalid token Id"); - ext.verifyBurnToClaim(tokenOwner, tokenId, quantity); // the tokenId here is 0, but eligible one is set as 1 above - } - - modifier whenCorrectTokenId() { - tokenId = 1; - _; - } - - function test_verifyBurnToClaim_ERC1155_balanceLessThanQuantity() - public - whenBurnToClaimInfoSetERC1155 - whenCorrectTokenId - { - quantity = 100; - vm.expectRevert("!Balance"); - ext.verifyBurnToClaim(tokenOwner, tokenId, quantity); // available balance is 10 - } - - modifier whenSufficientBalance() { - quantity = 10; - _; - } - - function test_verifyBurnToClaim_ERC1155() - public - whenBurnToClaimInfoSetERC1155 - whenCorrectTokenId - whenSufficientBalance - { - ext.verifyBurnToClaim(tokenOwner, tokenId, quantity); - } -} diff --git a/src/test/sdk/extension/upgradeable/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.tree b/src/test/sdk/extension/upgradeable/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.tree deleted file mode 100644 index ffc4dba9d..000000000 --- a/src/test/sdk/extension/upgradeable/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.tree +++ /dev/null @@ -1,23 +0,0 @@ -verifyBurnToClaim( - address tokenOwner, - uint256 tokenId, - uint256 quantity -) -├── when burn-to-claim info is not set - │ └── it should revert ✅ - └── when burn-to-claim info is set, with token type ERC721 - │ ├── when quantity param is not 1 - │ │ └── it should revert ✅ - │ └── when quantity param is 1 - │ ├── when token owner param is not the actual token owner - │ │ └── it should revert ✅ - │ └── when token owner param is the correct token owner - │ │ └── execution completes -- exit function ✅ - └── when burn-to-claim info is set, with token type ERC1155 - ├── when tokenId param doesn't match eligible tokenId - │ └── it should revert ✅ - └── when tokenId param matches eligible tokenId - ├── when token owner has balance less than quantity param - │ └── it should revert ✅ - └── when token owner has balance greater than or equal to quantity param - └── execution completes -- exit function ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/contract-metadata/set-contract-uri/setContractURI.t.sol b/src/test/sdk/extension/upgradeable/contract-metadata/set-contract-uri/setContractURI.t.sol deleted file mode 100644 index 0be6c9030..000000000 --- a/src/test/sdk/extension/upgradeable/contract-metadata/set-contract-uri/setContractURI.t.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { ContractMetadata, IContractMetadata } from "contracts/extension/upgradeable/ContractMetadata.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyContractMetadataUpg is ContractMetadata { - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function _canSetContractURI() internal view override returns (bool) { - return msg.sender == admin; - } -} - -contract UpgradeableContractMetadata_SetContractURI is ExtensionUtilTest { - MyContractMetadataUpg internal ext; - address internal admin; - address internal caller; - string internal uri; - - event ContractURIUpdated(string prevURI, string newURI); - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - caller = getActor(1); - uri = "ipfs://newUri"; - - ext = new MyContractMetadataUpg(address(admin)); - } - - function test_setContractURI_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert("Not authorized"); - ext.setContractURI(uri); - } - - modifier whenCallerAuthorized() { - caller = admin; - _; - } - - function test_setContractURI() public whenCallerAuthorized { - vm.prank(address(caller)); - ext.setContractURI(uri); - - string memory _updatedUri = ext.contractURI(); - assertEq(_updatedUri, uri); - } - - function test_setContractURI_event() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectEmit(false, false, false, true); - emit ContractURIUpdated("", uri); - ext.setContractURI(uri); - } -} diff --git a/src/test/sdk/extension/upgradeable/contract-metadata/set-contract-uri/setContractURI.tree b/src/test/sdk/extension/upgradeable/contract-metadata/set-contract-uri/setContractURI.tree deleted file mode 100644 index e626d76e4..000000000 --- a/src/test/sdk/extension/upgradeable/contract-metadata/set-contract-uri/setContractURI.tree +++ /dev/null @@ -1,6 +0,0 @@ -setContractURI(string memory uri) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── it should update contract URI to the new URI value ✅ - └── it should emit ContractURIUpdated event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/delayed-reveal/get-reveal-uri/getRevealURI.t.sol b/src/test/sdk/extension/upgradeable/delayed-reveal/get-reveal-uri/getRevealURI.t.sol deleted file mode 100644 index 62a10b844..000000000 --- a/src/test/sdk/extension/upgradeable/delayed-reveal/get-reveal-uri/getRevealURI.t.sol +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { DelayedReveal, IDelayedReveal } from "contracts/extension/upgradeable/DelayedReveal.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyDelayedRevealUpg is DelayedReveal { - function setEncryptedData(uint256 _batchId, bytes memory _encryptedData) external { - _setEncryptedData(_batchId, _encryptedData); - } - - function reveal(uint256 identifier, bytes calldata key) external returns (string memory revealedURI) {} -} - -contract UpgradeableDelayedReveal_GetRevealURI is ExtensionUtilTest { - MyDelayedRevealUpg internal ext; - string internal originalURI; - bytes internal encryptionKey; - bytes internal encryptedURI; - bytes internal encryptedData; - uint256 internal batchId; - bytes32 internal provenanceHash; - - function setUp() public override { - super.setUp(); - - ext = new MyDelayedRevealUpg(); - originalURI = "ipfs://original"; - encryptionKey = "key123"; - batchId = 1; - - provenanceHash = keccak256(abi.encodePacked(originalURI, encryptionKey, block.chainid)); - encryptedURI = ext.encryptDecrypt(bytes(originalURI), encryptionKey); - encryptedData = abi.encode(encryptedURI, provenanceHash); - } - - function test_getRevealURI_encryptedDataNotSet() public { - vm.expectRevert("Nothing to reveal"); - ext.getRevealURI(batchId, encryptionKey); - } - - modifier whenEncryptedDataIsSet() { - ext.setEncryptedData(batchId, encryptedData); - _; - } - - function test_getRevealURI_incorrectKey() public whenEncryptedDataIsSet { - bytes memory incorrectKey = "incorrect key"; - - vm.expectRevert("Incorrect key"); - ext.getRevealURI(batchId, incorrectKey); - } - - modifier whenCorrectKey() { - _; - } - - function test_getRevealURI() public whenEncryptedDataIsSet whenCorrectKey { - string memory revealedURI = ext.getRevealURI(batchId, encryptionKey); - - assertEq(originalURI, revealedURI); - } -} diff --git a/src/test/sdk/extension/upgradeable/delayed-reveal/get-reveal-uri/getRevealURI.tree b/src/test/sdk/extension/upgradeable/delayed-reveal/get-reveal-uri/getRevealURI.tree deleted file mode 100644 index acb580468..000000000 --- a/src/test/sdk/extension/upgradeable/delayed-reveal/get-reveal-uri/getRevealURI.tree +++ /dev/null @@ -1,8 +0,0 @@ -getRevealURI(uint256 _batchId, bytes calldata _key) -├── when there is no encrypted data set for the given batch id - │ └── it should revert ✅ - └── when there is an associated encrypted data present for the given batch id - ├── when the encryption key provided is incorrect - │ └── it should revert ✅ - └── when the encryption key provided is correct - └── it should correctly decrypt and return the original URI ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/delayed-reveal/set-encrypted-data/_setEncryptedData.t.sol b/src/test/sdk/extension/upgradeable/delayed-reveal/set-encrypted-data/_setEncryptedData.t.sol deleted file mode 100644 index a499bad5e..000000000 --- a/src/test/sdk/extension/upgradeable/delayed-reveal/set-encrypted-data/_setEncryptedData.t.sol +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { DelayedReveal, IDelayedReveal } from "contracts/extension/upgradeable/DelayedReveal.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyDelayedRevealUpg is DelayedReveal { - function setEncryptedData(uint256 _batchId, bytes memory _encryptedData) external { - _setEncryptedData(_batchId, _encryptedData); - } - - function reveal(uint256 identifier, bytes calldata key) external returns (string memory revealedURI) {} -} - -contract UpgradeableDelayedReveal_SetEncryptedData is ExtensionUtilTest { - MyDelayedRevealUpg internal ext; - uint256 internal batchId; - bytes internal data; - - function setUp() public override { - super.setUp(); - - ext = new MyDelayedRevealUpg(); - batchId = 1; - data = "test"; - } - - function test_setEncryptedData() public { - ext.setEncryptedData(batchId, data); - - assertEq(true, ext.isEncryptedBatch(batchId)); - assertEq(ext.encryptedData(batchId), data); - } -} diff --git a/src/test/sdk/extension/upgradeable/delayed-reveal/set-encrypted-data/_setEncryptedData.tree b/src/test/sdk/extension/upgradeable/delayed-reveal/set-encrypted-data/_setEncryptedData.tree deleted file mode 100644 index 68f99a2c8..000000000 --- a/src/test/sdk/extension/upgradeable/delayed-reveal/set-encrypted-data/_setEncryptedData.tree +++ /dev/null @@ -1,3 +0,0 @@ -_setEncryptedData(uint256 _batchId, bytes memory _encryptedData) -├── it should store input bytes data for the given batch id param ✅ -├── isEncryptedBatch should return true for this batch id ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/drop/claim/claim.t.sol b/src/test/sdk/extension/upgradeable/drop/claim/claim.t.sol deleted file mode 100644 index ca65c8830..000000000 --- a/src/test/sdk/extension/upgradeable/drop/claim/claim.t.sol +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Drop, IDrop, IClaimConditionMultiPhase, IClaimCondition } from "contracts/extension/upgradeable/Drop.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyDropUpg is Drop { - function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal override {} - - function _transferTokensOnClaim( - address _to, - uint256 _quantityBeingClaimed - ) internal override returns (uint256 startTokenId) {} - - function _canSetClaimConditions() internal view override returns (bool) { - return true; - } - - function verifyClaim( - uint256 _conditionId, - address _claimer, - uint256 _quantity, - address _currency, - uint256 _pricePerToken, - AllowlistProof calldata _allowlistProof - ) public view override returns (bool isOverride) {} -} - -contract UpgradeableDrop_Claim is ExtensionUtilTest { - MyDropUpg internal ext; - - address internal _claimer; - uint256 internal _quantity; - address internal _currency; - uint256 internal _pricePerToken; - IDrop.AllowlistProof internal _allowlistProof; - - IClaimCondition.ClaimCondition[] internal claimConditions; - - function setUp() public override { - super.setUp(); - - ext = new MyDropUpg(); - _claimer = getActor(1); - _quantity = 10; - } - - function _setConditionsState() public { - // values here are not important (except timestamp), since we won't be verifying claim params - - claimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 0, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - ext.setClaimConditions(claimConditions, false); - } - - function test_claim_noConditionsSet() public { - vm.expectRevert("!CONDITION."); - ext.claim(_claimer, _quantity, _currency, _pricePerToken, _allowlistProof, ""); - } - - modifier whenConditionsAreSet() { - _setConditionsState(); - _; - } - - function test_claim() public whenConditionsAreSet { - // claim - vm.prank(_claimer); - ext.claim(_claimer, _quantity, _currency, _pricePerToken, _allowlistProof, ""); - - uint256 supplyClaimedByWallet_1 = ext.getSupplyClaimedByWallet(0, _claimer); - uint256 supplyClaimed_1 = (ext.getClaimConditionById(0)).supplyClaimed; - - // claim again - vm.prank(_claimer); - ext.claim(_claimer, _quantity, _currency, _pricePerToken, _allowlistProof, ""); - - uint256 supplyClaimedByWallet_2 = ext.getSupplyClaimedByWallet(0, _claimer); - uint256 supplyClaimed_2 = (ext.getClaimConditionById(0)).supplyClaimed; - - // check state - assertEq(supplyClaimedByWallet_1, _quantity); - assertEq(supplyClaimedByWallet_2, supplyClaimedByWallet_1 + _quantity); - - assertEq(supplyClaimed_1, _quantity); - assertEq(supplyClaimed_2, supplyClaimed_1 + _quantity); - } -} diff --git a/src/test/sdk/extension/upgradeable/drop/claim/claim.tree b/src/test/sdk/extension/upgradeable/drop/claim/claim.tree deleted file mode 100644 index 4ca1d3187..000000000 --- a/src/test/sdk/extension/upgradeable/drop/claim/claim.tree +++ /dev/null @@ -1,15 +0,0 @@ -claim( - address _receiver, - uint256 _quantity, - address _currency, - uint256 _pricePerToken, - AllowlistProof calldata _allowlistProof, - bytes memory _data -) -├── when no active condition - │ └── it should revert ✅ - └── when there's an active condition - └── it should increase the supplyClaimed for that condition by quantity param input ✅ - └── it should increase the supplyClaimedByWallet for that condition and msg.sender by quantity param input ✅ - -(Note: verifyClaim function has been tested separately, and hence not being tested here) \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/drop/get-active-claim-condition-id/getActiveClaimConditionId.t.sol b/src/test/sdk/extension/upgradeable/drop/get-active-claim-condition-id/getActiveClaimConditionId.t.sol deleted file mode 100644 index 3fdd2dce2..000000000 --- a/src/test/sdk/extension/upgradeable/drop/get-active-claim-condition-id/getActiveClaimConditionId.t.sol +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Drop, IDrop, IClaimConditionMultiPhase, IClaimCondition } from "contracts/extension/upgradeable/Drop.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyDropUpg is Drop { - function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal override {} - - function _transferTokensOnClaim( - address _to, - uint256 _quantityBeingClaimed - ) internal override returns (uint256 startTokenId) {} - - function _canSetClaimConditions() internal view override returns (bool) { - return true; - } -} - -contract UpgradeableDrop_GetActiveClaimConditionId is ExtensionUtilTest { - MyDropUpg internal ext; - - IClaimCondition.ClaimCondition[] internal claimConditions; - - function setUp() public override { - super.setUp(); - - ext = new MyDropUpg(); - } - - function _setConditionsState() public { - claimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 100, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - claimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 200, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - claimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 300, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - ext.setClaimConditions(claimConditions, false); - } - - function test_getActiveClaimConditionId_noConditionsSet() public { - vm.expectRevert("!CONDITION."); - ext.getActiveClaimConditionId(); - } - - modifier whenConditionsAreSet() { - _setConditionsState(); - _; - } - - function test_getActiveClaimConditionId_noActiveCondition() public whenConditionsAreSet { - vm.expectRevert("!CONDITION."); - ext.getActiveClaimConditionId(); - } - - modifier whenActiveConditions() { - _; - } - - function test_getActiveClaimConditionId_activeConditions() public whenConditionsAreSet whenActiveConditions { - vm.warp(claimConditions[0].startTimestamp); - - uint256 id = ext.getActiveClaimConditionId(); - assertEq(id, 0); - - vm.warp(claimConditions[1].startTimestamp); - - id = ext.getActiveClaimConditionId(); - assertEq(id, 1); - - vm.warp(claimConditions[2].startTimestamp); - - id = ext.getActiveClaimConditionId(); - assertEq(id, 2); - } -} diff --git a/src/test/sdk/extension/upgradeable/drop/get-active-claim-condition-id/getActiveClaimConditionId.tree b/src/test/sdk/extension/upgradeable/drop/get-active-claim-condition-id/getActiveClaimConditionId.tree deleted file mode 100644 index 8b8a94d99..000000000 --- a/src/test/sdk/extension/upgradeable/drop/get-active-claim-condition-id/getActiveClaimConditionId.tree +++ /dev/null @@ -1,8 +0,0 @@ -getActiveClaimConditionId() -├── when no conditions are set - │ └── it should revert ✅ - └── when condition(s) are set - ├── when no active condition, i.e. start timestamps of all conditions greater than block timestamp - │ └── it should revert ✅ - └── when conditions active, i.e. start timestamps at least one condition is less than or equal to the block timestamp - └── it should return the latest active claim condition id (i.e. with highest start timestamp among those active) ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/drop/set-claim-conditions/setClaimConditions.t.sol b/src/test/sdk/extension/upgradeable/drop/set-claim-conditions/setClaimConditions.t.sol deleted file mode 100644 index cd96e3970..000000000 --- a/src/test/sdk/extension/upgradeable/drop/set-claim-conditions/setClaimConditions.t.sol +++ /dev/null @@ -1,379 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Drop, IDrop, IClaimConditionMultiPhase, IClaimCondition } from "contracts/extension/upgradeable/Drop.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyDropUpg is Drop { - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal override {} - - function _transferTokensOnClaim( - address _to, - uint256 _quantityBeingClaimed - ) internal override returns (uint256 startTokenId) {} - - function _canSetClaimConditions() internal view override returns (bool) { - return msg.sender == admin; - } - - /** - * note: the functions below are dummy functions for test purposes, - * to directly access and set/reset state without going through the actual functions and checks - */ - - function setCondition(ClaimCondition calldata condition, uint256 _conditionId) public { - _dropStorage().claimCondition.conditions[_conditionId] = condition; - } - - function setSupplyClaimedForCondition(uint256 _conditionId, uint256 _supplyClaimed) public { - _dropStorage().claimCondition.conditions[_conditionId].supplyClaimed = _supplyClaimed; - } -} - -contract UpgradeableDrop_SetClaimConditions is ExtensionUtilTest { - MyDropUpg internal ext; - address internal admin; - - IClaimCondition.ClaimCondition[] internal newClaimConditions; - IClaimCondition.ClaimCondition[] internal oldClaimConditions; - - event ClaimConditionsUpdated(IClaimCondition.ClaimCondition[] claimConditions, bool resetEligibility); - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - ext = new MyDropUpg(admin); - - _setOldConditionsState(); - - newClaimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 100, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - newClaimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 200, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - } - - function _setOldConditionsState() public { - oldClaimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 10, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - oldClaimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 20, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - oldClaimConditions.push( - IClaimCondition.ClaimCondition({ - startTimestamp: 30, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }) - ); - - vm.prank(admin); - ext.setClaimConditions(oldClaimConditions, false); - (, uint256 count) = ext.claimCondition(); - assertEq(count, oldClaimConditions.length); - - ext.setSupplyClaimedForCondition(0, 5); - ext.setSupplyClaimedForCondition(0, 20); - ext.setSupplyClaimedForCondition(0, 100); - } - - function test_setClaimConditions_notAuthorized() public { - vm.expectRevert("Not authorized"); - ext.setClaimConditions(newClaimConditions, false); - - vm.expectRevert("Not authorized"); - ext.setClaimConditions(newClaimConditions, true); - } - - modifier whenCallerAuthorized() { - vm.startPrank(admin); - _; - vm.stopPrank(); - } - - function test_setClaimConditions_incorrectStartTimestamps() public whenCallerAuthorized { - // reverse the order of timestamps - newClaimConditions[0].startTimestamp = newClaimConditions[1].startTimestamp + 100; - - vm.expectRevert(bytes("ST")); - ext.setClaimConditions(newClaimConditions, false); - - vm.expectRevert(bytes("ST")); - ext.setClaimConditions(newClaimConditions, true); - } - - modifier whenCorrectTimestamps() { - _; - } - - // ================== - // ======= Test branch: claim eligibility reset - // ================== - - function test_setClaimConditions_resetEligibility_startIndex() public whenCallerAuthorized whenCorrectTimestamps { - (, uint256 oldCount) = ext.claimCondition(); - - ext.setClaimConditions(newClaimConditions, true); - - (uint256 newStartIndex, ) = ext.claimCondition(); - assertEq(newStartIndex, oldCount); - } - - function test_setClaimConditions_resetEligibility_conditionCount() - public - whenCallerAuthorized - whenCorrectTimestamps - { - (, uint256 oldCount) = ext.claimCondition(); - assertEq(oldCount, oldClaimConditions.length); - - ext.setClaimConditions(newClaimConditions, true); - - (uint256 newStartIndex, uint256 newCount) = ext.claimCondition(); - assertEq(newCount, newClaimConditions.length); - } - - function test_setClaimConditions_resetEligibility_conditionState() - public - whenCallerAuthorized - whenCorrectTimestamps - { - ext.setClaimConditions(newClaimConditions, true); - - (uint256 newStartIndex, uint256 newCount) = ext.claimCondition(); - - for (uint256 i = 0; i < newCount; i++) { - IClaimCondition.ClaimCondition memory _claimCondition = ext.getClaimConditionById(i + newStartIndex); - - assertEq(_claimCondition.startTimestamp, newClaimConditions[i].startTimestamp); - assertEq(_claimCondition.maxClaimableSupply, newClaimConditions[i].maxClaimableSupply); - assertEq(_claimCondition.supplyClaimed, 0); - } - } - - function test_setClaimConditions_resetEligibility_oldConditionsDeleted() - public - whenCallerAuthorized - whenCorrectTimestamps - { - (uint256 oldStartIndex, uint256 oldCount) = ext.claimCondition(); - assertEq(oldCount, oldClaimConditions.length); - - ext.setClaimConditions(newClaimConditions, true); - - for (uint256 i = 0; i < oldCount; i++) { - IClaimCondition.ClaimCondition memory _claimCondition = ext.getClaimConditionById(i + oldStartIndex); - - assertEq(_claimCondition.startTimestamp, 0); - assertEq(_claimCondition.maxClaimableSupply, 0); - assertEq(_claimCondition.supplyClaimed, 0); - assertEq(_claimCondition.quantityLimitPerWallet, 0); - assertEq(_claimCondition.merkleRoot, bytes32(0)); - assertEq(_claimCondition.pricePerToken, 0); - assertEq(_claimCondition.currency, address(0)); - assertEq(_claimCondition.metadata, ""); - } - } - - function test_setClaimConditions_resetEligibility_event() public whenCallerAuthorized whenCorrectTimestamps { - // TODO: fix/review event data check by setting last param true - vm.expectEmit(false, false, false, false); - emit ClaimConditionsUpdated(newClaimConditions, true); - ext.setClaimConditions(newClaimConditions, true); - } - - // ================== - // ======= Test branch: claim eligibility not reset - // ================== - - function test_setClaimConditions_noReset_maxClaimableLessThanClaimed() - public - whenCallerAuthorized - whenCorrectTimestamps - { - IClaimCondition.ClaimCondition memory _oldCondition = ext.getClaimConditionById(0); - - // set new maxClaimableSupply less than supplyClaimed of the old condition - newClaimConditions[0].maxClaimableSupply = _oldCondition.supplyClaimed - 1; - - vm.expectRevert("max supply claimed"); - ext.setClaimConditions(newClaimConditions, false); - } - - modifier whenMaxClaimableNotLessThanClaimed() { - _; - } - - function test_setClaimConditions_noReset_startIndex() - public - whenCallerAuthorized - whenCorrectTimestamps - whenMaxClaimableNotLessThanClaimed - { - (uint256 oldStartIndex, ) = ext.claimCondition(); - - ext.setClaimConditions(newClaimConditions, false); - - (uint256 newStartIndex, ) = ext.claimCondition(); - assertEq(newStartIndex, oldStartIndex); - } - - function test_setClaimConditions_noReset_conditionCount() - public - whenCallerAuthorized - whenCorrectTimestamps - whenMaxClaimableNotLessThanClaimed - { - (, uint256 oldCount) = ext.claimCondition(); - assertEq(oldCount, oldClaimConditions.length); - - ext.setClaimConditions(newClaimConditions, false); - - (uint256 newStartIndex, uint256 newCount) = ext.claimCondition(); - assertEq(newCount, newClaimConditions.length); - } - - function test_setClaimConditions_noReset_conditionState() - public - whenCallerAuthorized - whenCorrectTimestamps - whenMaxClaimableNotLessThanClaimed - { - (, uint256 oldCount) = ext.claimCondition(); - - // setting array size as this way to avoid out-of-bound error in the second loop - uint256 length = newClaimConditions.length > oldCount ? newClaimConditions.length : oldCount; - IClaimCondition.ClaimCondition[] memory _oldConditions = new IClaimCondition.ClaimCondition[](length); - - for (uint256 i = 0; i < oldCount; i++) { - _oldConditions[i] = ext.getClaimConditionById(i); - } - - ext.setClaimConditions(newClaimConditions, false); - - (uint256 newStartIndex, uint256 newCount) = ext.claimCondition(); - - for (uint256 i = 0; i < newCount; i++) { - IClaimCondition.ClaimCondition memory _claimCondition = ext.getClaimConditionById(i + newStartIndex); - - assertEq(_claimCondition.startTimestamp, newClaimConditions[i].startTimestamp); - assertEq(_claimCondition.maxClaimableSupply, newClaimConditions[i].maxClaimableSupply); - assertEq(_claimCondition.supplyClaimed, _oldConditions[i].supplyClaimed); - } - } - - function test_setClaimConditions_resetEligibility_oldConditionsDeletedOrReplaced() - public - whenCallerAuthorized - whenCorrectTimestamps - whenMaxClaimableNotLessThanClaimed - { - (uint256 oldStartIndex, uint256 oldCount) = ext.claimCondition(); - assertEq(oldCount, oldClaimConditions.length); - - ext.setClaimConditions(newClaimConditions, false); - (, uint256 newCount) = ext.claimCondition(); - - for (uint256 i = 0; i < oldCount; i++) { - IClaimCondition.ClaimCondition memory _claimCondition = ext.getClaimConditionById(i + oldStartIndex); - - if (i >= newCount) { - // case where deleted - - assertEq(_claimCondition.startTimestamp, 0); - assertEq(_claimCondition.maxClaimableSupply, 0); - assertEq(_claimCondition.supplyClaimed, 0); - assertEq(_claimCondition.quantityLimitPerWallet, 0); - assertEq(_claimCondition.merkleRoot, bytes32(0)); - assertEq(_claimCondition.pricePerToken, 0); - assertEq(_claimCondition.currency, address(0)); - assertEq(_claimCondition.metadata, ""); - } else { - // case where replaced - - // supply claimed should be same as old condition, hence not checked below - - assertEq(_claimCondition.startTimestamp, newClaimConditions[i].startTimestamp); - assertEq(_claimCondition.maxClaimableSupply, newClaimConditions[i].maxClaimableSupply); - assertEq(_claimCondition.quantityLimitPerWallet, newClaimConditions[i].quantityLimitPerWallet); - assertEq(_claimCondition.merkleRoot, newClaimConditions[i].merkleRoot); - assertEq(_claimCondition.pricePerToken, newClaimConditions[i].pricePerToken); - assertEq(_claimCondition.currency, newClaimConditions[i].currency); - assertEq(_claimCondition.metadata, newClaimConditions[i].metadata); - } - } - } - - function test_setClaimConditions_noReset_event() - public - whenCallerAuthorized - whenCorrectTimestamps - whenMaxClaimableNotLessThanClaimed - { - // TODO: fix/review event data check by setting last param true - vm.expectEmit(false, false, false, false); - emit ClaimConditionsUpdated(newClaimConditions, false); - ext.setClaimConditions(newClaimConditions, false); - } -} diff --git a/src/test/sdk/extension/upgradeable/drop/set-claim-conditions/setClaimConditions.tree b/src/test/sdk/extension/upgradeable/drop/set-claim-conditions/setClaimConditions.tree deleted file mode 100644 index aecd6b06f..000000000 --- a/src/test/sdk/extension/upgradeable/drop/set-claim-conditions/setClaimConditions.tree +++ /dev/null @@ -1,24 +0,0 @@ -setClaimConditions(ClaimCondition[] calldata _conditions, bool _resetClaimEligibility) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when start timestamps of new conditions aren't in ascending order - │ └── it should revert ✅ - └── when start timestamps of new conditions are in ascending order - ├── when claim eligibility is reset - │ └── it should set new conditions start index as the count of old conditions ✅ - │ └── it should set claim condition count equal to the count of new conditions ✅ - │ └── it should correctly save all new conditions at right index ✅ - │ └── it should set supply claimed for each condition equal to 0 ✅ - │ └── it should delete all old conditions (i.e. all conditions with index less than new start index) ✅ - │ └── it should emit ClaimConditionsUpdated event ✅ - └── when claim eligibility is not reset - ├── when maxClaimableSupply of a new condition is less than supplyClaimed of the old condition (at that index) - │ └── it should revert ✅ - └── when maxClaimableSupply of a new condition is greater than or equal to supplyClaimed of the old condition (at that index) - └── it should set new conditions start index same as old start index ✅ - └── it should set claim condition count equal to the count of new conditions ✅ - └── it should correctly save all new conditions at right index ✅ - └── it should set supply claimed for each condition equal to what it was in old condition (at that index) ✅ - └── it should delete all old conditions with index exceeding new count, in case new count is less than previous count ✅ - └── it should emit ClaimConditionsUpdated event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.t.sol b/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.t.sol deleted file mode 100644 index 3cd3372a1..000000000 --- a/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.t.sol +++ /dev/null @@ -1,379 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Drop, IDrop, IClaimConditionMultiPhase, IClaimCondition } from "contracts/extension/upgradeable/Drop.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyDropUpg is Drop { - function _collectPriceOnClaim( - address _primarySaleRecipient, - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal override {} - - function _transferTokensOnClaim( - address _to, - uint256 _quantityBeingClaimed - ) internal override returns (uint256 startTokenId) {} - - function _canSetClaimConditions() internal view override returns (bool) { - return true; - } - - /** - * note: the functions below are dummy functions for test purposes, - * to directly access and set/reset state without going through the actual functions and checks - */ - - function setCondition(ClaimCondition calldata condition, uint256 _conditionId) public { - _dropStorage().claimCondition.conditions[_conditionId] = condition; - } - - function setSupplyClaimedByWallet(uint256 _conditionId, address _wallet, uint256 _supplyClaimed) public { - _dropStorage().claimCondition.supplyClaimedByWallet[_conditionId][_wallet] = _supplyClaimed; - } -} - -contract UpgradeableDrop_VerifyClaim is ExtensionUtilTest { - MyDropUpg internal ext; - - uint256 internal _conditionId; - address internal _claimer; - address internal _allowlistClaimer; - uint256 internal _quantity; - address internal _currency; - uint256 internal _pricePerToken; - IDrop.AllowlistProof internal _allowlistProof; - IDrop.AllowlistProof internal _allowlistProofEmpty; // will leave uninitialized - - IClaimCondition.ClaimCondition internal claimCondition; - IClaimCondition.ClaimCondition internal claimConditionWithAllowlist; - - function setUp() public override { - super.setUp(); - - ext = new MyDropUpg(); - - _claimer = getActor(1); - _allowlistClaimer = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); - - // claim condition without allowlist - claimCondition = IClaimCondition.ClaimCondition({ - startTimestamp: 1000, - maxClaimableSupply: 100, - supplyClaimed: 0, - quantityLimitPerWallet: 1, - merkleRoot: bytes32(0), - pricePerToken: 10, - currency: address(erc20), - metadata: "" - }); - - // claim condition with allowlist -- set defaults for now - claimConditionWithAllowlist = claimCondition; - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs( - 0, // default - type(uint256).max, // default - address(0) // default - ); - } - - function _setAllowlistAndProofs( - uint256 _quantity, - uint256 _price, - address _currency - ) internal returns (IDrop.AllowlistProof memory, bytes32) { - string[] memory inputs = new string[](5); - - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(_quantity); - inputs[3] = Strings.toString(_price); - inputs[4] = Strings.toHexString(uint160(_currency)); - - bytes memory result = vm.ffi(inputs); - // revert(); - bytes32 root = abi.decode(result, (bytes32)); - - inputs[1] = "src/test/scripts/getProof.ts"; - result = vm.ffi(inputs); - bytes32[] memory proofs = abi.decode(result, (bytes32[])); - - IDrop.AllowlistProof memory alp; - alp.proof = proofs; - alp.quantityLimitPerWallet = _quantity; - alp.pricePerToken = _price; - alp.currency = address(_currency); - - return (alp, root); - } - - // ================== - // ======= Test branch: when no allowlist - // ================== - - function test_verifyClaim_noAllowlist_invalidCurrency() public { - ext.setCondition(claimCondition, _conditionId); - - vm.expectRevert("!PriceOrCurrency"); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - modifier whenValidCurrency_open() { - _currency = claimCondition.currency; - _; - } - - function test_verifyClaim_noAllowlist_invalidPrice() public whenValidCurrency_open { - ext.setCondition(claimCondition, _conditionId); - - vm.expectRevert("!PriceOrCurrency"); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - modifier whenValidPrice_open() { - _pricePerToken = claimCondition.pricePerToken; - _; - } - - function test_verifyClaim_noAllowlist_zeroQuantity() public whenValidCurrency_open whenValidPrice_open { - ext.setCondition(claimCondition, _conditionId); - - _quantity = 0; - vm.expectRevert(bytes("!Qty")); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - modifier whenNonZeroQuantity() { - _quantity = claimCondition.quantityLimitPerWallet + 1234; - _; - } - - function test_verifyClaim_noAllowlist_nonZeroInvalidQuantity() - public - whenValidCurrency_open - whenValidPrice_open - whenNonZeroQuantity - { - ext.setCondition(claimCondition, _conditionId); - ext.setSupplyClaimedByWallet(_conditionId, _claimer, claimCondition.quantityLimitPerWallet); - - vm.expectRevert(bytes("!Qty")); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - modifier whenValidQuantity_open() { - _quantity = 1; - _; - } - - function test_verifyClaim_noAllowlist_quantityMoreThanMaxClaimableSupply() - public - whenValidCurrency_open - whenValidPrice_open - whenNonZeroQuantity - whenValidQuantity_open - { - claimCondition.supplyClaimed = claimCondition.maxClaimableSupply; - ext.setCondition(claimCondition, _conditionId); - - vm.expectRevert("!MaxSupply"); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - modifier whenQuantityWithinMaxLimit() { - _; - } - - function test_verifyClaim_noAllowlist_beforeStartTimestamp() - public - whenValidCurrency_open - whenValidPrice_open - whenNonZeroQuantity - whenValidQuantity_open - whenQuantityWithinMaxLimit - { - ext.setCondition(claimCondition, _conditionId); - - vm.expectRevert("cant claim yet"); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - modifier whenValidTimestamp() { - vm.warp(claimCondition.startTimestamp); - _; - } - - function test_verifyClaim_noAllowlist() - public - whenValidCurrency_open - whenValidPrice_open - whenNonZeroQuantity - whenValidQuantity_open - whenQuantityWithinMaxLimit - whenValidTimestamp - { - ext.setCondition(claimCondition, _conditionId); - - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - // ================== - // ======= Test branch: allowlist but incorrect proof -- open limits should apply - // ================== - - function test_verifyClaim_incorrectProof_invalidCurrency() public { - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - vm.expectRevert("!PriceOrCurrency"); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - function test_verifyClaim_incorrectProof_invalidPrice() public whenValidCurrency_open { - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - vm.expectRevert("!PriceOrCurrency"); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - function test_verifyClaim_incorrectProof_zeroQuantity() public whenValidCurrency_open whenValidPrice_open { - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - _quantity = 0; - vm.expectRevert(bytes("!Qty")); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - function test_verifyClaim_incorrectProof_nonZeroInvalidQuantity() - public - whenValidCurrency_open - whenValidPrice_open - whenNonZeroQuantity - { - ext.setCondition(claimConditionWithAllowlist, _conditionId); - ext.setSupplyClaimedByWallet(_conditionId, _claimer, claimCondition.quantityLimitPerWallet); - - vm.expectRevert(bytes("!Qty")); - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - function test_verifyClaim_incorrectProof() - public - whenValidCurrency_open - whenValidPrice_open - whenNonZeroQuantity - whenValidQuantity_open - whenQuantityWithinMaxLimit - whenValidTimestamp - { - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - ext.verifyClaim(_conditionId, _claimer, _quantity, _currency, _pricePerToken, _allowlistProofEmpty); - } - - // ================== - // ======= Test branch: allowlist with correct proof - // ================== - - function test_verifyClaim_allowlist_defaultPriceAndCurrency_invalidCurrencyParam() public { - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - vm.expectRevert("!PriceOrCurrency"); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist_defaultPriceNonDefaultCurrenct_invalidCurrencyParam() public { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs( - 0, // default - type(uint256).max, // default - address(weth) - ); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - vm.expectRevert("!PriceOrCurrency"); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist_nonDefaultPriceAndCurrency_invalidCurrencyParam() public { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs( - 0, // default - 2, - address(weth) - ); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - vm.expectRevert("!PriceOrCurrency"); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist_defaultQuantity_invalidQuantityParam() public { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs( - 0, // default - 2, - address(weth) - ); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - ext.setSupplyClaimedByWallet( - _conditionId, - _allowlistClaimer, - claimConditionWithAllowlist.quantityLimitPerWallet - ); - - _currency = address(weth); - _pricePerToken = 2; - _quantity = 1; - vm.expectRevert(bytes("!Qty")); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist_nonDefaultQuantity_invalidQuantityParam() public { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs(5, 2, address(weth)); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - ext.setSupplyClaimedByWallet(_conditionId, _allowlistClaimer, 5); - - _currency = address(weth); - _pricePerToken = 2; - _quantity = 1; - vm.expectRevert(bytes("!Qty")); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist_defaultPrice_invalidPriceParam() public { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs( - 5, - type(uint256).max, // default - address(weth) - ); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - _currency = address(weth); - _quantity = 1; - vm.expectRevert(bytes("!PriceOrCurrency")); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist_nonDefaultPrice_invalidPriceParam() public { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs(5, 1, address(weth)); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - _currency = address(weth); - _quantity = 1; - _pricePerToken = 2; - vm.expectRevert(bytes("!PriceOrCurrency")); - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } - - function test_verifyClaim_allowlist() public whenQuantityWithinMaxLimit whenValidTimestamp { - (_allowlistProof, claimConditionWithAllowlist.merkleRoot) = _setAllowlistAndProofs(5, 1, address(weth)); - ext.setCondition(claimConditionWithAllowlist, _conditionId); - - _currency = address(weth); - _quantity = 1; - _pricePerToken = 1; - ext.verifyClaim(_conditionId, _allowlistClaimer, _quantity, _currency, _pricePerToken, _allowlistProof); - } -} diff --git a/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.tree b/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.tree deleted file mode 100644 index ef84f4ef2..000000000 --- a/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.tree +++ /dev/null @@ -1,67 +0,0 @@ -verifyClaim( - uint256 conditionId, - address claimer, - uint256 quantity, - address currency, - uint256 pricePerToken, - AllowlistProof calldata allowlistProof -) -├── when no allowlist - └── when currency param not equal to open claim currency - │ └── it should revert ✅ - └── when currency param is equal to open claim currency - └── when pricePerToken param not equal to open claim price - │ └── it should revert ✅ - └── when pricePerToken param is equal to open claim price - └── when quantity param is 0 - │ └── it should revert ✅ - └── when quantity param is not 0 - └── when quantity param plus supply claimed is more than open claim limit - │ └── it should revert ✅ - └── when quantity param plus supply claimed is within open claim limit - └── when quantity param plus claimed supply is more than max claimable supply - │ └── it should revert ✅ - └── when quantity param plus claimed supply is within max claimable supply limit - └── when block timestamp is less than start timestamp of claim phase - │ └── it should revert ✅ - └── when block timestamp is greater than or equal to start timestamp of claim phase - └── execution completes -- exit function ✅ - -├── when allowlist but incorrect merkle proof - └── when currency param not equal to open claim currency - │ └── it should revert ✅ - └── when currency param is equal to open claim currency - └── when pricePerToken param not equal to open claim price - │ └── it should revert ✅ - └── when pricePerToken param is equal to open claim price - └── when quantity param is 0 - │ └── it should revert ✅ - └── when quantity param is not 0 - └── when quantity param plus supply claimed is more than open claim limit - │ └── it should revert ✅ - -├── when allowlist and correct merkle proof - └── when allowlist price is default max uint256 and allowlist currency is default address(0) - │ └── when currency param not equal to open claim currency - │ └── it should revert ✅ - └── when allowlist price is default max uint256 and allowlist currency is not default - │ └── when currency param not equal to open claim currency - │ └── it should revert ✅ - └── when allowlist price is not default and allowlist currency is default address(0) - │ └── when currency param not equal to open claim currency - │ └── it should revert ✅ - └── when allowlist price is not default and allowlist currency is not default - │ └── when currency param not equal to allowlist claim currency - │ └── it should revert ✅ - └── when allowlist quantity is default 0 - │ └── when nonzero quantity param plus supply claimed is more than open claim limit - │ └── it should revert ✅ - └── when allowlist quantity is not default - │ └── when nonzero quantity param plus supply claimed is more than allowlist claim limit - │ └── it should revert ✅ - └── when allowlist price is default max uint256 - │ └── when pricePerToken param not equal to open claim price - │ └── it should revert ✅ - └── when allowlist price is not default - │ └── when pricePerToken param not equal to allowlist claim price - │ └── it should revert ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/lazy-mint/lazy-mint/lazyMint.t.sol b/src/test/sdk/extension/upgradeable/lazy-mint/lazy-mint/lazyMint.t.sol deleted file mode 100644 index 97fec025d..000000000 --- a/src/test/sdk/extension/upgradeable/lazy-mint/lazy-mint/lazyMint.t.sol +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { LazyMint, BatchMintMetadata } from "contracts/extension/upgradeable/LazyMint.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyLazyMintUpg is LazyMint { - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function _canLazyMint() internal view override returns (bool) { - return msg.sender == admin; - } - - function getBaseURI(uint256 _tokenId) external view returns (string memory) { - return _getBaseURI(_tokenId); - } - - function getBatchStartId(uint256 _batchID) public view returns (uint256) { - return _getBatchStartId(_batchID); - } - - function nextTokenIdToMint() public view returns (uint256) { - return nextTokenIdToLazyMint(); - } -} - -contract UpgradeableLazyMint_LazyMint is ExtensionUtilTest { - MyLazyMintUpg internal ext; - uint256 internal startId; - uint256 internal amount; - uint256[] internal batchIds; - address internal admin; - address internal caller; - - event TokensLazyMinted(uint256 indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI); - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - caller = getActor(1); - - ext = new MyLazyMintUpg(address(admin)); - - startId = 0; - // mint 5 batches - vm.startPrank(admin); - for (uint256 i = 0; i < 5; i++) { - uint256 _amount = (i + 1) * 10; - uint256 batchId = startId + _amount; - batchIds.push(batchId); - - string memory baseURI = Strings.toString(batchId); - startId = ext.lazyMint(_amount, baseURI, ""); - } - vm.stopPrank(); - } - - function test_lazyMint_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert("Not authorized"); - ext.lazyMint(amount, "", ""); - } - - modifier whenCallerAuthorized() { - caller = admin; - _; - } - - function test_lazyMint_zeroAmount() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectRevert("0 amt"); - ext.lazyMint(amount, "", ""); - } - - modifier whenAmountNotZero() { - amount = 50; - _; - } - - function test_lazyMint() public whenCallerAuthorized whenAmountNotZero { - // check previous state - uint256 _nextTokenIdToLazyMintOld = ext.nextTokenIdToMint(); - assertEq(_nextTokenIdToLazyMintOld, batchIds[4]); - - string memory baseURI = "ipfs://baseURI"; - - // lazy mint next batch - vm.prank(address(caller)); - uint256 _batchId = ext.lazyMint(amount, baseURI, ""); - - // check new state - uint256 _batchStartId = ext.getBatchStartId(_batchId); - assertEq(_nextTokenIdToLazyMintOld, _batchStartId); - assertEq(_batchId, _nextTokenIdToLazyMintOld + amount); - for (uint256 i = _batchStartId; i < _batchId; i++) { - assertEq(ext.getBaseURI(i), baseURI); - } - assertEq(ext.nextTokenIdToMint(), _nextTokenIdToLazyMintOld + amount); - } - - function test_lazyMint_event() public whenCallerAuthorized whenAmountNotZero { - string memory baseURI = "ipfs://baseURI"; - uint256 _nextTokenIdToLazyMintOld = ext.nextTokenIdToMint(); - - // lazy mint next batch - vm.prank(address(caller)); - vm.expectEmit(); - emit TokensLazyMinted(_nextTokenIdToLazyMintOld, _nextTokenIdToLazyMintOld + amount - 1, baseURI, ""); - ext.lazyMint(amount, baseURI, ""); - } -} diff --git a/src/test/sdk/extension/upgradeable/lazy-mint/lazy-mint/lazyMint.tree b/src/test/sdk/extension/upgradeable/lazy-mint/lazy-mint/lazyMint.tree deleted file mode 100644 index daf177146..000000000 --- a/src/test/sdk/extension/upgradeable/lazy-mint/lazy-mint/lazyMint.tree +++ /dev/null @@ -1,17 +0,0 @@ -lazyMint( - uint256 _amount, - string calldata _baseURIForTokens, - bytes calldata _data -) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── when amount to lazy mint is 0 - │ └── it should revert ✅ - └── when amount to lazy mint is not 0 - └── it should save the batch of tokens starting at `nextTokenIdToLazyMint` ✅ - └── it should store batch id equal to the sum of `nextTokenIdToLazyMint` and `_amount` ✅ - └── it should map the new batch id to `_baseURIForTokens` ✅ - └── it should increase `nextTokenIdToLazyMint` by `_amount` ✅ - └── it should return the new `batchId` ✅ - └── it should emit TokensLazyMinted event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/ownable/set-owner/setOwner.t.sol b/src/test/sdk/extension/upgradeable/ownable/set-owner/setOwner.t.sol deleted file mode 100644 index 6c938158f..000000000 --- a/src/test/sdk/extension/upgradeable/ownable/set-owner/setOwner.t.sol +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Ownable, IOwnable } from "contracts/extension/upgradeable/Ownable.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyOwnableUpg is Ownable { - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function _canSetOwner() internal view override returns (bool) { - return msg.sender == admin; - } -} - -contract UpgradeableOwnable_SetOwner is ExtensionUtilTest { - MyOwnableUpg internal ext; - address internal admin; - address internal caller; - address internal oldOwner; - address internal newOwner; - - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - caller = getActor(1); - - oldOwner = getActor(2); - newOwner = getActor(3); - - ext = new MyOwnableUpg(address(admin)); - - vm.prank(address(admin)); - ext.setOwner(oldOwner); - - assertEq(oldOwner, ext.owner()); - } - - function test_setOwner_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert("Not authorized"); - ext.setOwner(newOwner); - } - - modifier whenCallerAuthorized() { - caller = admin; - _; - } - - function test_setOwner() public whenCallerAuthorized { - vm.prank(address(caller)); - ext.setOwner(newOwner); - - assertEq(newOwner, ext.owner()); - } - - function test_setOwner_event() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectEmit(true, true, false, false); - emit OwnerUpdated(oldOwner, newOwner); - ext.setOwner(newOwner); - } -} diff --git a/src/test/sdk/extension/upgradeable/ownable/set-owner/setOwner.tree b/src/test/sdk/extension/upgradeable/ownable/set-owner/setOwner.tree deleted file mode 100644 index 9db2c0a70..000000000 --- a/src/test/sdk/extension/upgradeable/ownable/set-owner/setOwner.tree +++ /dev/null @@ -1,6 +0,0 @@ -setContractURI(string memory uri) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── it should update owner by replacing old owner with the new owner input ✅ - └── it should emit OwnerUpdated event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol b/src/test/sdk/extension/upgradeable/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol deleted file mode 100644 index e541be5ad..000000000 --- a/src/test/sdk/extension/upgradeable/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Royalty, IRoyalty } from "contracts/extension/upgradeable/Royalty.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyRoyaltyUpg is Royalty { - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {} - - function _canSetRoyaltyInfo() internal view override returns (bool) { - return msg.sender == admin; - } -} - -contract UpgradeableRoyalty_SetDefaultRoyaltyInfo is ExtensionUtilTest { - MyRoyaltyUpg internal ext; - address internal admin; - address internal caller; - address internal defaultRoyaltyRecipient; - uint256 internal defaultRoyaltyBps; - - event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - caller = getActor(1); - defaultRoyaltyRecipient = getActor(2); - - ext = new MyRoyaltyUpg(address(admin)); - } - - function test_setDefaultRoyaltyInfo_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert("Not authorized"); - ext.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } - - modifier whenCallerAuthorized() { - caller = admin; - _; - } - - function test_setDefaultRoyaltyInfo_exceedMaxBps() public whenCallerAuthorized { - defaultRoyaltyBps = 10_001; - vm.prank(address(caller)); - vm.expectRevert("Exceeds max bps"); - ext.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } - - modifier whenNotExceedMaxBps() { - defaultRoyaltyBps = 500; - _; - } - - function test_setDefaultRoyaltyInfo() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - ext.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - - // get default royalty info - (address _recipient, uint16 _royaltyBps) = ext.getDefaultRoyaltyInfo(); - assertEq(_recipient, defaultRoyaltyRecipient); - assertEq(_royaltyBps, uint16(defaultRoyaltyBps)); - - // get royalty info for token - uint256 tokenId = 0; - (_recipient, _royaltyBps) = ext.getRoyaltyInfoForToken(tokenId); - assertEq(_recipient, defaultRoyaltyRecipient); - assertEq(_royaltyBps, uint16(defaultRoyaltyBps)); - - // royaltyInfo - ERC2981 - uint256 salePrice = 1000; - (address _royaltyRecipient, uint256 _royaltyAmount) = ext.royaltyInfo(tokenId, salePrice); - assertEq(_royaltyRecipient, defaultRoyaltyRecipient); - assertEq(_royaltyAmount, (salePrice * defaultRoyaltyBps) / 10_000); - } - - function test_setDefaultRoyaltyInfo_event() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - vm.expectEmit(true, false, false, true); - emit DefaultRoyalty(defaultRoyaltyRecipient, defaultRoyaltyBps); - ext.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } -} diff --git a/src/test/sdk/extension/upgradeable/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.tree b/src/test/sdk/extension/upgradeable/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.tree deleted file mode 100644 index 78a4312de..000000000 --- a/src/test/sdk/extension/upgradeable/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.tree +++ /dev/null @@ -1,11 +0,0 @@ -setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when royalty bps input is greater than MAX_BPS - │ └── it should revert ✅ - └── when royalty bps input is less than or equal to MAX_BPS - └── it should update default royalty recipient ✅ - └── it should update default royalty bps ✅ - └── it should calculate royalty amount for a token-id based on default royalty info ✅ - └── it should emit DefaultRoyalty event ✅ \ No newline at end of file diff --git a/src/test/sdk/extension/upgradeable/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol b/src/test/sdk/extension/upgradeable/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol deleted file mode 100644 index d28a142ee..000000000 --- a/src/test/sdk/extension/upgradeable/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; - -import { Royalty, IRoyalty } from "contracts/extension/upgradeable/Royalty.sol"; -import "../../../ExtensionUtilTest.sol"; - -contract MyRoyaltyUpg is Royalty { - address admin; - - constructor(address _admin) { - admin = _admin; - } - - function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {} - - function _canSetRoyaltyInfo() internal view override returns (bool) { - return msg.sender == admin; - } -} - -contract UpgradeableRoyalty_SetRoyaltyInfoForToken is ExtensionUtilTest { - MyRoyaltyUpg internal ext; - address internal admin; - address internal caller; - address internal defaultRoyaltyRecipient; - uint256 internal defaultRoyaltyBps; - - address internal royaltyRecipientForToken; - uint256 internal royaltyBpsForToken; - uint256 internal tokenId; - - event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps); - - function setUp() public override { - super.setUp(); - - admin = getActor(0); - caller = getActor(1); - defaultRoyaltyRecipient = getActor(2); - royaltyRecipientForToken = getActor(3); - defaultRoyaltyBps = 500; - tokenId = 1; - - ext = new MyRoyaltyUpg(address(admin)); - - vm.prank(address(admin)); - ext.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } - - function test_setRoyaltyInfoForToken_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert("Not authorized"); - ext.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - } - - modifier whenCallerAuthorized() { - caller = admin; - _; - } - - function test_setRoyaltyInfoForToken_exceedMaxBps() public whenCallerAuthorized { - royaltyBpsForToken = 10_001; - vm.prank(address(caller)); - vm.expectRevert("Exceeds max bps"); - ext.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - } - - modifier whenNotExceedMaxBps() { - royaltyBpsForToken = 1000; - _; - } - - function test_setRoyaltyInfoForToken() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - ext.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - - // get default royalty info - (address _defaultRecipient, uint16 _defaultRoyaltyBps) = ext.getDefaultRoyaltyInfo(); - assertEq(_defaultRecipient, defaultRoyaltyRecipient); - assertEq(_defaultRoyaltyBps, uint16(defaultRoyaltyBps)); - - // get royalty info for token - (address _royaltyRecipientForToken, uint16 _royaltyBpsForToken) = ext.getRoyaltyInfoForToken(tokenId); - assertEq(_royaltyRecipientForToken, royaltyRecipientForToken); - assertEq(_royaltyBpsForToken, uint16(royaltyBpsForToken)); - - // royaltyInfo - ERC2981: calculate for default - uint256 salePrice = 1000; - (address _royaltyRecipient, uint256 _royaltyAmount) = ext.royaltyInfo(0, salePrice); - assertEq(_royaltyRecipient, defaultRoyaltyRecipient); - assertEq(_royaltyAmount, (salePrice * defaultRoyaltyBps) / 10_000); - - // royaltyInfo - ERC2981: calculate for specific tokenId we set the royalty info for - (_royaltyRecipient, _royaltyAmount) = ext.royaltyInfo(tokenId, salePrice); - assertEq(_royaltyRecipient, royaltyRecipientForToken); - assertEq(_royaltyAmount, (salePrice * royaltyBpsForToken) / 10_000); - } - - function test_setRoyaltyInfoForToken_event() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - vm.expectEmit(true, true, false, true); - emit RoyaltyForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - ext.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - } -} diff --git a/src/test/sdk/extension/upgradeable/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.tree b/src/test/sdk/extension/upgradeable/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.tree deleted file mode 100644 index e28295634..000000000 --- a/src/test/sdk/extension/upgradeable/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.tree +++ /dev/null @@ -1,15 +0,0 @@ -function setRoyaltyInfoForToken( - uint256 _tokenId, - address _recipient, - uint256 _bps -) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when royalty bps input is greater than MAX_BPS - │ └── it should revert ✅ - └── when royalty bps input is less than or equal to MAX_BPS - └── it should update default royalty recipient ✅ - └── it should update default royalty bps ✅ - └── it should calculate royalty amount for a token-id based on default royalty info ✅ - └── it should emit DefaultRoyalty event ✅ \ No newline at end of file diff --git a/src/test/smart-wallet/Account.t.sol b/src/test/smart-wallet/Account.t.sol deleted file mode 100644 index 2693be086..000000000 --- a/src/test/smart-wallet/Account.t.sol +++ /dev/null @@ -1,813 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test utils -import "../utils/BaseTest.sol"; - -// Account Abstraction setup for smart wallets. -import { EntryPoint, IEntryPoint } from "contracts/prebuilts/account/utils/EntryPoint.sol"; -import { PackedUserOperation } from "contracts/prebuilts/account/interfaces/PackedUserOperation.sol"; - -// Target -import { IAccountPermissions } from "contracts/extension/interface/IAccountPermissions.sol"; -import { AccountFactory } from "contracts/prebuilts/account/non-upgradeable/AccountFactory.sol"; -import { Account as SimpleAccount } from "contracts/prebuilts/account/non-upgradeable/Account.sol"; -import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - -/// @dev This is a dummy contract to test contract interactions with Account. -contract Number { - uint256 public num; - - function setNum(uint256 _num) public { - num = _num; - } - - function doubleNum() public { - num *= 2; - } - - function incrementNum() public { - num += 1; - } -} - -contract SimpleAccountTest is BaseTest { - // Target contracts - EntryPoint private entrypoint; - AccountFactory private accountFactory; - - // Mocks - Number internal numberContract; - - // Test params - uint256 private accountAdminPKey = 100; - address private accountAdmin; - - uint256 private accountSignerPKey = 200; - address private accountSigner; - - uint256 private nonSignerPKey = 300; - address private nonSigner; - - // UserOp terminology: `sender` is the smart wallet. - address private sender = 0x0df2C3523703d165Aa7fA1a552f3F0B56275DfC6; - address payable private beneficiary = payable(address(0x45654)); - - bytes32 private uidCache = bytes32("random uid"); - - event AccountCreated(address indexed account, address indexed accountAdmin); - - function _prepareSignature( - IAccountPermissions.SignerPermissionRequest memory _req - ) internal view returns (bytes32 typedDataHash) { - bytes32 typehashSignerPermissionRequest = keccak256( - "SignerPermissionRequest(address signer,uint8 isAdmin,address[] approvedTargets,uint256 nativeTokenLimitPerTransaction,uint128 permissionStartTimestamp,uint128 permissionEndTimestamp,uint128 reqValidityStartTimestamp,uint128 reqValidityEndTimestamp,bytes32 uid)" - ); - bytes32 nameHash = keccak256(bytes("Account")); - bytes32 versionHash = keccak256(bytes("1")); - bytes32 typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - bytes32 domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, sender)); - - bytes memory encodedRequestStart = abi.encode( - typehashSignerPermissionRequest, - _req.signer, - _req.isAdmin, - keccak256(abi.encodePacked(_req.approvedTargets)), - _req.nativeTokenLimitPerTransaction - ); - - bytes memory encodedRequestEnd = abi.encode( - _req.permissionStartTimestamp, - _req.permissionEndTimestamp, - _req.reqValidityStartTimestamp, - _req.reqValidityEndTimestamp, - _req.uid - ); - - bytes32 structHash = keccak256(bytes.concat(encodedRequestStart, encodedRequestEnd)); - typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - } - - function _signSignerPermissionRequest( - IAccountPermissions.SignerPermissionRequest memory _req - ) internal view returns (bytes memory signature) { - bytes32 typedDataHash = _prepareSignature(_req); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, typedDataHash); - signature = abi.encodePacked(r, s, v); - } - - function _setupUserOp( - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (PackedUserOperation[] memory ops) { - uint256 nonce = entrypoint.getNonce(sender, 0); - PackedUserOperation memory op; - - { - uint128 verificationGasLimit = 500_000; - uint128 callGasLimit = 500_000; - bytes32 packedGasLimits = (bytes32(uint256(verificationGasLimit)) << 128) | bytes32(uint256(callGasLimit)); - - // Get user op fields - op = PackedUserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - accountGasLimits: packedGasLimits, - preVerificationGas: 500_000, - gasFees: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - } - - // Sign UserOp - bytes32 opHash = EntryPoint(entrypoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new PackedUserOperation[](1); - ops[0] = op; - } - - function _setupUserOpWithSender( - bytes memory _initCode, - bytes memory _callDataForEntrypoint, - address _sender - ) internal returns (PackedUserOperation[] memory ops) { - uint256 nonce = entrypoint.getNonce(_sender, 0); - PackedUserOperation memory op; - - { - uint128 verificationGasLimit = 500_000; - uint128 callGasLimit = 500_000; - bytes32 packedGasLimits = (bytes32(uint256(verificationGasLimit)) << 128) | bytes32(uint256(callGasLimit)); - - // Get user op fields - op = PackedUserOperation({ - sender: _sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - accountGasLimits: packedGasLimits, - preVerificationGas: 500_000, - gasFees: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - } - - // Sign UserOp - bytes32 opHash = EntryPoint(entrypoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(accountAdminPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new PackedUserOperation[](1); - ops[0] = op; - } - - function _setupUserOpExecuteWithSender( - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData, - address _sender - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "execute(address,uint256,bytes)", - _target, - _value, - _callData - ); - - return _setupUserOpWithSender(_initCode, callDataForEntrypoint, _sender); - } - - function _setupUserOpExecute( - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "execute(address,uint256,bytes)", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - function _setupUserOpExecuteBatch( - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "executeBatch(address[],uint256[],bytes[])", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - /// @dev Returns the salt used when deploying an Account. - function _generateSalt(address _admin, bytes memory _data) internal view virtual returns (bytes32) { - return keccak256(abi.encode(_admin, _data)); - } - - function setUp() public override { - super.setUp(); - - // Setup signers. - accountAdmin = vm.addr(accountAdminPKey); - vm.deal(accountAdmin, 100 ether); - - accountSigner = vm.addr(accountSignerPKey); - nonSigner = vm.addr(nonSignerPKey); - - // Setup contracts - entrypoint = new EntryPoint(); - // deploy account factory - accountFactory = new AccountFactory(deployer, IEntryPoint(payable(address(entrypoint)))); - // deploy dummy contract - numberContract = new Number(); - } - - /*/////////////////////////////////////////////////////////////// - Test: creating an account - //////////////////////////////////////////////////////////////*/ - - /// @dev Create an account by directly calling the factory. - function test_state_createAccount_viaFactory() public { - vm.expectEmit(true, true, false, true); - emit AccountCreated(sender, accountAdmin); - accountFactory.createAccount(accountAdmin, bytes("")); - - address[] memory allAccounts = accountFactory.getAllAccounts(); - assertEq(allAccounts.length, 1); - assertEq(allAccounts[0], sender); - } - - /// @dev Create an account via Entrypoint. - function test_state_createAccount_viaEntrypoint() public { - bytes memory initCallData = abi.encodeWithSignature("createAccount(address,bytes)", accountAdmin, bytes("")); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecute( - accountAdminPKey, - initCode, - address(0), - 0, - bytes("") - ); - - vm.expectEmit(true, true, false, true); - emit AccountCreated(sender, accountAdmin); - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - - address[] memory allAccounts = accountFactory.getAllAccounts(); - assertEq(allAccounts.length, 1); - assertEq(allAccounts[0], sender); - } - - /// @dev Try registering with factory with a contract not deployed by factory. - function test_revert_onRegister_nonFactoryChildContract() public { - vm.prank(address(0x12345)); - vm.expectRevert("AccountFactory: not an account."); - accountFactory.onRegister(_generateSalt(accountAdmin, "")); - } - - /// @dev Create more than one accounts with the same admin. - function test_state_createAccount_viaEntrypoint_multipleAccountSameAdmin() public { - uint256 start = 0; - uint256 end = 0; - - assertEq(accountFactory.totalAccounts(), 0); - - vm.expectRevert("BaseAccountFactory: invalid indices"); - address[] memory accs = accountFactory.getAccounts(start, end); - - uint256 amount = 100; - - for (uint256 i = 0; i < amount; i += 1) { - bytes memory initCallData = abi.encodeWithSignature( - "createAccount(address,bytes)", - accountAdmin, - bytes(abi.encode(i)) - ); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - address expectedSenderAddress = Clones.predictDeterministicAddress( - accountFactory.accountImplementation(), - _generateSalt(accountAdmin, bytes(abi.encode(i))), - address(accountFactory) - ); - - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecuteWithSender( - initCode, - address(0), - 0, - bytes(abi.encode(i)), - expectedSenderAddress - ); - - vm.expectEmit(true, true, false, true); - emit AccountCreated(expectedSenderAddress, accountAdmin); - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - } - - address[] memory allAccounts = accountFactory.getAllAccounts(); - assertEq(allAccounts.length, amount); - assertEq(accountFactory.totalAccounts(), amount); - - for (uint256 i = 0; i < amount; i += 1) { - assertEq( - allAccounts[i], - Clones.predictDeterministicAddress( - accountFactory.accountImplementation(), - _generateSalt(accountAdmin, bytes(abi.encode(i))), - address(accountFactory) - ) - ); - } - - start = 25; - end = 75; - - address[] memory accountsPaginatedOne = accountFactory.getAccounts(start, end); - - for (uint256 i = 0; i < (end - start); i += 1) { - assertEq( - accountsPaginatedOne[i], - Clones.predictDeterministicAddress( - accountFactory.accountImplementation(), - _generateSalt(accountAdmin, bytes(abi.encode(start + i))), - address(accountFactory) - ) - ); - } - - start = 0; - end = amount; - - address[] memory accountsPaginatedTwo = accountFactory.getAccounts(start, end); - - for (uint256 i = 0; i < (end - start); i += 1) { - assertEq( - accountsPaginatedTwo[i], - Clones.predictDeterministicAddress( - accountFactory.accountImplementation(), - _generateSalt(accountAdmin, bytes(abi.encode(start + i))), - address(accountFactory) - ) - ); - } - - start = 75; - end = 25; - - vm.expectRevert("BaseAccountFactory: invalid indices"); - accs = accountFactory.getAccounts(start, end); - - start = 25; - end = amount + 1; - - vm.expectRevert("BaseAccountFactory: invalid indices"); - accs = accountFactory.getAccounts(start, end); - } - - /*/////////////////////////////////////////////////////////////// - Test: performing a contract call - //////////////////////////////////////////////////////////////*/ - - function _setup_executeTransaction() internal { - bytes memory initCallData = abi.encodeWithSignature("createAccount(address,bytes)", accountAdmin, bytes("")); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecute( - accountAdminPKey, - initCode, - address(0), - 0, - bytes("") - ); - - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - } - - /// @dev Perform a state changing transaction directly via account. - function test_state_executeTransaction() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(numberContract.num(), 0); - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).execute( - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - assertEq(numberContract.num(), 42); - } - - /// @dev Perform many state changing transactions in a batch directly via account. - function test_state_executeBatchTransaction() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(numberContract.num(), 0); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 0; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).executeBatch(targets, values, callData); - - assertEq(numberContract.num(), count); - } - - /// @dev Perform a state changing transaction via Entrypoint. - function test_state_executeTransaction_viaEntrypoint() public { - _setup_executeTransaction(); - - assertEq(numberContract.num(), 0); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountAdminPKey, - bytes(""), - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - assertEq(numberContract.num(), 42); - } - - /// @dev Perform many state changing transactions in a batch via Entrypoint. - function test_state_executeBatchTransaction_viaEntrypoint() public { - _setup_executeTransaction(); - - assertEq(numberContract.num(), 0); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 0; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - PackedUserOperation[] memory userOp = _setupUserOpExecuteBatch( - accountAdminPKey, - bytes(""), - targets, - values, - callData - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - assertEq(numberContract.num(), count); - } - - /// @dev Perform many state changing transactions in a batch via Entrypoint. - function test_state_executeBatchTransaction_viaAccountSigner() public { - _setup_executeTransaction(); - - assertEq(numberContract.num(), 0); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 0; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - PackedUserOperation[] memory userOp = _setupUserOpExecuteBatch( - accountSignerPKey, - bytes(""), - targets, - values, - callData - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - assertEq(numberContract.num(), count); - } - - /// @dev Perform a state changing transaction via Entrypoint and a SIGNER_ROLE holder. - function test_state_executeTransaction_viaAccountSigner() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - assertEq(numberContract.num(), 0); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountSignerPKey, - bytes(""), - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - assertEq(numberContract.num(), 42); - } - - /// @dev Revert: perform a state changing transaction via Entrypoint without appropriate permissions. - function test_revert_executeTransaction_nonSigner_viaEntrypoint() public { - _setup_executeTransaction(); - - assertEq(numberContract.num(), 0); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountSignerPKey, - bytes(""), - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - vm.expectRevert(); - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - } - - /// @dev Revert: non-admin performs a state changing transaction directly via account contract. - function test_revert_executeTransaction_nonSigner_viaDirectCall() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - assertEq(numberContract.num(), 0); - - vm.prank(accountSigner); - vm.expectRevert("Account: not admin or EntryPoint."); - SimpleAccount(payable(account)).execute( - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - } - - /*/////////////////////////////////////////////////////////////// - Test: receiving and sending native tokens - //////////////////////////////////////////////////////////////*/ - - /// @dev Send native tokens to an account. - function test_state_accountReceivesNativeTokens() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(address(account).balance, 0); - - vm.prank(accountAdmin); - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory data) = payable(account).call{ value: 1000 }(""); - - // Silence warning: Return value of low-level calls not used. - (success, data) = (success, data); - - assertEq(address(account).balance, 1000); - } - - /// @dev Transfer native tokens out of an account. - function test_state_transferOutsNativeTokens() public { - _setup_executeTransaction(); - - uint256 value = 1000; - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - vm.prank(accountAdmin); - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory data) = payable(account).call{ value: value }(""); - assertEq(address(account).balance, value); - - // Silence warning: Return value of low-level calls not used. - (success, data) = (success, data); - - address recipient = address(0x3456); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountAdminPKey, - bytes(""), - recipient, - value, - bytes("") - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - assertEq(address(account).balance, 0); - assertEq(recipient.balance, value); - } - - /// @dev Add and remove a deposit for the account from the Entrypoint. - - function test_state_addAndWithdrawDeposit() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(EntryPoint(entrypoint).balanceOf(account), 0); - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).addDeposit{ value: 1000 }(); - assertEq(EntryPoint(entrypoint).balanceOf(account), 1000); - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).withdrawDepositTo(payable(accountSigner), 500); - assertEq(EntryPoint(entrypoint).balanceOf(account), 500); - } - - /*/////////////////////////////////////////////////////////////// - Test: receiving ERC-721 and ERC-1155 NFTs - //////////////////////////////////////////////////////////////*/ - - /// @dev Send an ERC-721 NFT to an account. - function test_state_receiveERC721NFT() public { - _setup_executeTransaction(); - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(erc721.balanceOf(account), 0); - - erc721.mint(account, 1); - - assertEq(erc721.balanceOf(account), 1); - } - - /// @dev Send an ERC-1155 NFT to an account. - function test_state_receiveERC1155NFT() public { - _setup_executeTransaction(); - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(erc1155.balanceOf(account, 0), 0); - - erc1155.mint(account, 0, 1); - - assertEq(erc1155.balanceOf(account, 0), 1); - } - - /*/////////////////////////////////////////////////////////////// - Test: setting contract metadata - //////////////////////////////////////////////////////////////*/ - - /// @dev Set contract metadata via admin or entrypoint. - function test_state_contractMetadata() public { - _setup_executeTransaction(); - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).setContractURI("https://example.com"); - assertEq(SimpleAccount(payable(account)).contractURI(), "https://example.com"); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountAdminPKey, - bytes(""), - address(account), - 0, - abi.encodeWithSignature("setContractURI(string)", "https://thirdweb.com") - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - assertEq(SimpleAccount(payable(account)).contractURI(), "https://thirdweb.com"); - - address[] memory approvedTargets = new address[](0); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - PackedUserOperation[] memory userOpViaSigner = _setupUserOpExecute( - accountSignerPKey, - bytes(""), - address(account), - 0, - abi.encodeWithSignature("setContractURI(string)", "https://thirdweb.com") - ); - - vm.expectRevert(); - EntryPoint(entrypoint).handleOps(userOpViaSigner, beneficiary); - } -} diff --git a/src/test/smart-wallet/AccountVulnPOC.t.sol b/src/test/smart-wallet/AccountVulnPOC.t.sol deleted file mode 100644 index fcb85f509..000000000 --- a/src/test/smart-wallet/AccountVulnPOC.t.sol +++ /dev/null @@ -1,320 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test utils -import "../utils/BaseTest.sol"; -// Account Abstraction setup for smart wallets. -import { EntryPoint, IEntryPoint } from "contracts/prebuilts/account/utils/EntryPoint.sol"; -import { PackedUserOperation } from "contracts/prebuilts/account/interfaces/PackedUserOperation.sol"; -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -// Target -import { IAccountPermissions } from "contracts/extension/interface/IAccountPermissions.sol"; -import { AccountFactory, Account as SimpleAccount } from "contracts/prebuilts/account/non-upgradeable/AccountFactory.sol"; -import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - -library GPv2EIP1271 { - bytes4 internal constant MAGICVALUE = 0x1626ba7e; -} - -interface EIP1271Verifier { - function isValidSignature(bytes32 _hash, bytes memory _signature) external view returns (bytes4 magicValue); -} - -/// @dev This is a dummy contract to test contract interactions with Account. -contract Number { - uint256 public num; - - function setNum(uint256 _num) public { - num = _num; - } - - function doubleNum() public { - num *= 2; - } - - function incrementNum() public { - num += 1; - } - - function setNumBySignature(address owner, uint256 newNum, bytes calldata signature) public { - if (owner.code.length == 0) { - // Signature verification by ECDSA - } else { - // Signature verification by EIP1271 - bytes32 digest = keccak256(abi.encode(newNum)); - require( - EIP1271Verifier(owner).isValidSignature(digest, signature) == GPv2EIP1271.MAGICVALUE, - "GPv2: invalid eip1271 signature" - ); - num = newNum; - } - } -} - -contract SimpleAccountVulnPOCTest is BaseTest { - // Target contracts - EntryPoint private entrypoint; - AccountFactory private accountFactory; - - // Mocks - Number internal numberContract; - - // Test params - uint256 private accountAdminPKey = 100; - address private accountAdmin; - - uint256 private accountSignerPKey = 200; - address private accountSigner; - - uint256 private nonSignerPKey = 300; - address private nonSigner; - - // UserOp terminology: `sender` is the smart wallet. - address private sender = 0x0df2C3523703d165Aa7fA1a552f3F0B56275DfC6; - address payable private beneficiary = payable(address(0x45654)); - - bytes32 private uidCache = bytes32("random uid"); - - event AccountCreated(address indexed account, address indexed accountAdmin); - - function _prepareSignature( - IAccountPermissions.SignerPermissionRequest memory _req - ) internal view returns (bytes32 typedDataHash) { - bytes32 typehashSignerPermissionRequest = keccak256( - "SignerPermissionRequest(address signer,uint8 isAdmin,address[] approvedTargets,uint256 nativeTokenLimitPerTransaction,uint128 permissionStartTimestamp,uint128 permissionEndTimestamp,uint128 reqValidityStartTimestamp,uint128 reqValidityEndTimestamp,bytes32 uid)" - ); - bytes32 nameHash = keccak256(bytes("Account")); - bytes32 versionHash = keccak256(bytes("1")); - bytes32 typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - bytes32 domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, sender)); - - bytes memory encodedRequestStart = abi.encode( - typehashSignerPermissionRequest, - _req.signer, - _req.isAdmin, - keccak256(abi.encodePacked(_req.approvedTargets)), - _req.nativeTokenLimitPerTransaction - ); - - bytes memory encodedRequestEnd = abi.encode( - _req.permissionStartTimestamp, - _req.permissionEndTimestamp, - _req.reqValidityStartTimestamp, - _req.reqValidityEndTimestamp, - _req.uid - ); - - bytes32 structHash = keccak256(bytes.concat(encodedRequestStart, encodedRequestEnd)); - typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - } - - function _signSignerPermissionRequest( - IAccountPermissions.SignerPermissionRequest memory _req - ) internal view returns (bytes memory signature) { - bytes32 typedDataHash = _prepareSignature(_req); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, typedDataHash); - signature = abi.encodePacked(r, s, v); - } - - function _setupUserOp( - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (PackedUserOperation[] memory ops) { - uint256 nonce = entrypoint.getNonce(sender, 0); - PackedUserOperation memory op; - - { - uint128 verificationGasLimit = 500_000; - uint128 callGasLimit = 500_000; - bytes32 packedGasLimits = (bytes32(uint256(verificationGasLimit)) << 128) | bytes32(uint256(callGasLimit)); - - // Get user op fields - op = PackedUserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - accountGasLimits: packedGasLimits, - preVerificationGas: 500_000, - gasFees: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - } - - // Sign UserOp - bytes32 opHash = EntryPoint(entrypoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new PackedUserOperation[](1); - ops[0] = op; - } - - function _setupUserOpExecute( - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "execute(address,uint256,bytes)", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - function _setupUserOpExecuteBatch( - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "executeBatch(address[],uint256[],bytes[])", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - function setUp() public override { - super.setUp(); - - // Setup signers. - accountAdmin = vm.addr(accountAdminPKey); - vm.deal(accountAdmin, 100 ether); - - accountSigner = vm.addr(accountSignerPKey); - nonSigner = vm.addr(nonSignerPKey); - - // Setup contracts - entrypoint = new EntryPoint(); - // deploy account factory - accountFactory = new AccountFactory(deployer, IEntryPoint(payable(address(entrypoint)))); - // deploy dummy contract - numberContract = new Number(); - } - - /*////////////////////////////////////////////////////////// - Test: performing a contract call - //////////////////////////////////////////////////////////////*/ - - function _setup_executeTransaction() internal { - bytes memory initCallData = abi.encodeWithSignature("createAccount(address,bytes)", accountAdmin, bytes("")); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecute( - accountAdminPKey, - initCode, - address(0), - 0, - bytes("") - ); - - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - } - - function test_POC() public { - _setup_executeTransaction(); - - /*////////////////////////////////////////////////////////// - Setup - //////////////////////////////////////////////////////////////*/ - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(0x123); // allowing accountSigner permissions for some random contract, consider it as 0 address here - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - IAccountPermissions(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - // As expected, Account Signer is not be able to call setNum on numberContract since it doesnt have numberContract as approved target - assertEq(numberContract.num(), 0); - - vm.prank(accountSigner); - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountSignerPKey, - bytes(""), - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - vm.expectRevert(); - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - /*////////////////////////////////////////////////////////// - Attack - //////////////////////////////////////////////////////////////*/ - - // However they can bypass this by using signature verification on number contract instead - vm.prank(accountSigner); - bytes32 digest = keccak256(abi.encode(42)); - bytes32 toSign = SimpleAccount(payable(account)).getMessageHash(digest); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountSignerPKey, toSign); - bytes memory signature = abi.encodePacked(r, s, v); - - vm.expectRevert("Account: caller not approved target."); - numberContract.setNumBySignature(account, 42, signature); - assertEq(numberContract.num(), 0); - - // Signer can perform transaction if target is approved. - address[] memory newApprovedTargets = new address[](2); - newApprovedTargets[0] = address(0x123); // allowing accountSigner permissions for some random contract, consider it as 0 address here - newApprovedTargets[1] = address(numberContract); - - IAccountPermissions.SignerPermissionRequest memory updatedPermissionsReq = IAccountPermissions - .SignerPermissionRequest( - accountSigner, - 0, - newApprovedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - bytes32("another UID") - ); - - vm.prank(accountAdmin); - bytes memory sig2 = _signSignerPermissionRequest(updatedPermissionsReq); - IAccountPermissions(payable(account)).setPermissionsForSigner(updatedPermissionsReq, sig2); - - numberContract.setNumBySignature(account, 42, signature); - assertEq(numberContract.num(), 42); - } -} diff --git a/src/test/smart-wallet/DynamicAccount.t.sol b/src/test/smart-wallet/DynamicAccount.t.sol deleted file mode 100644 index fce57c9cc..000000000 --- a/src/test/smart-wallet/DynamicAccount.t.sol +++ /dev/null @@ -1,867 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test utils -import "../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; -import { IAccountPermissions } from "contracts/extension/interface/IAccountPermissions.sol"; -import { AccountPermissions } from "contracts/extension/upgradeable/AccountPermissions.sol"; -import { AccountExtension } from "contracts/prebuilts/account/utils/AccountExtension.sol"; - -// Account Abstraction setup for smart wallets. -import { EntryPoint, IEntryPoint } from "contracts/prebuilts/account/utils/EntryPoint.sol"; -import { PackedUserOperation } from "contracts/prebuilts/account/interfaces/PackedUserOperation.sol"; - -// Target -import { Account as SimpleAccount } from "contracts/prebuilts/account/non-upgradeable/Account.sol"; -import { DynamicAccountFactory, DynamicAccount } from "contracts/prebuilts/account/dynamic/DynamicAccountFactory.sol"; -import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - -/// @dev This is a dummy contract to test contract interactions with Account. -contract Number { - uint256 public num; - - function setNum(uint256 _num) public { - num = _num; - } - - function doubleNum() public { - num *= 2; - } - - function incrementNum() public { - num += 1; - } -} - -contract NFTRejector { - function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) { - revert("NFTs not accepted"); - } -} - -contract DynamicAccountTest is BaseTest { - // Target contracts - EntryPoint private constant entrypoint = EntryPoint(payable(0x0000000071727De22E5E9d8BAf0edAc6f37da032)); - DynamicAccountFactory private accountFactory; - - // Mocks - Number internal numberContract; - - // Test params - uint256 private accountAdminPKey = 100; - address private accountAdmin; - - uint256 private accountSignerPKey = 200; - address private accountSigner; - - uint256 private nonSignerPKey = 300; - address private nonSigner; - - bytes internal data = bytes(""); - - // UserOp terminology: `sender` is the smart wallet. - address private sender = 0x78b942FBC9126b4Ed8384Bb9dd1420Ea865be91a; - address payable private beneficiary = payable(address(0x45654)); - - bytes32 private uidCache = bytes32("random uid"); - - event AccountCreated(address indexed account, address indexed accountAdmin); - - function _prepareSignature( - IAccountPermissions.SignerPermissionRequest memory _req - ) internal view returns (bytes32 typedDataHash) { - bytes32 typehashSignerPermissionRequest = keccak256( - "SignerPermissionRequest(address signer,uint8 isAdmin,address[] approvedTargets,uint256 nativeTokenLimitPerTransaction,uint128 permissionStartTimestamp,uint128 permissionEndTimestamp,uint128 reqValidityStartTimestamp,uint128 reqValidityEndTimestamp,bytes32 uid)" - ); - bytes32 nameHash = keccak256(bytes("Account")); - bytes32 versionHash = keccak256(bytes("1")); - bytes32 typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - bytes32 domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, sender)); - - bytes memory encodedRequestStart = abi.encode( - typehashSignerPermissionRequest, - _req.signer, - _req.isAdmin, - keccak256(abi.encodePacked(_req.approvedTargets)), - _req.nativeTokenLimitPerTransaction - ); - - bytes memory encodedRequestEnd = abi.encode( - _req.permissionStartTimestamp, - _req.permissionEndTimestamp, - _req.reqValidityStartTimestamp, - _req.reqValidityEndTimestamp, - _req.uid - ); - - bytes32 structHash = keccak256(bytes.concat(encodedRequestStart, encodedRequestEnd)); - typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - } - - function _signSignerPermissionRequest( - IAccountPermissions.SignerPermissionRequest memory _req - ) internal view returns (bytes memory signature) { - bytes32 typedDataHash = _prepareSignature(_req); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, typedDataHash); - signature = abi.encodePacked(r, s, v); - } - - function _setupUserOp( - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (PackedUserOperation[] memory ops) { - uint256 nonce = entrypoint.getNonce(sender, 0); - PackedUserOperation memory op; - - { - uint128 verificationGasLimit = 5_000_000; - uint128 callGasLimit = 5_000_000; - bytes32 packedGasLimits = (bytes32(uint256(verificationGasLimit)) << 128) | bytes32(uint256(callGasLimit)); - - // Get user op fields - op = PackedUserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - accountGasLimits: packedGasLimits, - preVerificationGas: 5_000_000, - gasFees: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - } - - // Sign UserOp - bytes32 opHash = EntryPoint(entrypoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new PackedUserOperation[](1); - ops[0] = op; - } - - function _setupUserOpWithSender( - bytes memory _initCode, - bytes memory _callDataForEntrypoint, - address _sender - ) internal returns (PackedUserOperation[] memory ops) { - uint256 nonce = entrypoint.getNonce(_sender, 0); - PackedUserOperation memory op; - - { - uint128 verificationGasLimit = 5_000_000; - uint128 callGasLimit = 5_000_000; - bytes32 packedGasLimits = (bytes32(uint256(verificationGasLimit)) << 128) | bytes32(uint256(callGasLimit)); - - // Get user op fields - op = PackedUserOperation({ - sender: _sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - accountGasLimits: packedGasLimits, - preVerificationGas: 5_000_000, - gasFees: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - } - - // Sign UserOp - bytes32 opHash = EntryPoint(entrypoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(accountAdminPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new PackedUserOperation[](1); - ops[0] = op; - } - - function _setupUserOpExecuteWithSender( - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData, - address _sender - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "execute(address,uint256,bytes)", - _target, - _value, - _callData - ); - - return _setupUserOpWithSender(_initCode, callDataForEntrypoint, _sender); - } - - function _setupUserOpExecute( - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "execute(address,uint256,bytes)", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - function _setupUserOpExecuteBatch( - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "executeBatch(address[],uint256[],bytes[])", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - /// @dev Returns the salt used when deploying an Account. - function _generateSalt(address _admin, bytes memory _data) internal view virtual returns (bytes32) { - return keccak256(abi.encode(_admin, _data)); - } - - function setUp() public override { - super.setUp(); - - // Setup signers. - accountAdmin = vm.addr(accountAdminPKey); - vm.deal(accountAdmin, 100 ether); - - accountSigner = vm.addr(accountSignerPKey); - nonSigner = vm.addr(nonSignerPKey); - - // Setup contracts - address _deployedEntrypoint = address(new EntryPoint()); - vm.etch(address(entrypoint), bytes(_deployedEntrypoint.code)); - - // Setting up default extension. - IExtension.Extension memory defaultExtension; - - defaultExtension.metadata = IExtension.ExtensionMetadata({ - name: "AccountExtension", - metadataURI: "ipfs://AccountExtension", - implementation: address(new AccountExtension()) - }); - - defaultExtension.functions = new IExtension.ExtensionFunction[](9); - - defaultExtension.functions[0] = IExtension.ExtensionFunction( - AccountExtension.supportsInterface.selector, - "supportsInterface(bytes4)" - ); - defaultExtension.functions[1] = IExtension.ExtensionFunction( - AccountExtension.execute.selector, - "execute(address,uint256,bytes)" - ); - defaultExtension.functions[2] = IExtension.ExtensionFunction( - AccountExtension.executeBatch.selector, - "executeBatch(address[],uint256[],bytes[])" - ); - defaultExtension.functions[3] = IExtension.ExtensionFunction( - ERC721Holder.onERC721Received.selector, - "onERC721Received(address,address,uint256,bytes)" - ); - defaultExtension.functions[4] = IExtension.ExtensionFunction( - ERC1155Holder.onERC1155Received.selector, - "onERC1155Received(address,address,uint256,uint256,bytes)" - ); - defaultExtension.functions[5] = IExtension.ExtensionFunction( - bytes4(0), // Selector for `receive()` function. - "receive()" - ); - defaultExtension.functions[6] = IExtension.ExtensionFunction( - AccountExtension.isValidSignature.selector, - "isValidSignature(bytes32,bytes)" - ); - defaultExtension.functions[7] = IExtension.ExtensionFunction( - AccountExtension.addDeposit.selector, - "addDeposit()" - ); - defaultExtension.functions[8] = IExtension.ExtensionFunction( - AccountExtension.withdrawDepositTo.selector, - "withdrawDepositTo(address,uint256)" - ); - - IExtension.Extension[] memory extensions = new IExtension.Extension[](1); - extensions[0] = defaultExtension; - - // deploy account factory - accountFactory = new DynamicAccountFactory(deployer, extensions); - // deploy dummy contract - numberContract = new Number(); - } - - /*/////////////////////////////////////////////////////////////// - Test: creating an account - //////////////////////////////////////////////////////////////*/ - - /// @dev benchmark test for deployment gas cost - function test_deploy_dynamicAccount() public { - // Setting up default extension. - IExtension.Extension memory defaultExtension; - - defaultExtension.metadata = IExtension.ExtensionMetadata({ - name: "AccountExtension", - metadataURI: "ipfs://AccountExtension", - implementation: address(new AccountExtension()) - }); - - defaultExtension.functions = new IExtension.ExtensionFunction[](7); - - defaultExtension.functions[0] = IExtension.ExtensionFunction( - AccountExtension.supportsInterface.selector, - "supportsInterface(bytes4)" - ); - defaultExtension.functions[1] = IExtension.ExtensionFunction( - AccountExtension.execute.selector, - "execute(address,uint256,bytes)" - ); - defaultExtension.functions[2] = IExtension.ExtensionFunction( - AccountExtension.executeBatch.selector, - "executeBatch(address[],uint256[],bytes[])" - ); - defaultExtension.functions[3] = IExtension.ExtensionFunction( - ERC721Holder.onERC721Received.selector, - "onERC721Received(address,address,uint256,bytes)" - ); - defaultExtension.functions[4] = IExtension.ExtensionFunction( - ERC1155Holder.onERC1155Received.selector, - "onERC1155Received(address,address,uint256,uint256,bytes)" - ); - defaultExtension.functions[5] = IExtension.ExtensionFunction( - bytes4(0), // Selector for `receive()` function. - "receive()" - ); - defaultExtension.functions[6] = IExtension.ExtensionFunction( - AccountExtension.isValidSignature.selector, - "isValidSignature(bytes32,bytes)" - ); - - IExtension.Extension[] memory extensions = new IExtension.Extension[](1); - extensions[0] = defaultExtension; - - // deploy account factory - DynamicAccountFactory factory = new DynamicAccountFactory(deployer, extensions); - } - - /// @dev Create an account by directly calling the factory. - function test_state_createAccount_viaFactory() public { - vm.expectEmit(true, true, false, true); - emit AccountCreated(sender, accountAdmin); - accountFactory.createAccount(accountAdmin, data); - - address[] memory allAccounts = accountFactory.getAllAccounts(); - assertEq(allAccounts.length, 1); - assertEq(allAccounts[0], sender); - } - - /// @dev Create an account via Entrypoint. - function test_state_createAccount_viaEntrypoint() public { - bytes memory initCallData = abi.encodeWithSignature("createAccount(address,bytes)", accountAdmin, data); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecute( - accountAdminPKey, - initCode, - address(0), - 0, - bytes("") - ); - - vm.expectEmit(true, true, false, true); - emit AccountCreated(sender, accountAdmin); - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - - address[] memory allAccounts = accountFactory.getAllAccounts(); - assertEq(allAccounts.length, 1); - assertEq(allAccounts[0], sender); - } - - /// @dev Try registering with factory with a contract not deployed by factory. - function test_revert_onRegister_nonFactoryChildContract() public { - vm.prank(address(0x12345)); - vm.expectRevert("AccountFactory: not an account."); - accountFactory.onRegister(_generateSalt(accountAdmin, "")); - } - - /// @dev Create more than one accounts with the same admin. - function test_state_createAccount_viaEntrypoint_multipleAccountSameAdmin() public { - uint256 amount = 1; - - for (uint256 i = 0; i < amount; i += 1) { - bytes memory initCallData = abi.encodeWithSignature( - "createAccount(address,bytes)", - accountAdmin, - bytes(abi.encode(i)) - ); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - address expectedSenderAddress = Clones.predictDeterministicAddress( - accountFactory.accountImplementation(), - _generateSalt(accountAdmin, bytes(abi.encode(i))), - address(accountFactory) - ); - - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecuteWithSender( - initCode, - address(0), - 0, - bytes(abi.encode(i)), - expectedSenderAddress - ); - - vm.expectEmit(true, true, false, true); - emit AccountCreated(expectedSenderAddress, accountAdmin); - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - } - - address[] memory allAccounts = accountFactory.getAllAccounts(); - assertEq(allAccounts.length, amount); - - for (uint256 i = 0; i < amount; i += 1) { - assertEq( - allAccounts[i], - Clones.predictDeterministicAddress( - accountFactory.accountImplementation(), - _generateSalt(accountAdmin, bytes(abi.encode(i))), - address(accountFactory) - ) - ); - } - } - - /*/////////////////////////////////////////////////////////////// - Test: performing a contract call - //////////////////////////////////////////////////////////////*/ - - function _setup_executeTransaction() internal { - bytes memory initCallData = abi.encodeWithSignature("createAccount(address,bytes)", accountAdmin, data); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecute( - accountAdminPKey, - initCode, - address(0), - 0, - bytes("") - ); - - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - } - - /// @dev Perform a state changing transaction directly via account. - function test_state_executeTransaction() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(numberContract.num(), 0); - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).execute( - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - assertEq(numberContract.num(), 42); - } - - /// @dev Perform many state changing transactions in a batch directly via account. - function test_state_executeBatchTransaction() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(numberContract.num(), 0); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 0; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).executeBatch(targets, values, callData); - - assertEq(numberContract.num(), count); - } - - /// @dev Perform a state changing transaction via Entrypoint. - function test_state_executeTransaction_viaEntrypoint() public { - _setup_executeTransaction(); - - assertEq(numberContract.num(), 0); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountAdminPKey, - bytes(""), - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - assertEq(numberContract.num(), 42); - } - - /// @dev Perform many state changing transactions in a batch via Entrypoint. - function test_state_executeBatchTransaction_viaEntrypoint() public { - _setup_executeTransaction(); - - assertEq(numberContract.num(), 0); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 0; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - PackedUserOperation[] memory userOp = _setupUserOpExecuteBatch( - accountAdminPKey, - bytes(""), - targets, - values, - callData - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - assertEq(numberContract.num(), count); - } - - /// @dev Perform many state changing transactions in a batch via Entrypoint. - function test_state_executeBatchTransaction_viaAccountSigner() public { - _setup_executeTransaction(); - - assertEq(numberContract.num(), 0); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 0; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - PackedUserOperation[] memory userOp = _setupUserOpExecuteBatch( - accountSignerPKey, - bytes(""), - targets, - values, - callData - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - assertEq(numberContract.num(), count); - } - - /// @dev Perform a state changing transaction via Entrypoint and a SIGNER_ROLE holder. - function test_state_executeTransaction_viaAccountSigner() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - assertEq(numberContract.num(), 0); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountSignerPKey, - bytes(""), - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - assertEq(numberContract.num(), 42); - } - - /// @dev Revert: perform a state changing transaction via Entrypoint without appropriate permissions. - function test_revert_executeTransaction_nonSigner_viaEntrypoint() public { - _setup_executeTransaction(); - - assertEq(numberContract.num(), 0); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountSignerPKey, - bytes(""), - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - vm.expectRevert(); - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - } - - /// @dev Revert: non-admin performs a state changing transaction directly via account contract. - function test_revert_executeTransaction_nonSigner_viaDirectCall() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - assertEq(numberContract.num(), 0); - - vm.prank(accountSigner); - vm.expectRevert("Account: not admin or EntryPoint."); - SimpleAccount(payable(account)).execute( - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - } - - /*/////////////////////////////////////////////////////////////// - Test: receiving and sending native tokens - //////////////////////////////////////////////////////////////*/ - - /// @dev Send native tokens to an account. - function test_state_accountReceivesNativeTokens() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(address(account).balance, 0); - - vm.prank(accountAdmin); - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory ret) = payable(account).call{ value: 1000 }(""); - - // Silence warning: Return value of low-level calls not used. - (success, ret) = (success, ret); - - assertEq(address(account).balance, 1000); - } - - /// @dev Transfer native tokens out of an account. - function test_state_transferOutsNativeTokens() public { - _setup_executeTransaction(); - - uint256 value = 1000; - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - vm.prank(accountAdmin); - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory ret) = payable(account).call{ value: value }(""); - assertEq(address(account).balance, value); - - // Silence warning: Return value of low-level calls not used. - (success, ret) = (success, ret); - - address recipient = address(0x3456); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountAdminPKey, - bytes(""), - recipient, - value, - bytes("") - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - assertEq(address(account).balance, 0); - assertEq(recipient.balance, value); - } - - /// @dev Add and remove a deposit for the account from the Entrypoint. - - function test_state_addAndWithdrawDeposit() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(EntryPoint(entrypoint).balanceOf(account), 0); - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).addDeposit{ value: 1000 }(); - assertEq(EntryPoint(entrypoint).balanceOf(account), 1000); - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).withdrawDepositTo(payable(accountSigner), 500); - assertEq(EntryPoint(entrypoint).balanceOf(account), 500); - } - - /*/////////////////////////////////////////////////////////////// - Test: receiving ERC-721 and ERC-1155 NFTs - //////////////////////////////////////////////////////////////*/ - - /// @dev Send an ERC-721 NFT to an account. - function test_state_receiveERC721NFT() public { - _setup_executeTransaction(); - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(erc721.balanceOf(account), 0); - - erc721.mint(account, 1); - - assertEq(erc721.balanceOf(account), 1); - } - - /// @dev Send an ERC-1155 NFT to an account. - function test_state_receiveERC1155NFT() public { - _setup_executeTransaction(); - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(erc1155.balanceOf(account, 0), 0); - - erc1155.mint(account, 0, 1); - - assertEq(erc1155.balanceOf(account, 0), 1); - } - - /*/////////////////////////////////////////////////////////////// - Test: change an extension on the account - //////////////////////////////////////////////////////////////*/ - - /// @dev Make the account reject ERC-721 NFTs instead of accepting them. - function test_scenario_changeExtensionForFunction() public { - _setup_executeTransaction(); - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - // The account can initially receive NFTs. - assertEq(erc721.balanceOf(account), 0); - erc721.mint(account, 1); - assertEq(erc721.balanceOf(account), 1); - - // Make the account reject ERC-721 NFTs going forward. - IExtension.Extension memory extension; - - extension.metadata = IExtension.ExtensionMetadata({ - name: "NFTRejector", - metadataURI: "ipfs://NFTRejector", - implementation: address(new NFTRejector()) - }); - - extension.functions = new IExtension.ExtensionFunction[](1); - - extension.functions[0] = IExtension.ExtensionFunction( - NFTRejector.onERC721Received.selector, - "onERC721Received(address,address,uint256,bytes)" - ); - - vm.prank(accountAdmin); - DynamicAccount(payable(account)).disableFunctionInExtension( - "AccountExtension", - NFTRejector.onERC721Received.selector - ); - - vm.prank(accountAdmin); - DynamicAccount(payable(account)).addExtension(extension); - - // Transfer NFTs to the account - erc721.mint(accountSigner, 1); - assertEq(erc721.ownerOf(1), accountSigner); - vm.prank(accountSigner); - vm.expectRevert("NFTs not accepted"); - erc721.safeTransferFrom(accountSigner, account, 1); - } -} diff --git a/src/test/smart-wallet/ManagedAccount.t.sol b/src/test/smart-wallet/ManagedAccount.t.sol deleted file mode 100644 index 5ae76968a..000000000 --- a/src/test/smart-wallet/ManagedAccount.t.sol +++ /dev/null @@ -1,876 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test utils -import "../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; -import { IAccountPermissions } from "contracts/extension/interface/IAccountPermissions.sol"; -import { AccountPermissions } from "contracts/extension/upgradeable/AccountPermissions.sol"; -import { AccountExtension } from "contracts/prebuilts/account/utils/AccountExtension.sol"; - -// Account Abstraction setup for smart wallets. -import { EntryPoint, IEntryPoint } from "contracts/prebuilts/account/utils/EntryPoint.sol"; -import { PackedUserOperation } from "contracts/prebuilts/account/interfaces/PackedUserOperation.sol"; - -// Target -import { Account as SimpleAccount } from "contracts/prebuilts/account/non-upgradeable/Account.sol"; -import { ManagedAccountFactory, ManagedAccount } from "contracts/prebuilts/account/managed/ManagedAccountFactory.sol"; -import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - -/// @dev This is a dummy contract to test contract interactions with Account. -contract Number { - uint256 public num; - - function setNum(uint256 _num) public { - num = _num; - } - - function doubleNum() public { - num *= 2; - } - - function incrementNum() public { - num += 1; - } -} - -contract NFTRejector { - function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) { - revert("NFTs not accepted"); - } -} - -contract ManagedAccountTest is BaseTest { - // Target contracts - EntryPoint private entrypoint; - ManagedAccountFactory private accountFactory; - - // Mocks - Number internal numberContract; - - // Test params - address private factoryDeployer = address(0x9876); - bytes internal data = bytes(""); - - uint256 private accountAdminPKey = 100; - address private accountAdmin; - - uint256 private accountSignerPKey = 200; - address private accountSigner; - - uint256 private nonSignerPKey = 300; - address private nonSigner; - - // UserOp terminology: `sender` is the smart wallet. - address private sender = 0xbEA1Fa134A1727187A8f2e7E714B660f3a95478D; - address payable private beneficiary = payable(address(0x45654)); - - bytes32 private uidCache = bytes32("random uid"); - - event AccountCreated(address indexed account, address indexed accountAdmin); - - function _prepareSignature( - IAccountPermissions.SignerPermissionRequest memory _req - ) internal view returns (bytes32 typedDataHash) { - bytes32 typehashSignerPermissionRequest = keccak256( - "SignerPermissionRequest(address signer,uint8 isAdmin,address[] approvedTargets,uint256 nativeTokenLimitPerTransaction,uint128 permissionStartTimestamp,uint128 permissionEndTimestamp,uint128 reqValidityStartTimestamp,uint128 reqValidityEndTimestamp,bytes32 uid)" - ); - bytes32 nameHash = keccak256(bytes("Account")); - bytes32 versionHash = keccak256(bytes("1")); - bytes32 typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - bytes32 domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, sender)); - - bytes memory encodedRequestStart = abi.encode( - typehashSignerPermissionRequest, - _req.signer, - _req.isAdmin, - keccak256(abi.encodePacked(_req.approvedTargets)), - _req.nativeTokenLimitPerTransaction - ); - - bytes memory encodedRequestEnd = abi.encode( - _req.permissionStartTimestamp, - _req.permissionEndTimestamp, - _req.reqValidityStartTimestamp, - _req.reqValidityEndTimestamp, - _req.uid - ); - - bytes32 structHash = keccak256(bytes.concat(encodedRequestStart, encodedRequestEnd)); - typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - } - - function _signSignerPermissionRequest( - IAccountPermissions.SignerPermissionRequest memory _req - ) internal view returns (bytes memory signature) { - bytes32 typedDataHash = _prepareSignature(_req); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, typedDataHash); - signature = abi.encodePacked(r, s, v); - } - - function _setupUserOp( - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (PackedUserOperation[] memory ops) { - uint256 nonce = entrypoint.getNonce(sender, 0); - PackedUserOperation memory op; - - { - uint128 verificationGasLimit = 500_000; - uint128 callGasLimit = 500_000; - bytes32 packedGasLimits = (bytes32(uint256(verificationGasLimit)) << 128) | bytes32(uint256(callGasLimit)); - - // Get user op fields - op = PackedUserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - accountGasLimits: packedGasLimits, - preVerificationGas: 500_000, - gasFees: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - } - - // Sign UserOp - bytes32 opHash = EntryPoint(entrypoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new PackedUserOperation[](1); - ops[0] = op; - } - - function _setupUserOpWithSender( - bytes memory _initCode, - bytes memory _callDataForEntrypoint, - address _sender - ) internal returns (PackedUserOperation[] memory ops) { - uint256 nonce = entrypoint.getNonce(_sender, 0); - PackedUserOperation memory op; - - { - uint128 verificationGasLimit = 5_000_000; - uint128 callGasLimit = 5_000_000; - bytes32 packedGasLimits = (bytes32(uint256(verificationGasLimit)) << 128) | bytes32(uint256(callGasLimit)); - - // Get user op fields - op = PackedUserOperation({ - sender: _sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - accountGasLimits: packedGasLimits, - preVerificationGas: 5_000_000, - gasFees: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - } - - // Sign UserOp - bytes32 opHash = EntryPoint(entrypoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(accountAdminPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new PackedUserOperation[](1); - ops[0] = op; - } - - function _setupUserOpExecuteWithSender( - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData, - address _sender - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "execute(address,uint256,bytes)", - _target, - _value, - _callData - ); - - return _setupUserOpWithSender(_initCode, callDataForEntrypoint, _sender); - } - - function _setupUserOpExecute( - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "execute(address,uint256,bytes)", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - function _setupUserOpExecuteBatch( - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "executeBatch(address[],uint256[],bytes[])", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - /// @dev Returns the salt used when deploying an Account. - function _generateSalt(address _admin, bytes memory _data) internal view virtual returns (bytes32) { - return keccak256(abi.encode(_admin, _data)); - } - - function setUp() public override { - super.setUp(); - - // Setup signers. - accountAdmin = vm.addr(accountAdminPKey); - vm.deal(accountAdmin, 100 ether); - - accountSigner = vm.addr(accountSignerPKey); - nonSigner = vm.addr(nonSignerPKey); - - // Setup contracts - entrypoint = new EntryPoint(); - - // Setting up default extension. - IExtension.Extension memory defaultExtension; - - defaultExtension.metadata = IExtension.ExtensionMetadata({ - name: "AccountExtension", - metadataURI: "ipfs://AccountExtension", - implementation: address(new AccountExtension()) - }); - - defaultExtension.functions = new IExtension.ExtensionFunction[](9); - - defaultExtension.functions[0] = IExtension.ExtensionFunction( - AccountExtension.supportsInterface.selector, - "supportsInterface(bytes4)" - ); - defaultExtension.functions[1] = IExtension.ExtensionFunction( - AccountExtension.execute.selector, - "execute(address,uint256,bytes)" - ); - defaultExtension.functions[2] = IExtension.ExtensionFunction( - AccountExtension.executeBatch.selector, - "executeBatch(address[],uint256[],bytes[])" - ); - defaultExtension.functions[3] = IExtension.ExtensionFunction( - ERC721Holder.onERC721Received.selector, - "onERC721Received(address,address,uint256,bytes)" - ); - defaultExtension.functions[4] = IExtension.ExtensionFunction( - ERC1155Holder.onERC1155Received.selector, - "onERC1155Received(address,address,uint256,uint256,bytes)" - ); - defaultExtension.functions[5] = IExtension.ExtensionFunction( - bytes4(0), // Selector for `receive()` function. - "receive()" - ); - defaultExtension.functions[6] = IExtension.ExtensionFunction( - AccountExtension.isValidSignature.selector, - "isValidSignature(bytes32,bytes)" - ); - defaultExtension.functions[7] = IExtension.ExtensionFunction( - AccountExtension.addDeposit.selector, - "addDeposit()" - ); - defaultExtension.functions[8] = IExtension.ExtensionFunction( - AccountExtension.withdrawDepositTo.selector, - "withdrawDepositTo(address,uint256)" - ); - - IExtension.Extension[] memory extensions = new IExtension.Extension[](1); - extensions[0] = defaultExtension; - - // deploy account factory - vm.prank(factoryDeployer); - accountFactory = new ManagedAccountFactory( - factoryDeployer, - IEntryPoint(payable(address(entrypoint))), - extensions - ); - // deploy dummy contract - numberContract = new Number(); - } - - /// @dev benchmark test for deployment gas cost - function test_deploy_managedAccount() public { - // Setting up default extension. - IExtension.Extension memory defaultExtension; - - defaultExtension.metadata = IExtension.ExtensionMetadata({ - name: "AccountExtension", - metadataURI: "ipfs://AccountExtension", - implementation: address(new AccountExtension()) - }); - - defaultExtension.functions = new IExtension.ExtensionFunction[](7); - - defaultExtension.functions[0] = IExtension.ExtensionFunction( - AccountExtension.supportsInterface.selector, - "supportsInterface(bytes4)" - ); - defaultExtension.functions[1] = IExtension.ExtensionFunction( - AccountExtension.execute.selector, - "execute(address,uint256,bytes)" - ); - defaultExtension.functions[2] = IExtension.ExtensionFunction( - AccountExtension.executeBatch.selector, - "executeBatch(address[],uint256[],bytes[])" - ); - defaultExtension.functions[3] = IExtension.ExtensionFunction( - ERC721Holder.onERC721Received.selector, - "onERC721Received(address,address,uint256,bytes)" - ); - defaultExtension.functions[4] = IExtension.ExtensionFunction( - ERC1155Holder.onERC1155Received.selector, - "onERC1155Received(address,address,uint256,uint256,bytes)" - ); - defaultExtension.functions[5] = IExtension.ExtensionFunction( - bytes4(0), // Selector for `receive()` function. - "receive()" - ); - defaultExtension.functions[6] = IExtension.ExtensionFunction( - AccountExtension.isValidSignature.selector, - "isValidSignature(bytes32,bytes)" - ); - - IExtension.Extension[] memory extensions = new IExtension.Extension[](1); - extensions[0] = defaultExtension; - - // deploy account factory - vm.prank(factoryDeployer); - ManagedAccountFactory factory = new ManagedAccountFactory( - factoryDeployer, - IEntryPoint(payable(address(entrypoint))), - extensions - ); - assertTrue(address(factory) != address(0), "factory address should not be zero"); - } - - /*/////////////////////////////////////////////////////////////// - Test: creating an account - //////////////////////////////////////////////////////////////*/ - - /// @dev Create an account by directly calling the factory. - function test_state_createAccount_viaFactory() public { - vm.expectEmit(true, true, false, true); - emit AccountCreated(sender, accountAdmin); - accountFactory.createAccount(accountAdmin, data); - - address[] memory allAccounts = accountFactory.getAllAccounts(); - assertEq(allAccounts.length, 1); - assertEq(allAccounts[0], sender); - } - - /// @dev Create an account via Entrypoint. - function test_state_createAccount_viaEntrypoint() public { - bytes memory initCallData = abi.encodeWithSignature("createAccount(address,bytes)", accountAdmin, data); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecute( - accountAdminPKey, - initCode, - address(0), - 0, - bytes("") - ); - - vm.expectEmit(true, true, false, true); - emit AccountCreated(sender, accountAdmin); - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - - address[] memory allAccounts = accountFactory.getAllAccounts(); - assertEq(allAccounts.length, 1); - assertEq(allAccounts[0], sender); - } - - /// @dev Try registering with factory with a contract not deployed by factory. - function test_revert_onRegister_nonFactoryChildContract() public { - vm.prank(address(0x12345)); - vm.expectRevert("AccountFactory: not an account."); - accountFactory.onRegister(_generateSalt(accountAdmin, "")); - } - - /// @dev Create more than one accounts with the same admin. - function test_state_createAccount_viaEntrypoint_multipleAccountSameAdmin() public { - uint256 amount = 1; - - for (uint256 i = 0; i < amount; i += 1) { - bytes memory initCallData = abi.encodeWithSignature( - "createAccount(address,bytes)", - accountAdmin, - bytes(abi.encode(i)) - ); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - address expectedSenderAddress = Clones.predictDeterministicAddress( - accountFactory.accountImplementation(), - _generateSalt(accountAdmin, bytes(abi.encode(i))), - address(accountFactory) - ); - - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecuteWithSender( - initCode, - address(0), - 0, - bytes(abi.encode(i)), - expectedSenderAddress - ); - - vm.expectEmit(true, true, false, true); - emit AccountCreated(expectedSenderAddress, accountAdmin); - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - } - - address[] memory allAccounts = accountFactory.getAllAccounts(); - assertEq(allAccounts.length, amount); - - for (uint256 i = 0; i < amount; i += 1) { - assertEq( - allAccounts[i], - Clones.predictDeterministicAddress( - accountFactory.accountImplementation(), - _generateSalt(accountAdmin, bytes(abi.encode(i))), - address(accountFactory) - ) - ); - } - } - - /*/////////////////////////////////////////////////////////////// - Test: performing a contract call - //////////////////////////////////////////////////////////////*/ - - function _setup_executeTransaction() internal { - bytes memory initCallData = abi.encodeWithSignature("createAccount(address,bytes)", accountAdmin, data); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecute( - accountAdminPKey, - initCode, - address(0), - 0, - bytes("") - ); - - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - } - - /// @dev Perform a state changing transaction directly via account. - function test_state_executeTransaction() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(numberContract.num(), 0); - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).execute( - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - assertEq(numberContract.num(), 42); - } - - /// @dev Perform many state changing transactions in a batch directly via account. - function test_state_executeBatchTransaction() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(numberContract.num(), 0); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 0; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).executeBatch(targets, values, callData); - - assertEq(numberContract.num(), count); - } - - /// @dev Perform a state changing transaction via Entrypoint. - function test_state_executeTransaction_viaEntrypoint() public { - _setup_executeTransaction(); - - assertEq(numberContract.num(), 0); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountAdminPKey, - bytes(""), - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - assertEq(numberContract.num(), 42); - } - - /// @dev Perform many state changing transactions in a batch via Entrypoint. - function test_state_executeBatchTransaction_viaEntrypoint() public { - _setup_executeTransaction(); - - assertEq(numberContract.num(), 0); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 0; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - PackedUserOperation[] memory userOp = _setupUserOpExecuteBatch( - accountAdminPKey, - bytes(""), - targets, - values, - callData - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - assertEq(numberContract.num(), count); - } - - /// @dev Perform a state changing transaction via Entrypoint and a SIGNER_ROLE holder. - function test_state_executeTransaction_viaAccountSigner() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - assertEq(numberContract.num(), 0); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountSignerPKey, - bytes(""), - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - assertEq(numberContract.num(), 42); - } - - /// @dev Revert: perform a state changing transaction via Entrypoint without appropriate permissions. - function test_revert_executeTransaction_nonSigner_viaEntrypoint() public { - _setup_executeTransaction(); - - assertEq(numberContract.num(), 0); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountSignerPKey, - bytes(""), - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - - vm.expectRevert(); - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - } - - /// @dev Perform many state changing transactions in a batch via Entrypoint. - function test_state_executeBatchTransaction_viaAccountSigner() public { - _setup_executeTransaction(); - - assertEq(numberContract.num(), 0); - - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 0; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - PackedUserOperation[] memory userOp = _setupUserOpExecuteBatch( - accountSignerPKey, - bytes(""), - targets, - values, - callData - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - - assertEq(numberContract.num(), count); - } - - /// @dev Revert: non-admin performs a state changing transaction directly via account contract. - function test_revert_executeTransaction_nonSigner_viaDirectCall() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1 ether, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - assertEq(numberContract.num(), 0); - - vm.prank(accountSigner); - vm.expectRevert("Account: not admin or EntryPoint."); - SimpleAccount(payable(account)).execute( - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ); - } - - /*/////////////////////////////////////////////////////////////// - Test: receiving and sending native tokens - //////////////////////////////////////////////////////////////*/ - - /// @dev Send native tokens to an account. - function test_state_accountReceivesNativeTokens() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(address(account).balance, 0); - - vm.prank(accountAdmin); - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory ret) = payable(account).call{ value: 1000 }(""); - - assertEq(address(account).balance, 1000); - - // Silence warning: Return value of low-level calls not used. - (success, ret) = (success, ret); - } - - /// @dev Transfer native tokens out of an account. - function test_state_transferOutsNativeTokens() public { - _setup_executeTransaction(); - - uint256 value = 1000; - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - vm.prank(accountAdmin); - // solhint-disable-next-line avoid-low-level-calls - (bool success, bytes memory ret) = payable(account).call{ value: value }(""); - assertEq(address(account).balance, value); - - // Silence warning: Return value of low-level calls not used. - (success, ret) = (success, ret); - - address recipient = address(0x3456); - - PackedUserOperation[] memory userOp = _setupUserOpExecute( - accountAdminPKey, - bytes(""), - recipient, - value, - bytes("") - ); - - EntryPoint(entrypoint).handleOps(userOp, beneficiary); - assertEq(address(account).balance, 0); - assertEq(recipient.balance, value); - } - - /// @dev Add and remove a deposit for the account from the Entrypoint. - - function test_state_addAndWithdrawDeposit() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(EntryPoint(entrypoint).balanceOf(account), 0); - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).addDeposit{ value: 1000 }(); - assertEq(EntryPoint(entrypoint).balanceOf(account), 1000); - - vm.prank(accountAdmin); - SimpleAccount(payable(account)).withdrawDepositTo(payable(accountSigner), 500); - assertEq(EntryPoint(entrypoint).balanceOf(account), 500); - } - - /*/////////////////////////////////////////////////////////////// - Test: receiving ERC-721 and ERC-1155 NFTs - //////////////////////////////////////////////////////////////*/ - - /// @dev Send an ERC-721 NFT to an account. - function test_state_receiveERC721NFT() public { - _setup_executeTransaction(); - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(erc721.balanceOf(account), 0); - - erc721.mint(account, 1); - - assertEq(erc721.balanceOf(account), 1); - } - - /// @dev Send an ERC-1155 NFT to an account. - function test_state_receiveERC1155NFT() public { - _setup_executeTransaction(); - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - assertEq(erc1155.balanceOf(account, 0), 0); - - erc1155.mint(account, 0, 1); - - assertEq(erc1155.balanceOf(account, 0), 1); - } - - /*/////////////////////////////////////////////////////////////// - Test: change an extension on the account - //////////////////////////////////////////////////////////////*/ - - /// @dev Make the account reject ERC-721 NFTs instead of accepting them. - function test_scenario_changeExtensionForFunction() public { - _setup_executeTransaction(); - address account = accountFactory.getAddress(accountAdmin, bytes("")); - - // The account can initially receive NFTs. - assertEq(erc721.balanceOf(account), 0); - erc721.mint(account, 1); - assertEq(erc721.balanceOf(account), 1); - - // Make the account reject ERC-721 NFTs going forward. - IExtension.Extension memory extension; - - extension.metadata = IExtension.ExtensionMetadata({ - name: "NFTRejector", - metadataURI: "ipfs://NFTRejector", - implementation: address(new NFTRejector()) - }); - - extension.functions = new IExtension.ExtensionFunction[](1); - - extension.functions[0] = IExtension.ExtensionFunction( - NFTRejector.onERC721Received.selector, - "onERC721Received(address,address,uint256,bytes)" - ); - - vm.prank(factoryDeployer); - accountFactory.disableFunctionInExtension("AccountExtension", NFTRejector.onERC721Received.selector); - - vm.prank(factoryDeployer); - accountFactory.addExtension(extension); - - // Transfer NFTs to the account - erc721.mint(accountSigner, 1); - assertEq(erc721.ownerOf(1), accountSigner); - vm.prank(accountSigner); - vm.expectRevert("NFTs not accepted"); - erc721.safeTransferFrom(accountSigner, account, 1); - } -} diff --git a/src/test/smart-wallet/account-core/isValidSigner.t.sol b/src/test/smart-wallet/account-core/isValidSigner.t.sol deleted file mode 100644 index 59bb0e1bf..000000000 --- a/src/test/smart-wallet/account-core/isValidSigner.t.sol +++ /dev/null @@ -1,568 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test utils -import { BaseTest } from "../../utils/BaseTest.sol"; -import "contracts/external-deps/openzeppelin/proxy/Clones.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; -import { IAccountPermissions } from "contracts/extension/interface/IAccountPermissions.sol"; -import { AccountPermissions, EnumerableSet, ECDSA } from "contracts/extension/upgradeable/AccountPermissions.sol"; - -// Account Abstraction setup for smart wallets. -import { EntryPoint, IEntryPoint } from "contracts/prebuilts/account/utils/EntryPoint.sol"; -import { PackedUserOperation } from "contracts/prebuilts/account/interfaces/PackedUserOperation.sol"; - -// Target -import { DynamicAccountFactory, DynamicAccount, BaseAccountFactory } from "contracts/prebuilts/account/dynamic/DynamicAccountFactory.sol"; - -/// @dev This is a dummy contract to test contract interactions with Account. -contract Number { - uint256 public num; - - function setNum(uint256 _num) public { - num = _num; - } - - function doubleNum() public { - num *= 2; - } - - function incrementNum() public { - num += 1; - } -} - -contract MyDynamicAccount is DynamicAccount { - using EnumerableSet for EnumerableSet.AddressSet; - - constructor( - IEntryPoint _entrypoint, - Extension[] memory _defaultExtensions - ) DynamicAccount(_entrypoint, _defaultExtensions) {} - - function setPermissionsForSigner( - address _signer, - uint256 _nativeTokenLimit, - uint256 _startTimestamp, - uint256 _endTimestamp - ) public { - _accountPermissionsStorage().signerPermissions[_signer] = SignerPermissionsStatic( - _nativeTokenLimit, - uint128(_startTimestamp), - uint128(_endTimestamp) - ); - } - - function setApprovedTargetsForSigner(address _signer, address[] memory _approvedTargets) public { - uint256 len = _approvedTargets.length; - for (uint256 i = 0; i < len; i += 1) { - _accountPermissionsStorage().approvedTargets[_signer].add(_approvedTargets[i]); - } - } - - function _setAdmin(address _account, bool _isAdmin) internal virtual override { - _accountPermissionsStorage().isAdmin[_account] = _isAdmin; - } - - function _isAuthorizedCallToUpgrade() internal view virtual override returns (bool) {} -} - -contract AccountCoreTest_isValidSigner is BaseTest { - // Target contracts - EntryPoint private entrypoint; - DynamicAccountFactory private accountFactory; - MyDynamicAccount private account; - - // Mocks - Number internal numberContract; - - // Test params - uint256 private accountAdminPKey = 100; - address private accountAdmin; - - uint256 private accountSignerPKey = 200; - address private accountSigner; - - uint256 private nonSignerPKey = 300; - address private nonSigner; - - address private opSigner; - uint256 private startTimestamp; - uint256 private endTimestamp; - uint256 private nativeTokenLimit; - PackedUserOperation private op; - - bytes internal data = bytes(""); - - function _setupUserOp( - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (PackedUserOperation memory) { - uint256 nonce = entrypoint.getNonce(address(account), 0); - PackedUserOperation memory op; - - { - uint128 verificationGasLimit = 5_000_000; - uint128 callGasLimit = 5_000_000; - bytes32 packedGasLimits = (bytes32(uint256(verificationGasLimit)) << 128) | bytes32(uint256(callGasLimit)); - - // Get user op fields - op = PackedUserOperation({ - sender: address(account), - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - accountGasLimits: packedGasLimits, - preVerificationGas: 5_000_000, - gasFees: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - } - - // Sign UserOp - bytes32 opHash = EntryPoint(entrypoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - return op; - } - - function _setupUserOpExecute( - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (PackedUserOperation memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "execute(address,uint256,bytes)", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - function _setupUserOpExecuteBatch( - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _targets, - uint256[] memory _values, - bytes[] memory _callData - ) internal returns (PackedUserOperation memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "executeBatch(address[],uint256[],bytes[])", - _targets, - _values, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - function _setupUserOpInvalidFunction( - uint256 _signerPKey, - bytes memory _initCode - ) internal returns (PackedUserOperation memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature("invalidFunction()"); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - function setUp() public override { - super.setUp(); - - // Setup signers. - accountAdmin = vm.addr(accountAdminPKey); - vm.deal(accountAdmin, 100 ether); - - accountSigner = vm.addr(accountSignerPKey); - nonSigner = vm.addr(nonSignerPKey); - - // Setup contracts - entrypoint = new EntryPoint(); - - IExtension.Extension[] memory extensions; - - // deploy account factory - accountFactory = new DynamicAccountFactory(deployer, extensions); - // deploy dummy contract - numberContract = new Number(); - - address accountImpl = address(new MyDynamicAccount(IEntryPoint(payable(address(entrypoint))), extensions)); - address _account = Clones.cloneDeterministic(accountImpl, "salt"); - account = MyDynamicAccount(payable(_account)); - account.initialize(accountAdmin, ""); - } - - function test_isValidSigner_whenSignerIsAdmin() public { - opSigner = accountAdmin; - PackedUserOperation memory _op; // empty op since it's not relevant for this check - bool isValid = DynamicAccount(payable(account)).isValidSigner(opSigner, _op); - - assertTrue(isValid); - } - - modifier whenNotAdmin() { - opSigner = accountSigner; - _; - } - - function test_isValidSigner_invalidTimestamps() public whenNotAdmin { - PackedUserOperation memory _op; // empty op since it's not relevant for this check - startTimestamp = 100; - endTimestamp = 200; - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - vm.warp(201); // block timestamp greater than end timestamp - bool isValid = account.isValidSigner(opSigner, _op); - - assertFalse(isValid); - - vm.warp(200); // block timestamp equal to end timestamp - isValid = account.isValidSigner(opSigner, _op); - - assertFalse(isValid); - - vm.warp(99); // block timestamp less than start timestamp - isValid = account.isValidSigner(opSigner, _op); - - assertFalse(isValid); - } - - modifier whenValidTimestamps() { - startTimestamp = 100; - endTimestamp = 200; - vm.warp(150); // block timestamp within start and end timestamps - _; - } - - function test_isValidSigner_noApprovedTargets() public whenNotAdmin whenValidTimestamps { - PackedUserOperation memory _op; // empty op since it's not relevant for this check - address[] memory _approvedTargets; - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - bool isValid = account.isValidSigner(opSigner, _op); - - assertFalse(isValid); - } - - // ================== - // ======= Test branch: wildcard - // ================== - - function test_isValidSigner_wildcardExecute_breachNativeTokenLimit() public whenNotAdmin whenValidTimestamps { - // set wildcard - address[] memory _approvedTargets = new address[](1); - _approvedTargets[0] = address(0); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - // user op execute - op = _setupUserOpExecute(accountSignerPKey, bytes(""), address(0x123), 10, bytes("")); - - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - bool isValid = account.isValidSigner(opSigner, op); - - assertFalse(isValid); - } - - function test_isValidSigner_wildcardExecuteBatch_breachNativeTokenLimit() public whenNotAdmin whenValidTimestamps { - // set wildcard - address[] memory _approvedTargets = new address[](1); - _approvedTargets[0] = address(0); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - // user op execute - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 10; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - op = _setupUserOpExecuteBatch(accountSignerPKey, bytes(""), targets, values, callData); - - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - bool isValid = account.isValidSigner(opSigner, op); - - assertFalse(isValid); - } - - modifier whenWithinNativeTokenLimit() { - nativeTokenLimit = 1000; - _; - } - - function test_isValidSigner_wildcardExecute() public whenNotAdmin whenValidTimestamps whenWithinNativeTokenLimit { - // set wildcard - address[] memory _approvedTargets = new address[](1); - _approvedTargets[0] = address(0); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - // user op execute - op = _setupUserOpExecute(accountSignerPKey, bytes(""), address(0x123), 10, bytes("")); - - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - bool isValid = account.isValidSigner(opSigner, op); - - assertTrue(isValid); - } - - function test_isValidSigner_wildcardExecuteBatch() - public - whenNotAdmin - whenValidTimestamps - whenWithinNativeTokenLimit - { - // set wildcard - address[] memory _approvedTargets = new address[](1); - _approvedTargets[0] = address(0); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - // user op execute - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 10; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - op = _setupUserOpExecuteBatch(accountSignerPKey, bytes(""), targets, values, callData); - - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - bool isValid = account.isValidSigner(opSigner, op); - - assertTrue(isValid); - } - - function test_isValidSigner_wildcardInvalidFunction() - public - whenNotAdmin - whenValidTimestamps - whenWithinNativeTokenLimit - { - // set wildcard - address[] memory _approvedTargets = new address[](1); - _approvedTargets[0] = address(0); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - // user op execute - op = _setupUserOpInvalidFunction(accountSignerPKey, bytes("")); - - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - bool isValid = account.isValidSigner(opSigner, op); - - assertFalse(isValid); - } - - // ================== - // ======= Test branch: not wildcard - // ================== - - function test_isValidSigner_execute_callingWrongTarget() - public - whenNotAdmin - whenValidTimestamps - whenWithinNativeTokenLimit - { - // set wildcard - address[] memory _approvedTargets = new address[](1); - _approvedTargets[0] = address(numberContract); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - // user op execute - address wrongTarget = address(0x123); - op = _setupUserOpExecute(accountSignerPKey, bytes(""), wrongTarget, 10, bytes("")); - - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - bool isValid = account.isValidSigner(opSigner, op); - - assertFalse(isValid); - } - - function test_isValidSigner_executeBatch_callingWrongTarget() - public - whenNotAdmin - whenValidTimestamps - whenWithinNativeTokenLimit - { - // set wildcard - address[] memory _approvedTargets = new address[](1); - _approvedTargets[0] = address(numberContract); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - // user op execute - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - address wrongTarget = address(0x123); - for (uint256 i = 0; i < count; i += 1) { - targets[i] = wrongTarget; - values[i] = 10; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - op = _setupUserOpExecuteBatch(accountSignerPKey, bytes(""), targets, values, callData); - - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - bool isValid = account.isValidSigner(opSigner, op); - - assertFalse(isValid); - } - - modifier whenCorrectTarget() { - _; - } - - function test_isValidSigner_execute_breachNativeTokenLimit() - public - whenNotAdmin - whenValidTimestamps - whenCorrectTarget - { - // set wildcard - address[] memory _approvedTargets = new address[](1); - _approvedTargets[0] = address(numberContract); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - // user op execute - op = _setupUserOpExecute(accountSignerPKey, bytes(""), address(numberContract), 10, bytes("")); - - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - bool isValid = account.isValidSigner(opSigner, op); - - assertFalse(isValid); - } - - function test_isValidSigner_executeBatch_breachNativeTokenLimit() - public - whenNotAdmin - whenValidTimestamps - whenCorrectTarget - { - // set wildcard - address[] memory _approvedTargets = new address[](1); - _approvedTargets[0] = address(numberContract); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - // user op execute - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 10; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - op = _setupUserOpExecuteBatch(accountSignerPKey, bytes(""), targets, values, callData); - - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - bool isValid = account.isValidSigner(opSigner, op); - - assertFalse(isValid); - } - - function test_isValidSigner_execute() - public - whenNotAdmin - whenValidTimestamps - whenWithinNativeTokenLimit - whenCorrectTarget - { - // set wildcard - address[] memory _approvedTargets = new address[](1); - _approvedTargets[0] = address(numberContract); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - // user op execute - op = _setupUserOpExecute(accountSignerPKey, bytes(""), address(numberContract), 10, bytes("")); - - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - bool isValid = account.isValidSigner(opSigner, op); - - assertTrue(isValid); - } - - function test_isValidSigner_executeBatch() - public - whenNotAdmin - whenValidTimestamps - whenWithinNativeTokenLimit - whenCorrectTarget - { - // set wildcard - address[] memory _approvedTargets = new address[](1); - _approvedTargets[0] = address(numberContract); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - // user op execute - uint256 count = 3; - address[] memory targets = new address[](count); - uint256[] memory values = new uint256[](count); - bytes[] memory callData = new bytes[](count); - - for (uint256 i = 0; i < count; i += 1) { - targets[i] = address(numberContract); - values[i] = 10; - callData[i] = abi.encodeWithSignature("incrementNum()", i); - } - - op = _setupUserOpExecuteBatch(accountSignerPKey, bytes(""), targets, values, callData); - - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - bool isValid = account.isValidSigner(opSigner, op); - - assertTrue(isValid); - } - - function test_isValidSigner_invalidFunction() public whenNotAdmin whenValidTimestamps whenWithinNativeTokenLimit { - // set wildcard - address[] memory _approvedTargets = new address[](1); - _approvedTargets[0] = address(numberContract); - account.setApprovedTargetsForSigner(opSigner, _approvedTargets); - - // user op execute - op = _setupUserOpInvalidFunction(accountSignerPKey, bytes("")); - - account.setPermissionsForSigner(opSigner, nativeTokenLimit, startTimestamp, endTimestamp); - - bool isValid = account.isValidSigner(opSigner, op); - - assertFalse(isValid); - } -} diff --git a/src/test/smart-wallet/account-core/isValidSigner.tree b/src/test/smart-wallet/account-core/isValidSigner.tree deleted file mode 100644 index cc4497260..000000000 --- a/src/test/smart-wallet/account-core/isValidSigner.tree +++ /dev/null @@ -1,46 +0,0 @@ -isValidSigner(address _signer, UserOperation calldata _userOp) -├── when `_signer` is admin -│ └── it should return true -├── when `_signer` is not admin - └── when timestamp is invalid - │ └── it should return false - └── when timestamp is valid - └── when no approved targets - │ └── it should return false - │ - │ // Case - Wildcard - └── when approved targets length is equal to 1 and contains address(0) - │ └── when calling `execute` function - │ │ └── when the decoded `value` is more than nativeTokenLimitPerTransaction - │ │ │ └── it should return false - │ │ └── when the decoded `value` is within nativeTokenLimitPerTransaction - │ │ └── it should return true - │ └── when calling `batchExecute` function - │ │ └── when any item in the decoded `values` array is more than nativeTokenLimitPerTransaction - │ │ │ └── it should return false - │ │ └── when all items in the decoded `values` array are within nativeTokenLimitPerTransaction - │ │ └── it should return true - │ └── when calling an invalid function - │ └── it should return false - │ - │ // Case - No Wildcard - └── when approved targets length is greater than 1, or doesn't contain address(0) - └── when calling `execute` function - │ └── when approvedTargets doesn't contain the decoded `target` - │ │ └── it should return false - │ └── when approvedTargets contains the decoded `target` - │ └── when the decoded `value` is more than nativeTokenLimitPerTransaction - │ │ └── it should return false - │ └── when the decoded `value` is within nativeTokenLimitPerTransaction - │ └── it should return true - └── when calling `batchExecute` function - │ └── when approvedTargets doesn't contain one or more items in the decoded `targets` array - │ │ └── it should return false - │ └── when approvedTargets contains all items in the decdoded `targets` array - │ └── when any item in the decoded `values` array is more than nativeTokenLimitPerTransaction - │ │ └── it should return false - │ └── when all items in the decoded `values` array are within nativeTokenLimitPerTransaction - │ └── it should return true - └── when calling an invalid function - └── it should return false - \ No newline at end of file diff --git a/src/test/smart-wallet/account-permissions/setPermissionsForSigner.t.sol b/src/test/smart-wallet/account-permissions/setPermissionsForSigner.t.sol deleted file mode 100644 index d5f63e08a..000000000 --- a/src/test/smart-wallet/account-permissions/setPermissionsForSigner.t.sol +++ /dev/null @@ -1,635 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test utils -import "../../utils/BaseTest.sol"; -import "@thirdweb-dev/dynamic-contracts/src/interface/IExtension.sol"; -import { IAccountPermissions } from "contracts/extension/interface/IAccountPermissions.sol"; -import { AccountPermissions } from "contracts/extension/upgradeable/AccountPermissions.sol"; -import { AccountExtension } from "contracts/prebuilts/account/utils/AccountExtension.sol"; - -// Account Abstraction setup for smart wallets. -import { EntryPoint, IEntryPoint } from "contracts/prebuilts/account/utils/EntryPoint.sol"; -import { PackedUserOperation } from "contracts/prebuilts/account/interfaces/PackedUserOperation.sol"; - -// Target -import { Account as SimpleAccount } from "contracts/prebuilts/account/non-upgradeable/Account.sol"; -import { DynamicAccountFactory, DynamicAccount } from "contracts/prebuilts/account/dynamic/DynamicAccountFactory.sol"; -import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - -/// @dev This is a dummy contract to test contract interactions with Account. -contract Number { - uint256 public num; - - function setNum(uint256 _num) public { - num = _num; - } - - function doubleNum() public { - num *= 2; - } - - function incrementNum() public { - num += 1; - } -} - -contract NFTRejector { - function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) { - revert("NFTs not accepted"); - } -} - -contract AccountPermissionsTest_setPermissionsForSigner is BaseTest { - event AdminUpdated(address indexed signer, bool isAdmin); - - event SignerPermissionsUpdated( - address indexed authorizingSigner, - address indexed targetSigner, - IAccountPermissions.SignerPermissionRequest permissions - ); - - // Target contracts - EntryPoint private constant entrypoint = EntryPoint(payable(0x0000000071727De22E5E9d8BAf0edAc6f37da032)); - DynamicAccountFactory private accountFactory; - - // Mocks - Number internal numberContract; - - // Test params - uint256 private accountAdminPKey = 100; - address private accountAdmin; - - uint256 private accountSignerPKey = 200; - address private accountSigner; - - uint256 private nonSignerPKey = 300; - address private nonSigner; - - bytes internal data = bytes(""); - - // UserOp terminology: `sender` is the smart wallet. - address private sender = 0x78b942FBC9126b4Ed8384Bb9dd1420Ea865be91a; - address payable private beneficiary = payable(address(0x45654)); - - bytes32 private uidCache = bytes32("random uid"); - - event AccountCreated(address indexed account, address indexed accountAdmin); - - function _prepareSignature( - IAccountPermissions.SignerPermissionRequest memory _req - ) internal view returns (bytes32 typedDataHash) { - bytes32 typehashSignerPermissionRequest = keccak256( - "SignerPermissionRequest(address signer,uint8 isAdmin,address[] approvedTargets,uint256 nativeTokenLimitPerTransaction,uint128 permissionStartTimestamp,uint128 permissionEndTimestamp,uint128 reqValidityStartTimestamp,uint128 reqValidityEndTimestamp,bytes32 uid)" - ); - bytes32 nameHash = keccak256(bytes("Account")); - bytes32 versionHash = keccak256(bytes("1")); - bytes32 typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - bytes32 domainSeparator = keccak256(abi.encode(typehashEip712, nameHash, versionHash, block.chainid, sender)); - - bytes memory encodedRequestStart = abi.encode( - typehashSignerPermissionRequest, - _req.signer, - _req.isAdmin, - keccak256(abi.encodePacked(_req.approvedTargets)), - _req.nativeTokenLimitPerTransaction - ); - - bytes memory encodedRequestEnd = abi.encode( - _req.permissionStartTimestamp, - _req.permissionEndTimestamp, - _req.reqValidityStartTimestamp, - _req.reqValidityEndTimestamp, - _req.uid - ); - - bytes32 structHash = keccak256(bytes.concat(encodedRequestStart, encodedRequestEnd)); - typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - } - - function _signSignerPermissionRequest( - IAccountPermissions.SignerPermissionRequest memory _req - ) internal view returns (bytes memory signature) { - bytes32 typedDataHash = _prepareSignature(_req); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, typedDataHash); - signature = abi.encodePacked(r, s, v); - } - - function _signSignerPermissionRequestInvalid( - IAccountPermissions.SignerPermissionRequest memory _req - ) internal view returns (bytes memory signature) { - bytes32 typedDataHash = _prepareSignature(_req); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(0x111, typedDataHash); - signature = abi.encodePacked(r, s, v); - } - - function _setupUserOp( - uint256 _signerPKey, - bytes memory _initCode, - bytes memory _callDataForEntrypoint - ) internal returns (PackedUserOperation[] memory ops) { - uint256 nonce = entrypoint.getNonce(sender, 0); - PackedUserOperation memory op; - - { - uint128 verificationGasLimit = 5_000_000; - uint128 callGasLimit = 5_000_000; - bytes32 packedGasLimits = (bytes32(uint256(verificationGasLimit)) << 128) | bytes32(uint256(callGasLimit)); - - // Get user op fields - op = PackedUserOperation({ - sender: sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - accountGasLimits: packedGasLimits, - preVerificationGas: 5_000_000, - gasFees: 0, - paymasterAndData: bytes(""), - signature: bytes("") - }); - } - - // Sign UserOp - bytes32 opHash = EntryPoint(entrypoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_signerPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(_signerPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new PackedUserOperation[](1); - ops[0] = op; - } - - function _setupUserOpExecute( - uint256 _signerPKey, - bytes memory _initCode, - address _target, - uint256 _value, - bytes memory _callData - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "execute(address,uint256,bytes)", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - function _setupUserOpExecuteBatch( - uint256 _signerPKey, - bytes memory _initCode, - address[] memory _target, - uint256[] memory _value, - bytes[] memory _callData - ) internal returns (PackedUserOperation[] memory) { - bytes memory callDataForEntrypoint = abi.encodeWithSignature( - "executeBatch(address[],uint256[],bytes[])", - _target, - _value, - _callData - ); - - return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); - } - - function setUp() public override { - super.setUp(); - - // Setup signers. - accountAdmin = vm.addr(accountAdminPKey); - vm.deal(accountAdmin, 100 ether); - - accountSigner = vm.addr(accountSignerPKey); - nonSigner = vm.addr(nonSignerPKey); - - // Setup contracts - address _deployedEntrypoint = address(new EntryPoint()); - vm.etch(address(entrypoint), bytes(_deployedEntrypoint.code)); - - // Setting up default extension. - IExtension.Extension memory defaultExtension; - - defaultExtension.metadata = IExtension.ExtensionMetadata({ - name: "AccountExtension", - metadataURI: "ipfs://AccountExtension", - implementation: address(new AccountExtension()) - }); - - defaultExtension.functions = new IExtension.ExtensionFunction[](7); - - defaultExtension.functions[0] = IExtension.ExtensionFunction( - AccountExtension.supportsInterface.selector, - "supportsInterface(bytes4)" - ); - defaultExtension.functions[1] = IExtension.ExtensionFunction( - AccountExtension.execute.selector, - "execute(address,uint256,bytes)" - ); - defaultExtension.functions[2] = IExtension.ExtensionFunction( - AccountExtension.executeBatch.selector, - "executeBatch(address[],uint256[],bytes[])" - ); - defaultExtension.functions[3] = IExtension.ExtensionFunction( - ERC721Holder.onERC721Received.selector, - "onERC721Received(address,address,uint256,bytes)" - ); - defaultExtension.functions[4] = IExtension.ExtensionFunction( - ERC1155Holder.onERC1155Received.selector, - "onERC1155Received(address,address,uint256,uint256,bytes)" - ); - defaultExtension.functions[5] = IExtension.ExtensionFunction( - bytes4(0), // Selector for `receive()` function. - "receive()" - ); - defaultExtension.functions[6] = IExtension.ExtensionFunction( - AccountExtension.isValidSignature.selector, - "isValidSignature(bytes32,bytes)" - ); - - IExtension.Extension[] memory extensions = new IExtension.Extension[](1); - extensions[0] = defaultExtension; - - // deploy account factory - accountFactory = new DynamicAccountFactory(deployer, extensions); - // deploy dummy contract - numberContract = new Number(); - } - - function _setup_executeTransaction() internal { - bytes memory initCallData = abi.encodeWithSignature("createAccount(address,bytes)", accountAdmin, data); - bytes memory initCode = abi.encodePacked(abi.encodePacked(address(accountFactory)), initCallData); - - PackedUserOperation[] memory userOpCreateAccount = _setupUserOpExecute( - accountAdminPKey, - initCode, - address(0), - 0, - bytes("") - ); - - EntryPoint(entrypoint).handleOps(userOpCreateAccount, beneficiary); - } - - function test_state_targetAdminNotAdmin() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - address[] memory approvedTargets = new address[](0); - - bool adminStatusBefore = SimpleAccount(payable(account)).isAdmin(accountSigner); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 1, - approvedTargets, - 0, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - bool adminStatusAfter = SimpleAccount(payable(account)).isAdmin(accountSigner); - - assertEq(adminStatusBefore, false); - assertEq(adminStatusAfter, true); - } - - function test_state_targetAdminIsAdmin() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - address[] memory approvedTargets = new address[](0); - - { - IAccountPermissions.SignerPermissionRequest memory request = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 1, - approvedTargets, - 1, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - bytes memory sig2 = _signSignerPermissionRequest(request); - SimpleAccount(payable(account)).setPermissionsForSigner(request, sig2); - - address[] memory adminsBefore = SimpleAccount(payable(account)).getAllAdmins(); - assertEq(adminsBefore[1], accountSigner); - } - - bool adminStatusBefore = SimpleAccount(payable(account)).isAdmin(accountAdmin); - - uidCache = bytes32("new uid"); - - IAccountPermissions.SignerPermissionRequest memory req = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 2, - approvedTargets, - 1, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - bytes memory sig3 = _signSignerPermissionRequest(req); - SimpleAccount(payable(account)).setPermissionsForSigner(req, sig3); - - bool adminStatusAfter = SimpleAccount(payable(account)).isAdmin(accountSigner); - address[] memory adminsAfter = SimpleAccount(payable(account)).getAllAdmins(); - - assertEq(adminStatusBefore, true); - assertEq(adminStatusAfter, false); - assertEq(adminsAfter.length, 1); - } - - function test_revert_attemptReplayUID() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - address[] memory approvedTargets = new address[](0); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 1, - approvedTargets, - 1, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - // Attempt replay UID - - IAccountPermissions.SignerPermissionRequest memory permissionsReqTwo = IAccountPermissions - .SignerPermissionRequest( - accountSigner, - 1, - approvedTargets, - 0, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - sig = _signSignerPermissionRequest(permissionsReqTwo); - vm.expectRevert(); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReqTwo, sig); - } - - function test_event_addAdmin_AdminUpdated() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - address[] memory approvedTargets = new address[](0); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 1, - approvedTargets, - 1, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - - vm.expectEmit(true, false, false, true); - emit AdminUpdated(accountSigner, true); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - } - - function test_event_removeAdmin_AdminUpdated() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - address[] memory approvedTargets = new address[](0); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 2, - approvedTargets, - 1, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - - vm.expectEmit(true, false, false, true); - emit AdminUpdated(accountSigner, false); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - } - - function test_revert_timeBeforeStart() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - address[] memory approvedTargets = new address[](0); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 1, - approvedTargets, - 0, - 0, - type(uint128).max, - uint128(block.timestamp + 1000), - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - vm.expectRevert("!period"); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - } - - function test_revert_timeAfterExpiry() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - address[] memory approvedTargets = new address[](0); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 1, - approvedTargets, - 0, - 0, - type(uint128).max, - 0, - uint128(block.timestamp - 1), - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - vm.expectRevert("!period"); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - } - - function test_revert_SignerNotAdmin() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - address[] memory approvedTargets = new address[](0); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 0, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequestInvalid(permissionsReq); - vm.expectRevert(bytes("!sig")); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - } - - function test_revert_SignerAlreadyAdmin() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - address[] memory approvedTargets = new address[](0); - - { - //set admin status - IAccountPermissions.SignerPermissionRequest memory req = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 1, - approvedTargets, - 0, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig2 = _signSignerPermissionRequest(req); - SimpleAccount(payable(account)).setPermissionsForSigner(req, sig2); - } - - //test set signerPerms as admin - - uidCache = bytes32("new uid"); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 0, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig3 = _signSignerPermissionRequest(permissionsReq); - vm.expectRevert("admin"); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig3); - } - - function test_state_setPermissionsForSigner() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - - IAccountPermissions.SignerPermissions[] memory allSigners = SimpleAccount(payable(account)).getAllSigners(); - assertEq(allSigners[0].signer, accountSigner); - assertEq(allSigners[0].approvedTargets[0], address(numberContract)); - assertEq(allSigners[0].nativeTokenLimitPerTransaction, 1); - assertEq(allSigners[0].startTimestamp, 0); - assertEq(allSigners[0].endTimestamp, type(uint128).max); - } - - function test_event_addSigner() public { - _setup_executeTransaction(); - - address account = accountFactory.getAddress(accountAdmin, bytes("")); - address[] memory approvedTargets = new address[](1); - approvedTargets[0] = address(numberContract); - - IAccountPermissions.SignerPermissionRequest memory permissionsReq = IAccountPermissions.SignerPermissionRequest( - accountSigner, - 0, - approvedTargets, - 1, - 0, - type(uint128).max, - 0, - type(uint128).max, - uidCache - ); - - vm.prank(accountAdmin); - bytes memory sig = _signSignerPermissionRequest(permissionsReq); - - vm.expectEmit(true, true, false, true); - emit SignerPermissionsUpdated(accountAdmin, accountSigner, permissionsReq); - SimpleAccount(payable(account)).setPermissionsForSigner(permissionsReq, sig); - } -} diff --git a/src/test/smart-wallet/account-permissions/setPermissionsForSigner.tree b/src/test/smart-wallet/account-permissions/setPermissionsForSigner.tree deleted file mode 100644 index fbe90351d..000000000 --- a/src/test/smart-wallet/account-permissions/setPermissionsForSigner.tree +++ /dev/null @@ -1,33 +0,0 @@ -function setPermissionsForSigner(SignerPermissionRequest calldata _req, bytes calldata _signature) -├── when reqValidityStartTimestamp is greater than block.timestamp -│ └── it should revert ✅ -├── when reqValidityEndTimestamp is less than block.timestamp -│ └── it should revert ✅ -├── when uid is executed -│ └── it should revert ✅ -├── when the recovered signer is not an admin -│ └── it should revert ✅ -└── when the reqValidityStartTimestamp is less than block.timestamp - └── when reqValidityEndTimestamp is greater than block.timestamp - └── when recovered signer is an admin ✅ - └── when req.uid has not been marked as executed - └── when _req.isAdmin is greater than zero - ├── it should mark req.uid as executed ✅ - ├── when _req.isAdmin is one - │ ├── it should set isAdmin[(targetAdmin)] as true ✅ - │ ├── it should add targetAdmin to allAdmins ✅ - │ └── it should emit AdminUpdated with the parameters targetAdmin, true ✅ - ├── when _req.isAdmin is greater than one - │ ├── it should set isAdmin[(targetAdmin)] as false ✅ - │ ├── it should remove targetAdmin from allAdmins ✅ - │ └── it should emit the event AdminUpdated with the parameters targetAdmin, false ✅ - └── when _req.isAdmin is equal to zero - ├── when targetSigner is an admin - │ └── it should revert ✅ - └── when targetSigner is not an admin - ├── it should mark req.uid as executed ✅ - ├── it should add targetSigner to allSigners ✅ - ├── it should set signerPermissions[(targetSigner)] as a SignerPermissionsStatic(nativeTokenLimitPerTransaction, permissionStartTimestamp, permissionEndTimestamp) ✅ - ├── it should remove current approved targets for targetSigner ✅ - ├── it should add the new approved targets for targetSigner ✅ - └── it should emit the event SignerPermissionsUpdated with the parameters signer, targetSigner, SignerPermissionRequest ✅ \ No newline at end of file diff --git a/src/test/smart-wallet/token-paymaster/TokenPaymaster.t.sol b/src/test/smart-wallet/token-paymaster/TokenPaymaster.t.sol deleted file mode 100644 index e6b3507ef..000000000 --- a/src/test/smart-wallet/token-paymaster/TokenPaymaster.t.sol +++ /dev/null @@ -1,230 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test utils -import "../../utils/BaseTest.sol"; -import { MockERC20CustomDecimals } from "../../mocks/MockERC20CustomDecimals.sol"; -import { TestUniswap } from "../../mocks/TestUniswap.sol"; -import { TestOracle2 } from "../../mocks/TestOracle2.sol"; -import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - -// Account Abstraction setup for smart wallets. -import { EntryPoint, IEntryPoint } from "contracts/prebuilts/account/utils/EntryPoint.sol"; -import { PackedUserOperation } from "contracts/prebuilts/account/interfaces/PackedUserOperation.sol"; - -// Target -import { IAccountPermissions } from "contracts/extension/interface/IAccountPermissions.sol"; -import { AccountFactory } from "contracts/prebuilts/account/non-upgradeable/AccountFactory.sol"; -import { Account as SimpleAccount } from "contracts/prebuilts/account/non-upgradeable/Account.sol"; -import { TokenPaymaster, IERC20Metadata } from "contracts/prebuilts/account/token-paymaster/TokenPaymaster.sol"; -import { OracleHelper, IOracle } from "contracts/prebuilts/account/utils/OracleHelper.sol"; -import { UniswapHelper, IV3SwapRouter } from "contracts/prebuilts/account/utils/UniswapHelper.sol"; - -/// @dev This is a dummy contract to test contract interactions with Account. -contract Number { - uint256 public num; - - function setNum(uint256 _num) public { - num = _num; - } - - function doubleNum() public { - num *= 2; - } - - function incrementNum() public { - num += 1; - } -} - -contract TokenPaymasterTest is BaseTest { - EntryPoint private entrypoint; - AccountFactory private accountFactory; - SimpleAccount private account; - MockERC20CustomDecimals private token; - TestUniswap private testUniswap; - TestOracle2 private nativeAssetOracle; - TestOracle2 private tokenOracle; - TokenPaymaster private paymaster; - - Number private numberContract; - - int256 initialPriceToken = 100000000; // USD per TOK - int256 initialPriceEther = 500000000; // USD per ETH - - uint256 priceDenominator = 10 ** 26; - uint128 minEntryPointBalance = 1e17; - - address payable private beneficiary = payable(address(0x45654)); - - uint256 private accountAdminPKey = 100; - address private accountAdmin; - - uint256 private accountSignerPKey = 200; - address private accountSigner; - - uint256 private nonSignerPKey = 300; - address private nonSigner; - - uint256 private paymasterOwnerPKey = 400; - address private paymasterOwner; - address private paymasterAddress; - - function setUp() public override { - super.setUp(); - - // Setup signers. - accountAdmin = vm.addr(accountAdminPKey); - vm.deal(accountAdmin, 100 ether); - - accountSigner = vm.addr(accountSignerPKey); - nonSigner = vm.addr(nonSignerPKey); - paymasterOwner = vm.addr(paymasterOwnerPKey); - - // Setup contracts - entrypoint = new EntryPoint(); - testUniswap = new TestUniswap(weth); - accountFactory = new AccountFactory(deployer, IEntryPoint(payable(address(entrypoint)))); - account = SimpleAccount(payable(accountFactory.createAccount(accountAdmin, bytes("")))); - token = new MockERC20CustomDecimals(6); - nativeAssetOracle = new TestOracle2(initialPriceEther, 8); - tokenOracle = new TestOracle2(initialPriceToken, 8); - numberContract = new Number(); - - weth.deposit{ value: 1 ether }(); - weth.transfer(address(testUniswap), 1 ether); - - TokenPaymaster.TokenPaymasterConfig memory tokenPaymasterConfig = TokenPaymaster.TokenPaymasterConfig({ - priceMarkup: (priceDenominator * 15) / 10, // +50% - minEntryPointBalance: minEntryPointBalance, - refundPostopCost: 40000, - priceMaxAge: 86400 - }); - - OracleHelper.OracleHelperConfig memory oracleHelperConfig = OracleHelper.OracleHelperConfig({ - cacheTimeToLive: 0, - maxOracleRoundAge: 0, - nativeOracle: IOracle(address(nativeAssetOracle)), - nativeOracleReverse: false, - priceUpdateThreshold: (priceDenominator * 12) / 100, // 20% - tokenOracle: IOracle(address(tokenOracle)), - tokenOracleReverse: false, - tokenToNativeOracle: false - }); - - UniswapHelper.UniswapHelperConfig memory uniswapHelperConfig = UniswapHelper.UniswapHelperConfig({ - minSwapAmount: 1, - slippage: 5, - uniswapPoolFee: 3, - wethIsNativeAsset: false - }); - - paymaster = new TokenPaymaster( - IERC20Metadata(address(token)), - entrypoint, - weth, - IV3SwapRouter(address(testUniswap)), - tokenPaymasterConfig, - oracleHelperConfig, - uniswapHelperConfig, - paymasterOwner - ); - paymasterAddress = address(paymaster); - - token.mint(paymasterOwner, 10_000 ether); - vm.deal(paymasterOwner, 10_000 ether); - - vm.startPrank(paymasterOwner); - token.transfer(address(paymaster), 100); - paymaster.updateCachedPrice(true); - entrypoint.depositTo{ value: 1000 ether }(address(paymaster)); - paymaster.addStake{ value: 2 ether }(1); - vm.stopPrank(); - } - - // test utils - function _packPaymasterStaticFields( - address paymaster, - uint128 validationGasLimit, - uint128 postOpGasLimit - ) internal pure returns (bytes memory) { - return abi.encodePacked(bytes20(paymaster), bytes16(validationGasLimit), bytes16(postOpGasLimit)); - } - - function _setupUserOpWithSenderAndPaymaster( - bytes memory _initCode, - bytes memory _callDataForEntrypoint, - address _sender, - address _paymaster, - uint128 _paymasterVerificationGasLimit, - uint128 _paymasterPostOpGasLimit - ) internal returns (PackedUserOperation[] memory ops) { - uint256 nonce = entrypoint.getNonce(_sender, 0); - PackedUserOperation memory op; - - { - uint128 verificationGasLimit = 500_000; - uint128 callGasLimit = 500_000; - bytes32 packedAccountGasLimits = (bytes32(uint256(verificationGasLimit)) << 128) | - bytes32(uint256(callGasLimit)); - bytes32 packedGasLimits = (bytes32(uint256(1e9)) << 128) | bytes32(uint256(1e9)); - - // Get user op fields - op = PackedUserOperation({ - sender: _sender, - nonce: nonce, - initCode: _initCode, - callData: _callDataForEntrypoint, - accountGasLimits: packedAccountGasLimits, - preVerificationGas: 500_000, - gasFees: packedGasLimits, - paymasterAndData: _packPaymasterStaticFields( - _paymaster, - _paymasterVerificationGasLimit, - _paymasterPostOpGasLimit - ), - signature: bytes("") - }); - } - - // Sign UserOp - bytes32 opHash = EntryPoint(entrypoint).getUserOpHash(op); - bytes32 msgHash = ECDSA.toEthSignedMessageHash(opHash); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountAdminPKey, msgHash); - bytes memory userOpSignature = abi.encodePacked(r, s, v); - - address recoveredSigner = ECDSA.recover(msgHash, v, r, s); - address expectedSigner = vm.addr(accountAdminPKey); - assertEq(recoveredSigner, expectedSigner); - - op.signature = userOpSignature; - - // Store UserOp - ops = new PackedUserOperation[](1); - ops[0] = op; - } - - // Should be able to sponsor the UserOp while charging correct amount of ERC-20 tokens - function test_validatePaymasterUserOp_correctERC20() public { - token.mint(address(account), 1 ether); - vm.prank(address(account)); - token.approve(address(paymaster), type(uint256).max); - - PackedUserOperation[] memory ops = _setupUserOpWithSenderAndPaymaster( - bytes(""), - abi.encodeWithSignature( - "execute(address,uint256,bytes)", - address(numberContract), - 0, - abi.encodeWithSignature("setNum(uint256)", 42) - ), - address(account), - address(paymaster), - 3e5, - 3e5 - ); - - entrypoint.handleOps(ops, beneficiary); - } -} diff --git a/src/test/smart-wallet/utils/AABenchmarkArtifacts.sol b/src/test/smart-wallet/utils/AABenchmarkArtifacts.sol deleted file mode 100644 index b71c16bcc..000000000 --- a/src/test/smart-wallet/utils/AABenchmarkArtifacts.sol +++ /dev/null @@ -1,14 +0,0 @@ - -pragma solidity ^0.8.0; -interface ThirdwebAccountFactory { - function createAccount(address _admin, bytes calldata _data) external returns (address); - function getAddress(address _adminSigner, bytes calldata _data) external view returns (address); -} -interface ThirdwebAccount { - function execute(address _target, uint256 _value, bytes calldata _calldata) external; -} -address constant THIRDWEB_ACCOUNT_FACTORY_ADDRESS = 0x2e234DAe75C793f67A35089C9d99245E1C58470b; -address constant THIRDWEB_ACCOUNT_IMPL_ADDRESS = 0xffD4505B3452Dc22f8473616d50503bA9E1710Ac; -bytes constant THIRDWEB_ACCOUNT_FACTORY_BYTECODE = hex"608060405234801561001057600080fd5b50600436106101285760003560e01c806308e93d0a1461012d5780630b61e12b1461014b5780630e6254fd1461016057806311464fbe14610173578063248a9ca3146101b25780632f2ff15d146101d357806336568abe146101e657806358451f97146101f957806383a03f8c146102015780638878ed33146102145780639010d07c1461022757806391d148541461023a5780639387a3801461025d578063938e3d7b14610270578063a217fddf14610283578063a32fa5b31461028b578063a65d69d41461029e578063ac9650d8146102c5578063c3c5a547146102e5578063ca15c873146102f8578063d547741f1461030b578063d8fd8f441461031e578063e68a7c3b14610331578063e8a3d48514610344575b600080fd5b610135610359565b6040516101429190611945565b60405180910390f35b61015e6101593660046119ae565b61036a565b005b61013561016e3660046119d8565b61040b565b61019a7f000000000000000000000000ffd4505b3452dc22f8473616d50503ba9e1710ac81565b6040516001600160a01b039091168152602001610142565b6101c56101c03660046119f3565b610435565b604051908152602001610142565b61015e6101e1366004611a0c565b610453565b61015e6101f4366004611a0c565b6104fd565b6101c561055c565b61015e61020f3660046119f3565b610568565b61019a610222366004611a38565b6105b6565b61019a610235366004611aba565b610630565b61024d610248366004611a0c565b61073e565b6040519015158152602001610142565b61015e61026b3660046119ae565b610772565b61015e61027e366004611af2565b610809565b6101c5600081565b61024d610299366004611a0c565b61085a565b61019a7f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da03281565b6102d86102d3366004611ba2565b6108bd565b6040516101429190611c66565b61024d6102f33660046119d8565b610a19565b6101c56103063660046119f3565b610a25565b61015e610319366004611a0c565b610ac2565b61019a61032c366004611a38565b610acd565b61013561033f366004611aba565b610c18565b61034c610d49565b6040516101429190611cca565b60606103656000610de1565b905090565b336103758183610dee565b61039a5760405162461bcd60e51b815260040161039190611cdd565b60405180910390fd5b6001600160a01b03831660009081526002602052604081206103bc9083610e32565b9050801561040557836001600160a01b0316826001600160a01b03167f12146497b3b826918ec47f0cac7272a09ed06b30c16c030e99ec48ff5dd60b4760405160405180910390a35b50505050565b6001600160a01b038116600090815260026020526040902060609061042f90610de1565b92915050565b600061043f610e47565b600092835260010160205250604090205490565b61047761045e610e47565b6000848152600191909101602052604090205433610e6b565b61047f610e47565b6000838152602091825260408082206001600160a01b0385168352909252205460ff16156104ef5760405162461bcd60e51b815260206004820152601d60248201527f43616e206f6e6c79206772616e7420746f206e6f6e20686f6c646572730000006044820152606401610391565b6104f98282610ef0565b5050565b336001600160a01b038216146105525760405162461bcd60e51b815260206004820152601a60248201527921b0b71037b7363c903932b737bab731b2903337b91039b2b63360311b6044820152606401610391565b6104f98282610f04565b60006103656000610f18565b336105738183610dee565b61058f5760405162461bcd60e51b815260040161039190611cdd565b61059a600082610e32565b6104f95760405162461bcd60e51b815260040161039190611d14565b6000806105f98585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610f2292505050565b90506106257f000000000000000000000000ffd4505b3452dc22f8473616d50503ba9e1710ac82610f55565b9150505b9392505050565b60008061063b610fb5565b600085815260209190915260408120549150805b82811015610735576000610661610fb5565b60008881526020918252604080822085835260010190925220546001600160a01b0316146106d9578482036106c757610698610fb5565b600087815260209182526040808220938252600190930190915220546001600160a01b0316925061042f915050565b6106d2600183611d74565b9150610723565b6106e486600061073e565b801561071057506106f3610fb5565b600087815260209182526040808220828052600201909252205481145b1561072357610720600183611d74565b91505b61072e600182611d74565b905061064f565b50505092915050565b6000610748610e47565b6000938452602090815260408085206001600160a01b039490941685529290525090205460ff1690565b3361077d8183610dee565b6107995760405162461bcd60e51b815260040161039190611cdd565b6001600160a01b03831660009081526002602052604081206107bb9083610fbf565b9050801561040557836001600160a01b0316826001600160a01b03167f98d1ebbe00ae92a5de96a0f49742a8afa89f42363592bc2e7cfaaed68b45e7a660405160405180910390a350505050565b610811610fd4565b61084e5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610391565b61085781610fe0565b50565b6000610864610e47565b600084815260209182526040808220828052909252205460ff166108b45761088a610e47565b6000848152602091825260408082206001600160a01b0386168352909252205460ff16905061042f565b50600192915050565b6060816001600160401b038111156108d7576108d7611adc565b60405190808252806020026020018201604052801561090a57816020015b60608152602001906001900390816108f55790505b509050336000805b848110156107355781156109915761096f3087878481811061093657610936611d87565b90506020028101906109489190611d9d565b8660405160200161095b93929190611dea565b6040516020818303038152906040526110c7565b84828151811061098157610981611d87565b6020026020010181905250610a11565b6109f3308787848181106109a7576109a7611d87565b90506020028101906109b99190611d9d565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506110c792505050565b848281518110610a0557610a05611d87565b60200260200101819052505b600101610912565b600061042f81836110ec565b600080610a30610fb5565b6000848152602091909152604081205491505b81811015610a9d576000610a55610fb5565b60008681526020918252604080822085835260010190925220546001600160a01b031614610a8b57610a88600184611d74565b92505b610a96600182611d74565b9050610a43565b50610aa983600061073e565b15610abc57610ab9600183611d74565b91505b50919050565b61055261045e610e47565b6000807f000000000000000000000000ffd4505b3452dc22f8473616d50503ba9e1710ac90506000610b358686868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610f2292505050565b90506000610b438383610f55565b90506001600160a01b0381163b15610b5f579250610629915050565b610b69838361110e565b9050336001600160a01b037f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da0321614610bc257610ba6600082610e32565b610bc25760405162461bcd60e51b815260040161039190611d14565b610bce818888886111a5565b866001600160a01b0316816001600160a01b03167fac631f3001b55ea1509cf3d7e74898f85392a61a76e8149181ae1259622dabc860405160405180910390a39695505050505050565b60608183108015610c325750610c2e6000610f18565b8211155b610c8a5760405162461bcd60e51b815260206004820152602360248201527f426173654163636f756e74466163746f72793a20696e76616c696420696e646960448201526263657360e81b6064820152608401610391565b6000610c968484611e0b565b9050610ca28484611e0b565b6001600160401b03811115610cb957610cb9611adc565b604051908082528060200260200182016040528015610ce2578160200160208202803683370190505b50915060005b81811015610d4157610d05610cfd8683611d74565b60009061120d565b838281518110610d1757610d17611d87565b6001600160a01b0390921660209283029190910190910152610d3a600182611d74565b9050610ce8565b505092915050565b6060610d53611219565b8054610d5e90611e1e565b80601f0160208091040260200160405190810160405280929190818152602001828054610d8a90611e1e565b8015610dd75780601f10610dac57610100808354040283529160200191610dd7565b820191906000526020600020905b815481529060010190602001808311610dba57829003601f168201915b5050505050905090565b606060006106298361123d565b600080610e1b7f000000000000000000000000ffd4505b3452dc22f8473616d50503ba9e1710ac84610f55565b6001600160a01b0385811691161491505092915050565b6000610629836001600160a01b038416611299565b7f0a7b0f5c59907924802379ebe98cdc23e2ee7820f63d30126e10b3752010e50090565b610e73610e47565b6000838152602091825260408082206001600160a01b0385168352909252205460ff166104f957610eae816001600160a01b031660146112e8565b610eb98360206112e8565b604051602001610eca929190611e52565b60408051601f198184030181529082905262461bcd60e51b825261039191600401611cca565b610efa8282611483565b6104f982826114ec565b610f0e82826115ab565b6104f98282611614565b600061042f825490565b60008282604051602001610f37929190611ebf565b60405160208183030381529060405280519060200120905092915050565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff602482015260148101839052733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018290526037600c82012060788201526055604390910120600090610629565b60006103656116a3565b6000610629836001600160a01b038416611705565b6000610365813361073e565b6000610fea611219565b8054610ff590611e1e565b80601f016020809104026020016040519081016040528092919081815260200182805461102190611e1e565b801561106e5780601f106110435761010080835404028352916020019161106e565b820191906000526020600020905b81548152906001019060200180831161105157829003601f168201915b505050505090508161107e611219565b906110899082611f34565b507fc9c7c3fe08b88b4df9d4d47ef47d2c43d55c025a0ba88ca442580ed9e7348a1681836040516110bb929190611ff3565b60405180910390a15050565b606061062983836040518060600160405280602781526020016120b9602791396117f8565b6001600160a01b03811660009081526001830160205260408120541515610629565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b03811661042f5760405162461bcd60e51b8152602060048201526017602482015276115490cc4c4d8dce8818dc99585d194c8819985a5b1959604a1b6044820152606401610391565b60405163347d5e2560e21b81526001600160a01b0385169063d1f57894906111d590869086908690600401612018565b600060405180830381600087803b1580156111ef57600080fd5b505af1158015611203573d6000803e3d6000fd5b5050505050505050565b60006106298383611870565b7f4bc804ba64359c0e35e5ed5d90ee596ecaa49a3a930ddcb1470ea0dd625da90090565b60608160000180548060200260200160405190810160405280929190818152602001828054801561128d57602002820191906000526020600020905b815481526020019060010190808311611279575b50505050509050919050565b60008181526001830160205260408120546112e05750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561042f565b50600061042f565b606060006112f7836002612058565b611302906002611d74565b6001600160401b0381111561131957611319611adc565b6040519080825280601f01601f191660200182016040528015611343576020820181803683370190505b509050600360fc1b8160008151811061135e5761135e611d87565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061138d5761138d611d87565b60200101906001600160f81b031916908160001a90535060006113b1846002612058565b6113bc906001611d74565b90505b6001811115611434576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106113f0576113f0611d87565b1a60f81b82828151811061140657611406611d87565b60200101906001600160f81b031916908160001a90535060049490941c9361142d8161206f565b90506113bf565b5083156106295760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610391565b600161148d610e47565b6000848152602091825260408082206001600160a01b0386168084529352808220805460ff1916941515949094179093559151339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b60006114f6610fb5565b6000848152602091909152604090205490506001611512610fb5565b6000858152602091909152604081208054909190611531908490611d74565b90915550829050611540610fb5565b6000858152602091825260408082208583526001019092522080546001600160a01b0319166001600160a01b039290921691909117905580611580610fb5565b6000948552602090815260408086206001600160a01b03909516865260029094019052919092205550565b6115b58282610e6b565b6115bd610e47565b6000838152602091825260408082206001600160a01b0385168084529352808220805460ff191690555133929185917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b600061161e610fb5565b6000848152602091825260408082206001600160a01b03861683526002019092522054905061164b610fb5565b6000848152602091825260408082208483526001019092522080546001600160a01b031916905561167a610fb5565b6000938452602090815260408085206001600160a01b0390941685526002909301905250812055565b60008060ff196116d460017f0c4ba382c0009cf238e4c1ca1a52f51c61e6248a70bdfb34e5ed49d5578a5c0c611e0b565b6040516020016116e691815260200190565b60408051601f1981840301815291905280516020909101201692915050565b600081815260018301602052604081205480156117ee576000611729600183611e0b565b855490915060009061173d90600190611e0b565b90508181146117a257600086600001828154811061175d5761175d611d87565b906000526020600020015490508087600001848154811061178057611780611d87565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806117b3576117b3612086565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061042f565b600091505061042f565b6060600080856001600160a01b031685604051611815919061209c565b600060405180830381855af49150503d8060008114611850576040519150601f19603f3d011682016040523d82523d6000602084013e611855565b606091505b50915091506118668683838761189a565b9695505050505050565b600082600001828154811061188757611887611d87565b9060005260206000200154905092915050565b60608315611909578251600003611902576001600160a01b0385163b6119025760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610391565b5081611913565b611913838361191b565b949350505050565b81511561192b5781518083602001fd5b8060405162461bcd60e51b81526004016103919190611cca565b6020808252825182820181905260009190848201906040850190845b818110156119865783516001600160a01b031683529284019291840191600101611961565b50909695505050505050565b80356001600160a01b03811681146119a957600080fd5b919050565b600080604083850312156119c157600080fd5b6119ca83611992565b946020939093013593505050565b6000602082840312156119ea57600080fd5b61062982611992565b600060208284031215611a0557600080fd5b5035919050565b60008060408385031215611a1f57600080fd5b82359150611a2f60208401611992565b90509250929050565b600080600060408486031215611a4d57600080fd5b611a5684611992565b925060208401356001600160401b0380821115611a7257600080fd5b818601915086601f830112611a8657600080fd5b813581811115611a9557600080fd5b876020828501011115611aa757600080fd5b6020830194508093505050509250925092565b60008060408385031215611acd57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b600060208284031215611b0457600080fd5b81356001600160401b0380821115611b1b57600080fd5b818401915084601f830112611b2f57600080fd5b813581811115611b4157611b41611adc565b604051601f8201601f19908116603f01168101908382118183101715611b6957611b69611adc565b81604052828152876020848701011115611b8257600080fd5b826020860160208301376000928101602001929092525095945050505050565b60008060208385031215611bb557600080fd5b82356001600160401b0380821115611bcc57600080fd5b818501915085601f830112611be057600080fd5b813581811115611bef57600080fd5b8660208260051b8501011115611c0457600080fd5b60209290920196919550909350505050565b60005b83811015611c31578181015183820152602001611c19565b50506000910152565b60008151808452611c52816020860160208601611c16565b601f01601f19169290920160200192915050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b82811015611cbd57603f19888603018452611cab858351611c3a565b94509285019290850190600101611c8f565b5092979650505050505050565b6020815260006106296020830184611c3a565b6020808252601f908201527f4163636f756e74466163746f72793a206e6f7420616e206163636f756e742e00604082015260600190565b6020808252602a908201527f4163636f756e74466163746f72793a206163636f756e7420616c7265616479206040820152691c9959da5cdd195c995960b21b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8082018082111561042f5761042f611d5e565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112611db457600080fd5b8301803591506001600160401b03821115611dce57600080fd5b602001915036819003821315611de357600080fd5b9250929050565b8284823760609190911b6001600160601b0319169101908152601401919050565b8181038181111561042f5761042f611d5e565b600181811c90821680611e3257607f821691505b602082108103610abc57634e487b7160e01b600052602260045260246000fd5b7402832b936b4b9b9b4b7b7399d1030b1b1b7bab73a1605d1b815260008351611e82816015850160208801611c16565b7001034b99036b4b9b9b4b733903937b6329607d1b6015918401918201528351611eb3816026840160208801611c16565b01602601949350505050565b6001600160a01b038316815260406020820181905260009061191390830184611c3a565b601f821115611f2f576000816000526020600020601f850160051c81016020861015611f0c5750805b601f850160051c820191505b81811015611f2b57828155600101611f18565b5050505b505050565b81516001600160401b03811115611f4d57611f4d611adc565b611f6181611f5b8454611e1e565b84611ee3565b602080601f831160018114611f965760008415611f7e5750858301515b600019600386901b1c1916600185901b178555611f2b565b600085815260208120601f198616915b82811015611fc557888601518255948401946001909101908401611fa6565b5085821015611fe35787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6040815260006120066040830185611c3a565b82810360208401526106258185611c3a565b6001600160a01b03841681526040602082018190528101829052818360608301376000818301606090810191909152601f909201601f1916010192915050565b808202811582820484141761042f5761042f611d5e565b60008161207e5761207e611d5e565b506000190190565b634e487b7160e01b600052603160045260246000fd5b600082516120ae818460208701611c16565b919091019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220538cf797b69d0577cfb61215e8aa13191cf496b4c95d27eda1df0ff0fa18189264736f6c63430008170033"; -bytes constant THIRDWEB_ACCOUNT_IMPL_BYTECODE = hex"60806040526004361061014b5760003560e01c806301ffc9a714610157578063150b7a021461018c5780631626ba7e146101c557806319822f7c146101e557806324d7806c14610213578063399b77da1461023357806347e1da2a146102535780634a58db19146102755780634d44560d1461027d5780635892e2361461029d5780637dff5a79146102bd5780638b52d723146102dd578063938e3d7b146102ff578063a9082d841461031f578063ac9650d81461035e578063b0d691fe1461038b578063b61d27f6146103ad578063b76464d5146103cd578063bc197c81146103ed578063bc66cea214610419578063c45a015514610439578063d087d2881461046d578063d1f5789414610482578063d42f2f35146104a2578063e8a3d485146104b7578063e9523c97146104d9578063f15d424e146104fb578063f23a6e611461052857600080fd5b3661015257005b600080fd5b34801561016357600080fd5b50610177610172366004612d97565b610554565b60405190151581526020015b60405180910390f35b34801561019857600080fd5b506101ac6101a7366004612ea3565b61059a565b6040516001600160e01b03199091168152602001610183565b3480156101d157600080fd5b506101ac6101e0366004612f0e565b6105ab565b3480156101f157600080fd5b50610205610200366004612f6d565b6106ca565b604051908152602001610183565b34801561021f57600080fd5b5061017761022e366004612fba565b6106f0565b34801561023f57600080fd5b5061020561024e366004612fd7565b61071f565b34801561025f57600080fd5b5061027361026e366004613034565b6107ea565b005b610273610951565b34801561028957600080fd5b506102736102983660046130cd565b6109b9565b3480156102a957600080fd5b506102736102b836600461313a565b610a2c565b3480156102c957600080fd5b506101776102d8366004612fba565b610de9565b3480156102e957600080fd5b506102f2610ea2565b6040516101839190613244565b34801561030b57600080fd5b5061027361031a3660046132a8565b6110e9565b34801561032b57600080fd5b5061033f61033a36600461313a565b61113a565b6040805192151583526001600160a01b03909116602083015201610183565b34801561036a57600080fd5b5061037e6103793660046132f0565b611191565b6040516101839190613381565b34801561039757600080fd5b506103a06112f6565b60405161018391906133d8565b3480156103b957600080fd5b506102736103c83660046133ec565b61133f565b3480156103d957600080fd5b506102736103e8366004612fba565b6113cf565b3480156103f957600080fd5b506101ac6104083660046134d9565b63bc197c8160e01b95945050505050565b34801561042557600080fd5b50610177610434366004613586565b611401565b34801561044557600080fd5b506103a07f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b81565b34801561047957600080fd5b506102056116c5565b34801561048e57600080fd5b5061027361049d3660046135cb565b611745565b3480156104ae57600080fd5b506102f26118fd565b3480156104c357600080fd5b506104cc611a6e565b6040516101839190613612565b3480156104e557600080fd5b506104ee611b06565b6040516101839190613625565b34801561050757600080fd5b5061051b610516366004612fba565b611b18565b6040516101839190613672565b34801561053457600080fd5b506101ac610543366004613685565b63f23a6e6160e01b95945050505050565b60006001600160e01b03198216630271189760e51b148061058557506001600160e01b03198216630a85bd0160e11b145b80610594575061059482611bf0565b92915050565b630a85bd0160e11b5b949350505050565b6000806105b78461071f565b905060006105c58285611c25565b90506105d0816106f0565b156105e75750630b135d3f60e11b91506105949050565b3360006105f2611c49565b6001600160a01b038416600090815260069190910160205260409020905061061a8183611c6d565b8061064a575061062981611c8f565b600114801561064a5750600061063f8282611c99565b6001600160a01b0316145b6106a75760405162461bcd60e51b8152602060048201526024808201527f4163636f756e743a2063616c6c6572206e6f7420617070726f7665642074617260448201526333b2ba1760e11b60648201526084015b60405180910390fd5b6106b083610de9565b156106c057630b135d3f60e11b94505b5050505092915050565b60006106d4611ca5565b6106de8484611d0e565b90506106e982611e53565b9392505050565b60006106fa611c49565b6001600160a01b03909216600090815260049290920160205250604090205460ff1690565b6000808260405160200161073591815260200190565b60405160208183030381529060405280519060200120905060007f82cac545155fcbf147f2a9013809613677ac7d65498556e6d19ce43bcbf6c2848260405160200161078b929190918252602082015260400190565b6040516020818303038152906040528051906020012090506107ab611ea0565b60405161190160f01b60208201526022810191909152604281018290526062016040516020818303038152906040528051906020012092505050919050565b6107f26112f6565b6001600160a01b0316336001600160a01b031614806108155750610815336106f0565b6108315760405162461bcd60e51b815260040161069e906136ed565b610839611fc7565b848114801561084757508483145b6108935760405162461bcd60e51b815260206004820152601d60248201527f4163636f756e743a2077726f6e67206172726179206c656e677468732e000000604482015260640161069e565b60005b858110156109485761093f8787838181106108b3576108b361372e565b90506020020160208101906108c89190612fba565b8686848181106108da576108da61372e565b905060200201358585858181106108f3576108f361372e565b90506020028101906109059190613744565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506120ad92505050565b50600101610896565b50505050505050565b6109596112f6565b6001600160a01b031663b760faf934306040518363ffffffff1660e01b815260040161098591906133d8565b6000604051808303818588803b15801561099e57600080fd5b505af11580156109b2573d6000803e3d6000fd5b5050505050565b6109c161211e565b6109c96112f6565b6001600160a01b031663205c287883836040518363ffffffff1660e01b81526004016109f692919061378a565b600060405180830381600087803b158015610a1057600080fd5b505af1158015610a24573d6000803e3d6000fd5b505050505050565b6000610a3b6020850185612fba565b905042610a4e60e0860160c087016137ba565b6001600160801b031611158015610a7d5750610a71610100850160e086016137ba565b6001600160801b031642105b610ab35760405162461bcd60e51b8152602060048201526007602482015266085c195c9a5bd960ca1b604482015260640161069e565b600080610ac186868661113a565b9150915081610afb5760405162461bcd60e51b815260040161069e906020808252600490820152632173696760e01b604082015260600190565b6001610b05611c49565b610100880135600090815260079190910160209081526040808320805460ff1916941515949094179093559091610b41919089019089016137e6565b60ff161115610b6e576000610b5c60408801602089016137e6565b60ff166001149050610948848261215c565b610b77836106f0565b15610bac5760405162461bcd60e51b815260206004820152600560248201526430b236b4b760d91b604482015260640161069e565b610bc183610bb8611c49565b60020190612231565b50604051806060016040528087606001358152602001876080016020810190610bea91906137ba565b6001600160801b03168152602001610c0860c0890160a08a016137ba565b6001600160801b03169052610c1b611c49565b6001600160a01b03851660009081526005919091016020908152604080832084518155918401519301516001600160801b03908116600160801b02931692909217600190920191909155610c91610c70611c49565b6001600160a01b038616600090815260069190910160205260409020612246565b805190915060005b81811015610cfb57610ce8838281518110610cb657610cb661372e565b6020026020010151610cc6611c49565b6001600160a01b03891660009081526006919091016020526040902090612253565b50610cf4600182613817565b9050610c99565b50610d09604089018961382a565b9050905060005b81811015610d8a57610d77610d2860408b018b61382a565b83818110610d3857610d3861372e565b9050602002016020810190610d4d9190612fba565b610d55611c49565b6001600160a01b03891660009081526006919091016020526040902090612231565b50610d83600182613817565b9050610d10565b50610d9488612268565b846001600160a01b0316836001600160a01b03167ff21d10c26e35863a8df291aca54181ee8c4a3bc8e00246c3f7a5a14b69d826a78a604051610dd79190613904565b60405180910390a35050505050505050565b600080610df4611c49565b6001600160a01b038416600090815260059190910160209081526040918290208251606081018452815481526001909101546001600160801b03808216938301849052600160801b90910416928101929092529091504210801590610e65575080604001516001600160801b031642105b80156106e957506000610e9a610e79611c49565b6001600160a01b038616600090815260069190910160205260409020611c8f565b119392505050565b60606000610eb9610eb1611c49565b600201612246565b80519091506000805b82811015610f4a57610eec848281518110610edf57610edf61372e565b6020026020010151610de9565b15610f035781610efb816139ef565b925050610f38565b6000848281518110610f1757610f1761372e565b60200260200101906001600160a01b031690816001600160a01b0316815250505b610f43600182613817565b9050610ec2565b50806001600160401b03811115610f6357610f63612de6565b604051908082528060200260200182016040528015610f9c57816020015b610f89612d4d565b815260200190600190039081610f815790505b5093506000805b838110156110e15760006001600160a01b0316858281518110610fc857610fc861372e565b60200260200101516001600160a01b0316146110cf576000858281518110610ff257610ff261372e565b602002602001015190506000611006611c49565b6001600160a01b038316600081815260059290920160209081526040928390208351606081018552815481526001909101546001600160801b0380821683850152600160801b9091041681850152835160a081019094529183529092508101611070610c70611c49565b81526020018260000151815260200182602001516001600160801b0316815260200182604001516001600160801b03168152508885806110af906139ef565b9650815181106110c1576110c161372e565b602002602001018190525050505b6110da600182613817565b9050610fa3565b505050505090565b6110f16122fd565b61112e5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b604482015260640161069e565b61113781612315565b50565b600080611150611149866123fc565b8585612540565b905061115a611c49565b6101008601356000908152600791909101602052604090205460ff161580156111875750611187816106f0565b9150935093915050565b6060816001600160401b038111156111ab576111ab612de6565b6040519080825280602002602001820160405280156111de57816020015b60608152602001906001900390816111c95790505b509050336000805b848110156112ed578115611265576112433087878481811061120a5761120a61372e565b905060200281019061121c9190613744565b8660405160200161122f93929190613a08565b604051602081830303815290604052612592565b8482815181106112555761125561372e565b60200260200101819052506112e5565b6112c73087878481811061127b5761127b61372e565b905060200281019061128d9190613744565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061259292505050565b8482815181106112d9576112d961372e565b60200260200101819052505b6001016111e6565b50505092915050565b6000806113016125b7565b546001600160a01b03169050801561131857919050565b7f0000000000000000000000000000000071727de22e5e9d8baf0edac6f37da03291505090565b6113476112f6565b6001600160a01b0316336001600160a01b0316148061136a575061136a336106f0565b6113865760405162461bcd60e51b815260040161069e906136ed565b61138e611fc7565b6109b2848484848080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506120ad92505050565b6113d761211e565b806113e06125b7565b80546001600160a01b0319166001600160a01b039290921691909117905550565b600061140b611c49565b6001600160a01b0384166000908152600491909101602052604090205460ff161561143857506001610594565b6000611442611c49565b6001600160a01b0385166000908152600591909101602090815260408083208151606081018352815481526001909101546001600160801b0380821694830194909452600160801b900490921690820152915061149d611c49565b6006016000866001600160a01b03166001600160a01b0316815260200190815260200160002090504282602001516001600160801b031611806114ed575081604001516001600160801b03164210155b806114fe57506114fc81611c8f565b155b1561150e57600092505050610594565b60006115256115206060870187613744565b6125db565b9050600061153283611c8f565b6001148015611553575060006115488482611c99565b6001600160a01b0316145b90506324f16c0560e11b6001600160e01b03198316016115ca5760008061158561158060608a018a613744565b612615565b91509150826115ab576115988583611c6d565b6115ab5760009650505050505050610594565b85518111156115c35760009650505050505050610594565b50506116b8565b635c0f12eb60e11b6001600160e01b03198316016116ab576000806115fa6115f560608a018a613744565b61267a565b50915091508261165a5760005b82518110156116585761163c8382815181106116255761162561372e565b602002602001015187611c6d90919063ffffffff16565b611650576000975050505050505050610594565b600101611607565b505b60005b82518110156116a3578181815181106116785761167861372e565b60200260200101518760000151101561169b576000975050505050505050610594565b60010161165d565b5050506116b8565b6000945050505050610594565b5060019695505050505050565b60006116cf6112f6565b604051631aab3f0d60e11b8152306004820152600060248201526001600160a01b0391909116906335567e1a90604401602060405180830381865afa15801561171c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117409190613a29565b905090565b600061174f6126c7565b5460ff169050600061175f6126c7565b54610100900460ff169050801580801561177c575060018360ff16105b8061179b575061178b306126eb565b15801561179b57508260ff166001145b6117fe5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161069e565b60016118086126c7565b805460ff191660ff92909216919091179055801561184157600161182a6126c7565b80549115156101000261ff00199092169190911790555b6118818686868080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506126fa92505050565b6118896125b7565b6001018190555061189b86600161215c565b8015610a245760006118ab6126c7565b80549115156101000261ff0019909216919091179055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050505050565b6060600061190c610eb1611c49565b8051909150806001600160401b0381111561192957611929612de6565b60405190808252806020026020018201604052801561196257816020015b61194f612d4d565b8152602001906001900390816119475790505b50925060005b81811015611a685760008382815181106119845761198461372e565b602002602001015190506000611998611c49565b6001600160a01b038316600081815260059290920160209081526040928390208351606081018552815481526001909101546001600160801b0380821683850152600160801b9091041681850152835160a081019094529183529092508101611a02610c70611c49565b81526020018260000151815260200182602001516001600160801b0316815260200182604001516001600160801b0316815250868481518110611a4757611a4761372e565b60200260200101819052505050600181611a619190613817565b9050611968565b50505090565b6060611a7861272d565b8054611a8390613a42565b80601f0160208091040260200160405190810160405280929190818152602001828054611aaf90613a42565b8015611afc5780601f10611ad157610100808354040283529160200191611afc565b820191906000526020600020905b815481529060010190602001808311611adf57829003601f168201915b5050505050905090565b6060611740611b13611c49565b612246565b611b20612d4d565b6000611b2a611c49565b6001600160a01b038416600081815260059290920160209081526040928390208351606081018552815481526001909101546001600160801b0380821683850152600160801b9091041681850152835160a081019094529183529092508101611bb5611b94611c49565b6001600160a01b038716600090815260069190910160205260409020612246565b81526020018260000151815260200182602001516001600160801b0316815260200182604001516001600160801b0316815250915050919050565b60006001600160e01b03198216630271189760e51b148061059457506301ffc9a760e01b6001600160e01b0319831614610594565b6000806000611c348585612751565b91509150611c4181612796565b509392505050565b7f3181e78fc1b109bc611fd2406150bf06e33faa75f71cba12c3e1fd670f2def0090565b6001600160a01b038116600090815260018301602052604081205415156106e9565b6000610594825490565b60006106e983836128db565b611cad6112f6565b6001600160a01b0316336001600160a01b031614611d0c5760405162461bcd60e51b815260206004820152601c60248201527b1858d8dbdd5b9d0e881b9bdd08199c9bdb48115b9d1c9e541bda5b9d60221b604482015260640161069e565b565b7b0ca2ba3432b932bab69029b4b3b732b21026b2b9b9b0b3b29d05199960211b6000908152601c829052603c81206000611d8c611d4f610100870187613744565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508693925050611c259050565b9050611d988186611401565b611da757600192505050610594565b6000611db1611c49565b6001600160a01b03929092166000908152600590920160209081526040808420815160608082018452825482526001909201546001600160801b0380821683870152600160801b8204908116928501929092528351928301845295825265ffffffffffff8087169483019490945292831691015260d09290921b6001600160d01b03191660a09290921b65ffffffffffff60a01b169190911795945050505050565b801561113757604051600090339060001990849084818181858888f193505050503d80600081146109b2576040519150601f19603f3d011682016040523d82523d6000602084013e6109b2565b6000306001600160a01b037f000000000000000000000000ffd4505b3452dc22f8473616d50503ba9e1710ac16148015611ef957507f0000000000000000000000000000000000000000000000000000000000007a6946145b15611f2357507fbcdadf6444930a967ffda04923d78c49b3dd65df3ed39abb04a1e3eb1190553790565b50604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527ff0729608244859f656d32ae4cbc6b0367695d68d8e941a28f5e2d33c6d5182dd828401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b60405163c3c5a54760e01b81527f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b906001600160a01b0382169063c3c5a547906120159030906004016133d8565b602060405180830381865afa158015612032573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120569190613a76565b61113757806001600160a01b03166383a03f8c6120716125b7565b600101546040518263ffffffff1660e01b815260040161209391815260200190565b600060405180830381600087803b15801561099e57600080fd5b60606000846001600160a01b031684846040516120ca9190613a98565b60006040518083038185875af1925050503d8060008114612107576040519150601f19603f3d011682016040523d82523d6000602084013e61210c565b606091505b509250905080611c4157815160208301fd5b612127336106f0565b611d0c5760405162461bcd60e51b815260206004820152600660248201526510b0b236b4b760d11b604482015260640161069e565b6121668282612905565b6001600160a01b037f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b163b1561222d5780156121f5577f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b6001600160a01b0316630b61e12b836121d46125b7565b600101546040518363ffffffff1660e01b81526004016109f692919061378a565b7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b6001600160a01b0316639387a380836121d46125b7565b5050565b60006106e9836001600160a01b0384166129b4565b606060006106e983612a03565b60006106e9836001600160a01b038416612a5f565b6001600160a01b037f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b163b15611137576001600160a01b037f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16630b61e12b6122d46020840184612fba565b6122dc6125b7565b600101546040518363ffffffff1660e01b815260040161209392919061378a565b6000612308336106f0565b8061174057505030331490565b600061231f61272d565b805461232a90613a42565b80601f016020809104026020016040519081016040528092919081815260200182805461235690613a42565b80156123a35780601f10612378576101008083540402835291602001916123a3565b820191906000526020600020905b81548152906001019060200180831161238657829003601f168201915b50505050509050816123b361272d565b906123be9082613b01565b507fc9c7c3fe08b88b4df9d4d47ef47d2c43d55c025a0ba88ca442580ed9e7348a1681836040516123f0929190613bc0565b60405180910390a15050565b60607f3fd4a1a1a267c84185e3b7eecd57c68783c0581d538b9d6e5f23e4670497c1e961242c6020840184612fba565b61243c60408501602086016137e6565b612449604086018661382a565b60405160200161245a929190613bee565b60408051601f198184030181529190528051602090910120606086013561248760a08801608089016137ba565b61249760c0890160a08a016137ba565b6124a760e08a0160c08b016137ba565b6124b86101008b0160e08c016137ba565b60408051602081019a909a526001600160a01b039098169789019790975260ff9095166060880152608087019390935260a08601919091526001600160801b0390811660c086015290811660e0850152908116610100848101919091529116610120830152830135610140820152610160016040516020818303038152906040529050919050565b60006105a383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250508751602089012061258c92509050612b52565b90611c25565b60606106e98383604051806060016040528060278152602001613e7160279139612b7f565b7f036f52c1827dab135f7fd44ca0bddde297e2f659c710e0ec53e975f22b54830090565b600060048210156125fe5760405162461bcd60e51b815260040161069e90613c30565b61260c600460008486613c4f565b6106e991613c79565b60008060448310156126395760405162461bcd60e51b815260040161069e90613c30565b612647602460048587613c4f565b8101906126549190612fba565b9150612664604460248587613c4f565b8101906126719190612fd7565b90509250929050565b60608080606484101561269f5760405162461bcd60e51b815260040161069e90613c30565b6126ac8460048188613c4f565b8101906126b99190613d28565b919790965090945092505050565b7f322cf19c484104d3b1a9c2982ebae869ede3fa5f6c4703ca41b9a48c76ee030090565b6001600160a01b03163b151590565b6000828260405160200161270f929190613e0d565b60405160208183030381529060405280519060200120905092915050565b7f4bc804ba64359c0e35e5ed5d90ee596ecaa49a3a930ddcb1470ea0dd625da90090565b60008082516041036127875760208301516040840151606085015160001a61277b87828585612bf7565b9450945050505061278f565b506000905060025b9250929050565b60008160048111156127aa576127aa613e31565b036127b25750565b60018160048111156127c6576127c6613e31565b0361280e5760405162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b604482015260640161069e565b600281600481111561282257612822613e31565b0361286f5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161069e565b600381600481111561288357612883613e31565b036111375760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161069e565b60008260000182815481106128f2576128f261372e565b9060005260206000200154905092915050565b8061290e611c49565b6001600160a01b038416600090815260049190910160205260409020805460ff19169115159190911790558015612957576129518261294b611c49565b90612231565b5061296b565b61296982612963611c49565b90612253565b505b816001600160a01b03167f235bc17e7930760029e9f4d860a2a8089976de5b381cf8380fc11c1d88a11133826040516129a8911515815260200190565b60405180910390a25050565b60008181526001830160205260408120546129fb57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610594565b506000610594565b606081600001805480602002602001604051908101604052809291908181526020018280548015612a5357602002820191906000526020600020905b815481526020019060010190808311612a3f575b50505050509050919050565b60008181526001830160205260408120548015612b48576000612a83600183613e47565b8554909150600090612a9790600190613e47565b9050818114612afc576000866000018281548110612ab757612ab761372e565b9060005260206000200154905080876000018481548110612ada57612ada61372e565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612b0d57612b0d613e5a565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610594565b6000915050610594565b6000610594612b5f611ea0565b8360405161190160f01b8152600281019290925260228201526042902090565b6060600080856001600160a01b031685604051612b9c9190613a98565b600060405180830381855af49150503d8060008114612bd7576040519150601f19603f3d011682016040523d82523d6000602084013e612bdc565b606091505b5091509150612bed86838387612cb1565b9695505050505050565b6000806fa2a8918ca85bafe22016d0b997e4df60600160ff1b03831115612c245750600090506003612ca8565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015612c78573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116612ca157600060019250925050612ca8565b9150600090505b94509492505050565b60608315612d1e578251600003612d1757612ccb856126eb565b612d175760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161069e565b50816105a3565b6105a38383815115612d335781518083602001fd5b8060405162461bcd60e51b815260040161069e9190613612565b6040518060a0016040528060006001600160a01b03168152602001606081526020016000815260200160006001600160801b0316815260200160006001600160801b031681525090565b600060208284031215612da957600080fd5b81356001600160e01b0319811681146106e957600080fd5b6001600160a01b038116811461113757600080fd5b8035612de181612dc1565b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715612e2457612e24612de6565b604052919050565b60006001600160401b03831115612e4557612e45612de6565b612e58601f8401601f1916602001612dfc565b9050828152838383011115612e6c57600080fd5b828260208301376000602084830101529392505050565b600082601f830112612e9457600080fd5b6106e983833560208501612e2c565b60008060008060808587031215612eb957600080fd5b8435612ec481612dc1565b93506020850135612ed481612dc1565b92506040850135915060608501356001600160401b03811115612ef657600080fd5b612f0287828801612e83565b91505092959194509250565b60008060408385031215612f2157600080fd5b8235915060208301356001600160401b03811115612f3e57600080fd5b612f4a85828601612e83565b9150509250929050565b60006101208284031215612f6757600080fd5b50919050565b600080600060608486031215612f8257600080fd5b83356001600160401b03811115612f9857600080fd5b612fa486828701612f54565b9660208601359650604090950135949350505050565b600060208284031215612fcc57600080fd5b81356106e981612dc1565b600060208284031215612fe957600080fd5b5035919050565b60008083601f84011261300257600080fd5b5081356001600160401b0381111561301957600080fd5b6020830191508360208260051b850101111561278f57600080fd5b6000806000806000806060878903121561304d57600080fd5b86356001600160401b038082111561306457600080fd5b6130708a838b01612ff0565b9098509650602089013591508082111561308957600080fd5b6130958a838b01612ff0565b909650945060408901359150808211156130ae57600080fd5b506130bb89828a01612ff0565b979a9699509497509295939492505050565b600080604083850312156130e057600080fd5b82356130eb81612dc1565b946020939093013593505050565b60008083601f84011261310b57600080fd5b5081356001600160401b0381111561312257600080fd5b60208301915083602082850101111561278f57600080fd5b60008060006040848603121561314f57600080fd5b83356001600160401b038082111561316657600080fd5b61317287838801612f54565b9450602086013591508082111561318857600080fd5b50613195868287016130f9565b9497909650939450505050565b6001600160801b03169052565b80516001600160a01b03908116835260208083015160a082860181905281519086018190526000939183019290849060c08801905b80831015613206578551851682529483019460019290920191908301906131e4565b50604087015160408901526060870151945061322560608901866131a2565b6080870151945061323960808901866131a2565b979650505050505050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b8281101561329b57603f198886030184526132898583516131af565b9450928501929085019060010161326d565b5092979650505050505050565b6000602082840312156132ba57600080fd5b81356001600160401b038111156132d057600080fd5b8201601f810184136132e157600080fd5b6105a384823560208401612e2c565b6000806020838503121561330357600080fd5b82356001600160401b0381111561331957600080fd5b61332585828601612ff0565b90969095509350505050565b60005b8381101561334c578181015183820152602001613334565b50506000910152565b6000815180845261336d816020860160208601613331565b601f01601f19169290920160200192915050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b8281101561329b57603f198886030184526133c6858351613355565b945092850192908501906001016133aa565b6001600160a01b0391909116815260200190565b6000806000806060858703121561340257600080fd5b843561340d81612dc1565b93506020850135925060408501356001600160401b0381111561342f57600080fd5b61343b878288016130f9565b95989497509550505050565b60006001600160401b0382111561346057613460612de6565b5060051b60200190565b600082601f83011261347b57600080fd5b8135602061349061348b83613447565b612dfc565b8083825260208201915060208460051b8701019350868411156134b257600080fd5b602086015b848110156134ce57803583529183019183016134b7565b509695505050505050565b600080600080600060a086880312156134f157600080fd5b85356134fc81612dc1565b9450602086013561350c81612dc1565b935060408601356001600160401b038082111561352857600080fd5b61353489838a0161346a565b9450606088013591508082111561354a57600080fd5b61355689838a0161346a565b9350608088013591508082111561356c57600080fd5b5061357988828901612e83565b9150509295509295909350565b6000806040838503121561359957600080fd5b82356135a481612dc1565b915060208301356001600160401b038111156135bf57600080fd5b612f4a85828601612f54565b6000806000604084860312156135e057600080fd5b83356135eb81612dc1565b925060208401356001600160401b0381111561360657600080fd5b613195868287016130f9565b6020815260006106e96020830184613355565b6020808252825182820181905260009190848201906040850190845b818110156136665783516001600160a01b031683529284019291840191600101613641565b50909695505050505050565b6020815260006106e960208301846131af565b600080600080600060a0868803121561369d57600080fd5b85356136a881612dc1565b945060208601356136b881612dc1565b9350604086013592506060860135915060808601356001600160401b038111156136e157600080fd5b61357988828901612e83565b60208082526021908201527f4163636f756e743a206e6f742061646d696e206f7220456e747279506f696e746040820152601760f91b606082015260800190565b634e487b7160e01b600052603260045260246000fd5b6000808335601e1984360301811261375b57600080fd5b8301803591506001600160401b0382111561377557600080fd5b60200191503681900382131561278f57600080fd5b6001600160a01b03929092168252602082015260400190565b80356001600160801b0381168114612de157600080fd5b6000602082840312156137cc57600080fd5b6106e9826137a3565b803560ff81168114612de157600080fd5b6000602082840312156137f857600080fd5b6106e9826137d5565b634e487b7160e01b600052601160045260246000fd5b8082018082111561059457610594613801565b6000808335601e1984360301811261384157600080fd5b8301803591506001600160401b0382111561385b57600080fd5b6020019150600581901b360382131561278f57600080fd5b6000808335601e1984360301811261388a57600080fd5b83016020810192503590506001600160401b038111156138a957600080fd5b8060051b360382131561278f57600080fd5b8183526000602080850194508260005b858110156138f95781356138de81612dc1565b6001600160a01b0316875295820195908201906001016138cb565b509495945050505050565b602081526139256020820161391884612dd6565b6001600160a01b03169052565b6000613933602084016137d5565b60ff811660408401525061394a6040840184613873565b610120806060860152613962610140860183856138bb565b92506060860135608086015261397a608087016137a3565b915061398960a08601836131a2565b61399560a087016137a3565b91506139a460c08601836131a2565b6139b060c087016137a3565b91506139bf60e08601836131a2565b6139cb60e087016137a3565b91506101006139dc818701846131a2565b9590950135939094019290925250919050565b600060018201613a0157613a01613801565b5060010190565b8284823760609190911b6001600160601b0319169101908152601401919050565b600060208284031215613a3b57600080fd5b5051919050565b600181811c90821680613a5657607f821691505b602082108103612f6757634e487b7160e01b600052602260045260246000fd5b600060208284031215613a8857600080fd5b815180151581146106e957600080fd5b60008251613aaa818460208701613331565b9190910192915050565b601f821115613afc576000816000526020600020601f850160051c81016020861015613add5750805b601f850160051c820191505b81811015610a2457828155600101613ae9565b505050565b81516001600160401b03811115613b1a57613b1a612de6565b613b2e81613b288454613a42565b84613ab4565b602080601f831160018114613b635760008415613b4b5750858301515b600019600386901b1c1916600185901b178555610a24565b600085815260208120601f198616915b82811015613b9257888601518255948401946001909101908401613b73565b5085821015613bb05787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b604081526000613bd36040830185613355565b8281036020840152613be58185613355565b95945050505050565b60008184825b85811015613c25578135613c0781612dc1565b6001600160a01b031683526020928301929190910190600101613bf4565b509095945050505050565b602080825260059082015264214461746160d81b604082015260600190565b60008085851115613c5f57600080fd5b83861115613c6c57600080fd5b5050820193919092039150565b6001600160e01b03198135818116916004851015613ca15780818660040360031b1b83161692505b505092915050565b600082601f830112613cba57600080fd5b81356020613cca61348b83613447565b82815260059290921b84018101918181019086841115613ce957600080fd5b8286015b848110156134ce5780356001600160401b03811115613d0c5760008081fd5b613d1a8986838b0101612e83565b845250918301918301613ced565b600080600060608486031215613d3d57600080fd5b83356001600160401b0380821115613d5457600080fd5b818601915086601f830112613d6857600080fd5b81356020613d7861348b83613447565b82815260059290921b8401810191818101908a841115613d9757600080fd5b948201945b83861015613dbe578535613daf81612dc1565b82529482019490820190613d9c565b97505087013592505080821115613dd457600080fd5b613de08783880161346a565b93506040860135915080821115613df657600080fd5b50613e0386828701613ca9565b9150509250925092565b6001600160a01b03831681526040602082018190526000906105a390830184613355565b634e487b7160e01b600052602160045260246000fd5b8181038181111561059457610594613801565b634e487b7160e01b600052603160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220cad4afb4c5b67a0f0c89d57ce9aadc583771ebb0832657a8af9d2f4d729ee64f64736f6c63430008170033"; - diff --git a/src/test/smart-wallet/utils/AABenchmarkPrepare.sol b/src/test/smart-wallet/utils/AABenchmarkPrepare.sol deleted file mode 100644 index d7273abf6..000000000 --- a/src/test/smart-wallet/utils/AABenchmarkPrepare.sol +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -// Test utils -import { BaseTest } from "../../utils/BaseTest.sol"; - -// Account Abstraction setup for smart wallets. -import { IEntryPoint } from "contracts/prebuilts/account/utils/EntryPoint.sol"; -import { Strings } from "contracts/lib/Strings.sol"; -import { AccountFactory } from "contracts/prebuilts/account/non-upgradeable/AccountFactory.sol"; -import "forge-std/Test.sol"; - -contract AABenchmarkPrepare is BaseTest { - AccountFactory private accountFactory; - - function setUp() public override { - super.setUp(); - accountFactory = new AccountFactory( - deployer, - IEntryPoint(payable(address(0x0000000071727De22E5E9d8BAf0edAc6f37da032))) - ); - } - - function test_prepareBenchmarkFile() public { - address accountFactoryAddress = address(accountFactory); - bytes memory accountFactoryBytecode = accountFactoryAddress.code; - - address accountImplAddress = accountFactory.accountImplementation(); - bytes memory accountImplBytecode = accountImplAddress.code; - - string memory accountFactoryAddressString = string.concat( - "address constant THIRDWEB_ACCOUNT_FACTORY_ADDRESS = ", - Strings.toHexStringChecksummed(accountFactoryAddress), - ";" - ); - string memory accountFactoryBytecodeString = string.concat( - 'bytes constant THIRDWEB_ACCOUNT_FACTORY_BYTECODE = hex"', - Strings.toHexStringNoPrefix(accountFactoryBytecode), - '"', - ";" - ); - - string memory accountImplAddressString = string.concat( - "address constant THIRDWEB_ACCOUNT_IMPL_ADDRESS = ", - Strings.toHexStringChecksummed(accountImplAddress), - ";" - ); - string memory accountImplBytecodeString = string.concat( - 'bytes constant THIRDWEB_ACCOUNT_IMPL_BYTECODE = hex"', - Strings.toHexStringNoPrefix(accountImplBytecode), - '"', - ";" - ); - - string memory path = "src/test/smart-wallet/utils/AABenchmarkArtifacts.sol"; - - vm.removeFile(path); - - vm.writeLine(path, ""); - vm.writeLine(path, "pragma solidity ^0.8.0;"); - vm.writeLine(path, "interface ThirdwebAccountFactory {"); - vm.writeLine( - path, - " function createAccount(address _admin, bytes calldata _data) external returns (address);" - ); - vm.writeLine( - path, - " function getAddress(address _adminSigner, bytes calldata _data) external view returns (address);" - ); - vm.writeLine(path, "}"); - - vm.writeLine(path, "interface ThirdwebAccount {"); - vm.writeLine(path, " function execute(address _target, uint256 _value, bytes calldata _calldata) external;"); - vm.writeLine(path, "}"); - vm.writeLine(path, accountFactoryAddressString); - vm.writeLine(path, accountImplAddressString); - vm.writeLine(path, accountFactoryBytecodeString); - vm.writeLine(path, accountImplBytecodeString); - - vm.writeLine(path, ""); - } -} diff --git a/src/test/smart-wallet/utils/AABenchmarkTest.t.sol b/src/test/smart-wallet/utils/AABenchmarkTest.t.sol deleted file mode 100644 index 61b5ee6ed..000000000 --- a/src/test/smart-wallet/utils/AABenchmarkTest.t.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "./AATestBase.sol"; -import { ThirdwebAccountFactory, ThirdwebAccount, THIRDWEB_ACCOUNT_FACTORY_ADDRESS, THIRDWEB_ACCOUNT_IMPL_ADDRESS, THIRDWEB_ACCOUNT_FACTORY_BYTECODE, THIRDWEB_ACCOUNT_IMPL_BYTECODE } from "./AABenchmarkArtifacts.sol"; - -contract ProfileThirdwebAccount is AAGasProfileBase { - ThirdwebAccountFactory factory; - - function setUp() external { - initializeTest("thirdwebAccount"); - factory = ThirdwebAccountFactory(THIRDWEB_ACCOUNT_FACTORY_ADDRESS); - vm.etch(address(factory), THIRDWEB_ACCOUNT_FACTORY_BYTECODE); - vm.etch(THIRDWEB_ACCOUNT_IMPL_ADDRESS, THIRDWEB_ACCOUNT_IMPL_BYTECODE); - setAccount(); - } - - function fillData(address _to, uint256 _value, bytes memory _data) internal view override returns (bytes memory) { - return abi.encodeWithSelector(ThirdwebAccount.execute.selector, _to, _value, _data); - } - - function getSignature(PackedUserOperation memory _op) internal view override returns (bytes memory) { - return signUserOpHash(key, _op); - } - - function createAccount(address _owner) internal override { - // if (address(account).code.length == 0) { - factory.createAccount(_owner, ""); - // } - } - - function getAccountAddr(address _owner) internal view override returns (IAccount) { - return IAccount(factory.getAddress(_owner, "")); - } - - function getInitCode(address _owner) internal view override returns (bytes memory) { - return abi.encodePacked(address(factory), abi.encodeWithSelector(factory.createAccount.selector, _owner, "")); - } - - function getDummySig(PackedUserOperation memory _op) internal pure override returns (bytes memory) { - return - hex"fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"; - } -} diff --git a/src/test/smart-wallet/utils/AATestArtifacts.sol b/src/test/smart-wallet/utils/AATestArtifacts.sol deleted file mode 100644 index 86fcb0aaf..000000000 --- a/src/test/smart-wallet/utils/AATestArtifacts.sol +++ /dev/null @@ -1,9 +0,0 @@ -pragma solidity ^0.8.0; - -bytes constant ENTRYPOINT_0_7_BYTECODE = hex"60806040526004361015610024575b361561001957600080fd5b61002233612748565b005b60003560e01c806242dc5314611b0057806301ffc9a7146119ae5780630396cb60146116765780630bd28e3b146115fa5780631b2e01b814611566578063205c2878146113d157806322cdde4c1461136b57806335567e1a146112b35780635287ce12146111a557806370a0823114611140578063765e827f14610e82578063850aaf6214610dc35780639b249f6914610c74578063b760faf914610c3a578063bb9fe6bf14610a68578063c23a5cea146107c4578063dbed18e0146101a15763fc7e286d0361000e573461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff61013a61229f565b16600052600060205260a0604060002065ffffffffffff6001825492015460405192835260ff8116151560208401526dffffffffffffffffffffffffffff8160081c16604084015263ffffffff8160781c16606084015260981c166080820152f35b600080fd5b3461019c576101af36612317565b906101b86129bd565b60009160005b82811061056f57506101d08493612588565b6000805b8481106102fc5750507fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000809360005b81811061024757610240868660007f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d8180a2613ba7565b6001600255005b6102a261025582848a612796565b73ffffffffffffffffffffffffffffffffffffffff6102766020830161282a565b167f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d600080a2806127d6565b906000915b8083106102b957505050600101610209565b909194976102f36102ed6001926102e78c8b6102e0826102da8e8b8d61269d565b9261265a565b5191613597565b90612409565b99612416565b950191906102a7565b6020610309828789612796565b61031f61031682806127d6565b9390920161282a565b9160009273ffffffffffffffffffffffffffffffffffffffff8091165b8285106103505750505050506001016101d4565b909192939561037f83610378610366848c61265a565b516103728b898b61269d565b856129f6565b9290613dd7565b9116840361050a576104a5576103958491613dd7565b9116610440576103b5576103aa600191612416565b96019392919061033c565b60a487604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f65000000000000000000000000000000000000000000000000000000000000006084820152fd5b608488604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413334207369676e6174757265206572726f720000000000000000000000006064820152fd5b608488604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f414132322065787069726564206f72206e6f74206475650000000000000000006064820152fd5b608489604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413234207369676e6174757265206572726f720000000000000000000000006064820152fd5b61057a818487612796565b9361058585806127d6565b919095602073ffffffffffffffffffffffffffffffffffffffff6105aa82840161282a565b1697600192838a1461076657896105da575b5050505060019293949550906105d191612409565b939291016101be565b8060406105e892019061284b565b918a3b1561019c57929391906040519485937f2dd8113300000000000000000000000000000000000000000000000000000000855288604486016040600488015252606490818601918a60051b8701019680936000915b8c83106106e657505050505050838392610684927ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8560009803016024860152612709565b03818a5afa90816106d7575b506106c657602486604051907f86a9f7500000000000000000000000000000000000000000000000000000000082526004820152fd5b93945084936105d1600189806105bc565b6106e0906121bd565b88610690565b91939596977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9c908a9294969a0301865288357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18336030181121561019c57836107538793858394016128ec565b9a0196019301909189979695949261063f565b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601760248201527f4141393620696e76616c69642061676772656761746f720000000000000000006044820152fd5b3461019c576020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c576107fc61229f565b33600052600082526001604060002001908154916dffffffffffffffffffffffffffff8360081c16928315610a0a5765ffffffffffff8160981c1680156109ac57421061094e5760009373ffffffffffffffffffffffffffffffffffffffff859485947fffffffffffffff000000000000000000000000000000000000000000000000ff86951690556040517fb7c918e0e249f999e965cafeb6c664271b3f4317d296461500e71da39f0cbda33391806108da8786836020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b0390a2165af16108e8612450565b50156108f057005b606490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601860248201527f6661696c656420746f207769746864726177207374616b6500000000000000006044820152fd5b606485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601b60248201527f5374616b65207769746864726177616c206973206e6f742064756500000000006044820152fd5b606486604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601d60248201527f6d7573742063616c6c20756e6c6f636b5374616b6528292066697273740000006044820152fd5b606485604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601460248201527f4e6f207374616b6520746f2077697468647261770000000000000000000000006044820152fd5b3461019c5760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c573360005260006020526001604060002001805463ffffffff8160781c16908115610bdc5760ff1615610b7e5765ffffffffffff908142160191818311610b4f5780547fffffffffffffff000000000000ffffffffffffffffffffffffffffffffffff001678ffffffffffff00000000000000000000000000000000000000609885901b161790556040519116815233907ffa9b3c14cc825c412c9ed81b3ba365a5b459439403f18829e572ed53a4180f0a90602090a2005b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f616c726561647920756e7374616b696e670000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f6e6f74207374616b6564000000000000000000000000000000000000000000006044820152fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c57610022610c6f61229f565b612748565b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043567ffffffffffffffff811161019c576020610cc8610d1b9236906004016122c2565b919073ffffffffffffffffffffffffffffffffffffffff9260405194859283927f570e1a360000000000000000000000000000000000000000000000000000000084528560048501526024840191612709565b03816000857f000000000000000000000000efc2c1444ebcc4db75e7613d20c6a62ff67a167c165af1908115610db757602492600092610d86575b50604051917f6ca7b806000000000000000000000000000000000000000000000000000000008352166004820152fd5b610da991925060203d602011610db0575b610da181836121ed565b8101906126dd565b9083610d56565b503d610d97565b6040513d6000823e3d90fd5b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c57610dfa61229f565b60243567ffffffffffffffff811161019c57600091610e1e839236906004016122c2565b90816040519283928337810184815203915af4610e39612450565b90610e7e6040519283927f99410554000000000000000000000000000000000000000000000000000000008452151560048401526040602484015260448301906123c6565b0390fd5b3461019c57610e9036612317565b610e9b9291926129bd565b610ea483612588565b60005b848110610f1c57506000927fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000915b858310610eec576102408585613ba7565b909193600190610f12610f0087898761269d565b610f0a888661265a565b519088613597565b0194019190610edb565b610f47610f40610f2e8385979561265a565b51610f3a84898761269d565b846129f6565b9190613dd7565b73ffffffffffffffffffffffffffffffffffffffff929183166110db5761107657610f7190613dd7565b911661101157610f8657600101929092610ea7565b60a490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f65000000000000000000000000000000000000000000000000000000000000006084820152fd5b608482604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413334207369676e6174757265206572726f720000000000000000000000006064820152fd5b608483604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f414132322065787069726564206f72206e6f74206475650000000000000000006064820152fd5b608484604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601460448201527f41413234207369676e6174757265206572726f720000000000000000000000006064820152fd5b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff61118c61229f565b1660005260006020526020604060002054604051908152f35b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5773ffffffffffffffffffffffffffffffffffffffff6111f161229f565b6000608060405161120181612155565b828152826020820152826040820152826060820152015216600052600060205260a06040600020608060405161123681612155565b6001835493848352015490602081019060ff8316151582526dffffffffffffffffffffffffffff60408201818560081c16815263ffffffff936060840193858760781c16855265ffffffffffff978891019660981c1686526040519788525115156020880152511660408601525116606084015251166080820152f35b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760206112ec61229f565b73ffffffffffffffffffffffffffffffffffffffff6113096122f0565b911660005260018252604060002077ffffffffffffffffffffffffffffffffffffffffffffffff821660005282526040600020547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006040519260401b16178152f35b3461019c577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60208136011261019c576004359067ffffffffffffffff821161019c5761012090823603011261019c576113c9602091600401612480565b604051908152f35b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5761140861229f565b60243590336000526000602052604060002090815491828411611508576000808573ffffffffffffffffffffffffffffffffffffffff8295839561144c848a612443565b90556040805173ffffffffffffffffffffffffffffffffffffffff831681526020810185905233917fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb91a2165af16114a2612450565b50156114aa57005b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6661696c656420746f20776974686472617700000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f576974686472617720616d6f756e7420746f6f206c61726765000000000000006044820152fd5b3461019c5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5761159d61229f565b73ffffffffffffffffffffffffffffffffffffffff6115ba6122f0565b9116600052600160205277ffffffffffffffffffffffffffffffffffffffffffffffff604060002091166000526020526020604060002054604051908152f35b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043577ffffffffffffffffffffffffffffffffffffffffffffffff811680910361019c5733600052600160205260406000209060005260205260406000206116728154612416565b9055005b6020807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5760043563ffffffff9182821680920361019c5733600052600081526040600020928215611950576001840154908160781c1683106118f2576116f86dffffffffffffffffffffffffffff9182349160081c16612409565b93841561189457818511611836579065ffffffffffff61180592546040519061172082612155565b8152848101926001845260408201908816815260608201878152600160808401936000855233600052600089526040600020905181550194511515917fffffffffffffffffffffffffff0000000000000000000000000000000000000060ff72ffffffff0000000000000000000000000000006effffffffffffffffffffffffffff008954945160081b16945160781b1694169116171717835551167fffffffffffffff000000000000ffffffffffffffffffffffffffffffffffffff78ffffffffffff0000000000000000000000000000000000000083549260981b169116179055565b6040519283528201527fa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c0160403392a2005b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152600e60248201527f7374616b65206f766572666c6f770000000000000000000000000000000000006044820152fd5b606483604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601260248201527f6e6f207374616b652073706563696669656400000000000000000000000000006044820152fd5b606482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601c60248201527f63616e6e6f7420646563726561736520756e7374616b652074696d65000000006044820152fd5b606482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601a60248201527f6d757374207370656369667920756e7374616b652064656c61790000000000006044820152fd5b3461019c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c576004357fffffffff00000000000000000000000000000000000000000000000000000000811680910361019c57807f60fc6b6e0000000000000000000000000000000000000000000000000000000060209214908115611ad6575b8115611aac575b8115611a82575b8115611a58575b506040519015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501482611a4d565b7f3e84f0210000000000000000000000000000000000000000000000000000000081149150611a46565b7fcf28ef970000000000000000000000000000000000000000000000000000000081149150611a3f565b7f915074d80000000000000000000000000000000000000000000000000000000081149150611a38565b3461019c576102007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261019c5767ffffffffffffffff60043581811161019c573660238201121561019c57611b62903690602481600401359101612268565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016101c0811261019c5761014060405191611b9e83612155565b1261019c5760405192611bb0846121a0565b60243573ffffffffffffffffffffffffffffffffffffffff8116810361019c578452602093604435858201526064356040820152608435606082015260a435608082015260c43560a082015260e43560c08201526101043573ffffffffffffffffffffffffffffffffffffffff8116810361019c5760e08201526101243561010082015261014435610120820152825261016435848301526101843560408301526101a43560608301526101c43560808301526101e43590811161019c57611c7c9036906004016122c2565b905a3033036120f7578351606081015195603f5a0260061c61271060a0840151890101116120ce5760009681519182611ff0575b5050505090611cca915a9003608085015101923691612268565b925a90600094845193611cdc85613ccc565b9173ffffffffffffffffffffffffffffffffffffffff60e0870151168015600014611ea957505073ffffffffffffffffffffffffffffffffffffffff855116935b5a9003019360a06060820151910151016080860151850390818111611e95575b50508302604085015192818410600014611dce5750506003811015611da157600203611d79576113c99293508093611d7481613d65565b613cf6565b5050507fdeadaa51000000000000000000000000000000000000000000000000000000008152fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b81611dde92979396940390613c98565b506003841015611e6857507f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f60808683015192519473ffffffffffffffffffffffffffffffffffffffff865116948873ffffffffffffffffffffffffffffffffffffffff60e0890151169701519160405192835215898301528760408301526060820152a46113c9565b807f4e487b7100000000000000000000000000000000000000000000000000000000602492526021600452fd5b6064919003600a0204909301928780611d3d565b8095918051611eba575b5050611d1d565b6003861015611fc1576002860315611eb35760a088015190823b1561019c57600091611f2491836040519586809581947f7c627b210000000000000000000000000000000000000000000000000000000083528d60048401526080602484015260848301906123c6565b8b8b0260448301528b60648301520393f19081611fad575b50611fa65787893d610800808211611f9e575b506040519282828501016040528184528284013e610e7e6040519283927fad7954bc000000000000000000000000000000000000000000000000000000008452600484015260248301906123c6565b905083611f4f565b8980611eb3565b611fb89199506121bd565b6000978a611f3c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b91600092918380938c73ffffffffffffffffffffffffffffffffffffffff885116910192f115612023575b808080611cb0565b611cca929195503d6108008082116120c6575b5060405190888183010160405280825260008983013e805161205f575b5050600194909161201b565b7f1c4fada7374c0a9ee8841fc38afe82932dc0f8e69012e927f061a8bae611a20188870151918973ffffffffffffffffffffffffffffffffffffffff8551169401516120bc604051928392835260408d84015260408301906123c6565b0390a38680612053565b905088612036565b877fdeaddead000000000000000000000000000000000000000000000000000000006000526000fd5b606486604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601760248201527f4141393220696e7465726e616c2063616c6c206f6e6c790000000000000000006044820152fd5b60a0810190811067ffffffffffffffff82111761217157604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610140810190811067ffffffffffffffff82111761217157604052565b67ffffffffffffffff811161217157604052565b6060810190811067ffffffffffffffff82111761217157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761217157604052565b67ffffffffffffffff811161217157601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b9291926122748261222e565b9161228260405193846121ed565b82948184528183011161019c578281602093846000960137010152565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361019c57565b9181601f8401121561019c5782359167ffffffffffffffff831161019c576020838186019501011161019c57565b6024359077ffffffffffffffffffffffffffffffffffffffffffffffff8216820361019c57565b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261019c5760043567ffffffffffffffff9283821161019c578060238301121561019c57816004013593841161019c5760248460051b8301011161019c57602401919060243573ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b60005b8381106123b65750506000910152565b81810151838201526020016123a6565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612402815180928187528780880191016123a3565b0116010190565b91908201809211610b4f57565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610b4f5760010190565b91908203918211610b4f57565b3d1561247b573d906124618261222e565b9161246f60405193846121ed565b82523d6000602084013e565b606090565b604061248e8183018361284b565b90818351918237206124a3606084018461284b565b90818451918237209260c06124bb60e083018361284b565b908186519182372091845195602087019473ffffffffffffffffffffffffffffffffffffffff833516865260208301358789015260608801526080870152608081013560a087015260a081013582870152013560e08501526101009081850152835261012083019167ffffffffffffffff918484108385111761217157838252845190206101408501908152306101608601524661018086015260608452936101a00191821183831017612171575251902090565b67ffffffffffffffff81116121715760051b60200190565b9061259282612570565b6040906125a260405191826121ed565b8381527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06125d08295612570565b019160005b8381106125e25750505050565b60209082516125f081612155565b83516125fb816121a0565b600081526000849181838201528187820152816060818184015260809282848201528260a08201528260c08201528260e082015282610100820152826101208201528652818587015281898701528501528301528286010152016125d5565b805182101561266e5760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b919081101561266e5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18136030182121561019c570190565b9081602091031261019c575173ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b7f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4602073ffffffffffffffffffffffffffffffffffffffff61278a3485613c98565b936040519485521692a2565b919081101561266e5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa18136030182121561019c570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561019c570180359067ffffffffffffffff821161019c57602001918160051b3603831361019c57565b3573ffffffffffffffffffffffffffffffffffffffff8116810361019c5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561019c570180359067ffffffffffffffff821161019c5760200191813603831361019c57565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561019c57016020813591019167ffffffffffffffff821161019c57813603831361019c57565b61012091813573ffffffffffffffffffffffffffffffffffffffff811680910361019c576129626129476129ba9561299b93855260208601356020860152612937604087018761289c565b9091806040880152860191612709565b612954606086018661289c565b908583036060870152612709565b6080840135608084015260a084013560a084015260c084013560c084015261298d60e085018561289c565b9084830360e0860152612709565b916129ac610100918281019061289c565b929091818503910152612709565b90565b60028054146129cc5760028055565b60046040517f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b926000905a93805194843573ffffffffffffffffffffffffffffffffffffffff811680910361019c5786526020850135602087015260808501356fffffffffffffffffffffffffffffffff90818116606089015260801c604088015260a086013560c088015260c086013590811661010088015260801c610120870152612a8060e086018661284b565b801561357b576034811061351d578060141161019c578060241161019c5760341161019c57602481013560801c60a0880152601481013560801c60808801523560601c60e08701525b612ad285612480565b60208301526040860151946effffffffffffffffffffffffffffff8660c08901511760608901511760808901511760a0890151176101008901511761012089015117116134bf57604087015160608801510160808801510160a08801510160c0880151016101008801510296835173ffffffffffffffffffffffffffffffffffffffff81511690612b66604085018561284b565b806131e4575b505060e0015173ffffffffffffffffffffffffffffffffffffffff1690600082156131ac575b6020612bd7918b828a01516000868a604051978896879586937f19822f7c00000000000000000000000000000000000000000000000000000000855260048501613db5565b0393f160009181613178575b50612c8b573d8c610800808311612c83575b50604051916020818401016040528083526000602084013e610e7e6040519283927f65c8fd4d000000000000000000000000000000000000000000000000000000008452600484015260606024840152600d60648401527f4141323320726576657274656400000000000000000000000000000000000000608484015260a0604484015260a48301906123c6565b915082612bf5565b9a92939495969798999a91156130f2575b509773ffffffffffffffffffffffffffffffffffffffff835116602084015190600052600160205260406000208160401c60005260205267ffffffffffffffff604060002091825492612cee84612416565b9055160361308d575a8503116130285773ffffffffffffffffffffffffffffffffffffffff60e0606093015116612d42575b509060a09184959697986040608096015260608601520135905a900301910152565b969550505a9683519773ffffffffffffffffffffffffffffffffffffffff60e08a01511680600052600060205260406000208054848110612fc3576080612dcd9a9b9c600093878094039055015192602089015183604051809d819582947f52b7512c0000000000000000000000000000000000000000000000000000000084528c60048501613db5565b039286f1978860009160009a612f36575b50612e86573d8b610800808311612e7e575b50604051916020818401016040528083526000602084013e610e7e6040519283927f65c8fd4d000000000000000000000000000000000000000000000000000000008452600484015260606024840152600d60648401527f4141333320726576657274656400000000000000000000000000000000000000608484015260a0604484015260a48301906123c6565b915082612df0565b9991929394959697989998925a900311612eab57509096959094939291906080612d20565b60a490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602760448201527f41413336206f766572207061796d6173746572566572696669636174696f6e4760648201527f61734c696d6974000000000000000000000000000000000000000000000000006084820152fd5b915098503d90816000823e612f4b82826121ed565b604081838101031261019c5780519067ffffffffffffffff821161019c57828101601f83830101121561019c578181015191612f868361222e565b93612f9460405195866121ed565b838552820160208483850101011161019c57602092612fba9184808701918501016123a3565b01519838612dde565b60848b604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601e60448201527f41413331207061796d6173746572206465706f73697420746f6f206c6f7700006064820152fd5b608490604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601e60448201527f41413236206f76657220766572696669636174696f6e4761734c696d697400006064820152fd5b608482604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601a60448201527f4141323520696e76616c6964206163636f756e74206e6f6e63650000000000006064820152fd5b600052600060205260406000208054808c11613113578b9003905538612c9c565b608484604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601760448201527f41413231206469646e2774207061792070726566756e640000000000000000006064820152fd5b9091506020813d6020116131a4575b81613194602093836121ed565b8101031261019c57519038612be3565b3d9150613187565b508060005260006020526040600020548a81116000146131d75750612bd7602060005b915050612b92565b6020612bd7918c036131cf565b833b61345a57604088510151602060405180927f570e1a360000000000000000000000000000000000000000000000000000000082528260048301528160008161323260248201898b612709565b039273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000efc2c1444ebcc4db75e7613d20c6a62ff67a167c1690f1908115610db75760009161343b575b5073ffffffffffffffffffffffffffffffffffffffff811680156133d6578503613371573b1561330c5760141161019c5773ffffffffffffffffffffffffffffffffffffffff9183887fd51a9c61267aa6196961883ecf5ff2da6619c37dac0fa92122513fb32c032d2d604060e0958787602086015195510151168251913560601c82526020820152a391612b6c565b60848d604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602060448201527f4141313520696e6974436f6465206d757374206372656174652073656e6465726064820152fd5b60848e604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152602060448201527f4141313420696e6974436f6465206d7573742072657475726e2073656e6465726064820152fd5b60848f604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601b60448201527f4141313320696e6974436f6465206661696c6564206f72204f4f4700000000006064820152fd5b613454915060203d602011610db057610da181836121ed565b3861327c565b60848d604051907f220266b6000000000000000000000000000000000000000000000000000000008252600482015260406024820152601f60448201527f414131302073656e64657220616c726561647920636f6e7374727563746564006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f41413934206761732076616c756573206f766572666c6f7700000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4141393320696e76616c6964207061796d6173746572416e64446174610000006044820152fd5b5050600060e087015260006080870152600060a0870152612ac9565b9092915a906060810151916040928351967fffffffff00000000000000000000000000000000000000000000000000000000886135d7606084018461284b565b600060038211613b9f575b7f8dd7712f0000000000000000000000000000000000000000000000000000000094168403613a445750505061379d6000926136b292602088015161363a8a5193849360208501528b602485015260648401906128ec565b90604483015203906136727fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0928381018352826121ed565b61379189519485927e42dc5300000000000000000000000000000000000000000000000000000000602085015261020060248501526102248401906123c6565b613760604484018b60806101a091805173ffffffffffffffffffffffffffffffffffffffff808251168652602082015160208701526040820151604087015260608201516060870152838201518487015260a082015160a087015260c082015160c087015260e08201511660e0860152610100808201519086015261012080910151908501526020810151610140850152604081015161016085015260608101516101808501520151910152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83820301610204840152876123c6565b039081018352826121ed565b6020918183809351910182305af1600051988652156137bf575b505050505050565b909192939495965060003d8214613a3a575b7fdeaddead00000000000000000000000000000000000000000000000000000000810361385b57608487878051917f220266b600000000000000000000000000000000000000000000000000000000835260048301526024820152600f60448201527f41413935206f7574206f662067617300000000000000000000000000000000006064820152fd5b7fdeadaa510000000000000000000000000000000000000000000000000000000091929395949650146000146138c55750506138a961389e6138b8935a90612443565b608085015190612409565b9083015183611d748295613d65565b905b3880808080806137b7565b909261395290828601518651907ff62676f440ff169a3a9afdbf812e89e7f95975ee8e5c31214ffdef631c5f479273ffffffffffffffffffffffffffffffffffffffff9580878551169401516139483d610800808211613a32575b508a519084818301018c5280825260008583013e8a805194859485528401528a8301906123c6565b0390a35a90612443565b916139636080860193845190612409565b926000905a94829488519761397789613ccc565b948260e08b0151168015600014613a1857505050875116955b5a9003019560a06060820151910151019051860390818111613a04575b5050840290850151928184106000146139de57505080611e68575090816139d89293611d7481613d65565b906138ba565b6139ee9082849397950390613c98565b50611e68575090826139ff92613cf6565b6139d8565b6064919003600a02049094019338806139ad565b90919892509751613a2a575b50613990565b955038613a24565b905038613920565b8181803e516137d1565b613b97945082935090613a8c917e42dc53000000000000000000000000000000000000000000000000000000006020613b6b9501526102006024860152610224850191612709565b613b3a604484018860806101a091805173ffffffffffffffffffffffffffffffffffffffff808251168652602082015160208701526040820151604087015260608201516060870152838201518487015260a082015160a087015260c082015160c087015260e08201511660e0860152610100808201519086015261012080910151908501526020810151610140850152604081015161016085015260608101516101808501520151910152565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc83820301610204840152846123c6565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018952886121ed565b60008761379d565b5081356135e2565b73ffffffffffffffffffffffffffffffffffffffff168015613c3a57600080809381935af1613bd4612450565b5015613bdc57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f41413931206661696c65642073656e6420746f2062656e6566696369617279006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4141393020696e76616c69642062656e656669636961727900000000000000006044820152fd5b73ffffffffffffffffffffffffffffffffffffffff166000526000602052613cc66040600020918254612409565b80915590565b610120610100820151910151808214613cf257480180821015613ced575090565b905090565b5090565b9190917f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f6080602083015192519473ffffffffffffffffffffffffffffffffffffffff946020868851169660e089015116970151916040519283526000602084015260408301526060820152a4565b60208101519051907f67b4fa9642f42120bf031f3051d1824b0fe25627945b27b8a6a65d5761d5482e60208073ffffffffffffffffffffffffffffffffffffffff855116940151604051908152a3565b613dcd604092959493956060835260608301906128ec565b9460208201520152565b8015613e6457600060408051613dec816121d1565b828152826020820152015273ffffffffffffffffffffffffffffffffffffffff811690604065ffffffffffff91828160a01c16908115613e5c575b60d01c92825191613e37836121d1565b8583528460208401521691829101524211908115613e5457509091565b905042109091565b839150613e27565b5060009060009056fea2646970667358221220b094fd69f04977ae9458e5ba422d01cd2d20dbcfca0992ff37f19aa07deec25464736f6c63430008170033"; - -bytes constant CREATOR_0_7_BYTECODE = hex"6080600436101561000f57600080fd5b6000803560e01c63570e1a361461002557600080fd5b3461018a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261018a576004359167ffffffffffffffff9081841161018657366023850112156101865783600401358281116101825736602482870101116101825780601411610182577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec810192808411610155577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f81600b8501160116830190838210908211176101555792846024819482600c60209a968b9960405286845289840196603889018837830101525193013560601c5af1908051911561014d575b5073ffffffffffffffffffffffffffffffffffffffff60405191168152f35b90503861012e565b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b8380fd5b8280fd5b80fdfea26469706673582212207adef8895ad3393b02fab10a111d85ea80ff35366aa43995f4ea20e67f29200664736f6c63430008170033"; - -bytes constant VERIFYINGPAYMASTER_BYTECODE = hex"6080604052600436106100b85760003560e01c80630396cb60146100bd578063205c2878146100d257806323d9ac9b146100f257806352b7512c1461013c5780635829c5f51461016a578063715018a6146101985780637c627b21146101ad5780638da5cb5b146101cd57806394d4ad60146101e2578063b0d691fe14610212578063bb9fe6bf14610246578063c23a5cea1461025b578063c399ec881461027b578063d0e30db014610290578063f2fde38b14610298575b600080fd5b6100d06100cb366004610d99565b6102b8565b005b3480156100de57600080fd5b506100d06100ed366004610ddb565b610343565b3480156100fe57600080fd5b506101267f000000000000000000000000000000000000000000000000000000000000000081565b6040516101339190610e07565b60405180910390f35b34801561014857600080fd5b5061015c610157366004610e34565b6103b5565b604051610133929190610e81565b34801561017657600080fd5b5061018a610185366004610ef1565b6103d9565b604051908152602001610133565b3480156101a457600080fd5b506100d06104e9565b3480156101b957600080fd5b506100d06101c8366004610f8f565b6104fd565b3480156101d957600080fd5b50610126610519565b3480156101ee57600080fd5b506102026101fd366004610ff9565b610528565b604051610133949392919061103a565b34801561021e57600080fd5b506101267f000000000000000000000000000000000000000000000000000000000000000081565b34801561025257600080fd5b506100d0610570565b34801561026757600080fd5b506100d0610276366004611086565b6105ed565b34801561028757600080fd5b5061018a61066f565b6100d0610704565b3480156102a457600080fd5b506100d06102b3366004611086565b61076b565b6102c06107e9565b604051621cb65b60e51b815263ffffffff821660048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690630396cb609034906024016000604051808303818588803b15801561032757600080fd5b505af115801561033b573d6000803e3d6000fd5b505050505050565b61034b6107e9565b60405163040b850f60e31b81526001600160a01b038381166004830152602482018390527f0000000000000000000000000000000000000000000000000000000000000000169063205c287890604401600060405180830381600087803b15801561032757600080fd5b606060006103c1610848565b6103cc8585856108b8565b915091505b935093915050565b600083358060208601356103f060408801886110a3565b6040516103fe9291906110e9565b60405190819003902061041460608901896110a3565b6040516104229291906110e9565b604051908190039020608089013561043d60e08b018b6110a3565b61044c916034916014916110f9565b61045591611123565b604080516001600160a01b0390971660208801528601949094526060850192909252608084015260a08084019190915260c08084019290925287013560e0830152860135610100820152466101208201523061014082015265ffffffffffff80861661016083015284166101808201526101a001604051602081830303815290604052805190602001209150509392505050565b6104f16107e9565b6104fb6000610a6f565b565b610505610848565b6105128585858585610abf565b5050505050565b6000546001600160a01b031690565b600080368161053a85603481896110f9565b8101906105479190611141565b9094509250858561055a60346040611174565b6105659282906110f9565b949793965094505050565b6105786107e9565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bb9fe6bf6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156105d357600080fd5b505af11580156105e7573d6000803e3d6000fd5b50505050565b6105f56107e9565b60405163611d2e7560e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063c23a5cea90610641908490600401610e07565b600060405180830381600087803b15801561065b57600080fd5b505af1158015610512573d6000803e3d6000fd5b6040516370a0823160e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a08231906106be903090600401610e07565b602060405180830381865afa1580156106db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106ff9190611195565b905090565b60405163b760faf960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b760faf9903490610752903090600401610e07565b6000604051808303818588803b15801561065b57600080fd5b6107736107e9565b6001600160a01b0381166107dd5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b6107e681610a6f565b50565b336107f2610519565b6001600160a01b0316146104fb5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016107d4565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104fb5760405162461bcd60e51b815260206004820152601560248201527414d95b99195c881b9bdd08115b9d1c9e541bda5b9d605a1b60448201526064016107d4565b60606000808036816108d06101fd60e08b018b6110a3565b9296509094509250905060408114806108e95750604181145b61095d576040805162461bcd60e51b81526020600482015260248101919091527f566572696679696e675061796d61737465723a20696e76616c6964207369676e60448201527f6174757265206c656e67746820696e207061796d6173746572416e644461746160648201526084016107d4565b600061099f61096d8b87876103d9565b7b0ca2ba3432b932bab69029b4b3b732b21026b2b9b9b0b3b29d05199960211b6000908152601c91909152603c902090565b90506109e18184848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610af792505050565b6001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614610a4457610a2560018686610b1d565b60405180602001604052806000815250909650965050505050506103d1565b610a5060008686610b1d565b6040805160208101909152600081529b909a5098505050505050505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60405162461bcd60e51b815260206004820152600d60248201526c6d757374206f7665727269646560981b60448201526064016107d4565b6000806000610b068585610b55565b91509150610b1381610b9a565b5090505b92915050565b600060d08265ffffffffffff16901b60a08465ffffffffffff16901b85610b45576000610b48565b60015b60ff161717949350505050565b6000808251604103610b8b5760208301516040840151606085015160001a610b7f87828585610cdf565b94509450505050610b93565b506000905060025b9250929050565b6000816004811115610bae57610bae6111ae565b03610bb65750565b6001816004811115610bca57610bca6111ae565b03610c125760405162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b60448201526064016107d4565b6002816004811115610c2657610c266111ae565b03610c735760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016107d4565b6003816004811115610c8757610c876111ae565b036107e65760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016107d4565b6000806fa2a8918ca85bafe22016d0b997e4df60600160ff1b03831115610d0c5750600090506003610d90565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015610d60573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116610d8957600060019250925050610d90565b9150600090505b94509492505050565b600060208284031215610dab57600080fd5b813563ffffffff81168114610dbf57600080fd5b9392505050565b6001600160a01b03811681146107e657600080fd5b60008060408385031215610dee57600080fd5b8235610df981610dc6565b946020939093013593505050565b6001600160a01b0391909116815260200190565b60006101208284031215610e2e57600080fd5b50919050565b600080600060608486031215610e4957600080fd5b83356001600160401b03811115610e5f57600080fd5b610e6b86828701610e1b565b9660208601359650604090950135949350505050565b604081526000835180604084015260005b81811015610eaf5760208187018101516060868401015201610e92565b506000606082850101526060601f19601f8301168401019150508260208301529392505050565b803565ffffffffffff81168114610eec57600080fd5b919050565b600080600060608486031215610f0657600080fd5b83356001600160401b03811115610f1c57600080fd5b610f2886828701610e1b565b935050610f3760208501610ed6565b9150610f4560408501610ed6565b90509250925092565b60008083601f840112610f6057600080fd5b5081356001600160401b03811115610f7757600080fd5b602083019150836020828501011115610b9357600080fd5b600080600080600060808688031215610fa757600080fd5b853560038110610fb657600080fd5b945060208601356001600160401b03811115610fd157600080fd5b610fdd88828901610f4e565b9699909850959660408101359660609091013595509350505050565b6000806020838503121561100c57600080fd5b82356001600160401b0381111561102257600080fd5b61102e85828601610f4e565b90969095509350505050565b600065ffffffffffff808716835280861660208401525060606040830152826060830152828460808401376000608084840101526080601f19601f850116830101905095945050505050565b60006020828403121561109857600080fd5b8135610dbf81610dc6565b6000808335601e198436030181126110ba57600080fd5b8301803591506001600160401b038211156110d457600080fd5b602001915036819003821315610b9357600080fd5b8183823760009101908152919050565b6000808585111561110957600080fd5b8386111561111657600080fd5b5050820193919092039150565b80356020831015610b1757600019602084900360031b1b1692915050565b6000806040838503121561115457600080fd5b61115d83610ed6565b915061116b60208401610ed6565b90509250929050565b80820180821115610b1757634e487b7160e01b600052601160045260246000fd5b6000602082840312156111a757600080fd5b5051919050565b634e487b7160e01b600052602160045260246000fdfea2646970667358221220b39fe5fcd01271dac339cd8e4ceb50933262bdaa8ec1586bc91f0eb5eed0e91264736f6c63430008170033"; - -address constant VERIFYINGPAYMASTER_ADDRESS = 0xe1Fb85Ec54767ED89252751F6667CF566b16f1F0; diff --git a/src/test/smart-wallet/utils/AATestBase.sol b/src/test/smart-wallet/utils/AATestBase.sol deleted file mode 100644 index 76dd150a8..000000000 --- a/src/test/smart-wallet/utils/AATestBase.sol +++ /dev/null @@ -1,333 +0,0 @@ -pragma solidity ^0.8.0; - -import { IEntryPoint } from "contracts/prebuilts/account/interfaces/IEntryPoint.sol"; -import { EntryPoint } from "contracts/prebuilts/account/utils/EntryPoint.sol"; -import { PackedUserOperation } from "contracts/prebuilts/account/interfaces/PackedUserOperation.sol"; -import { IAccount } from "contracts/prebuilts/account/interfaces/IAccount.sol"; -import { VERIFYINGPAYMASTER_BYTECODE, VERIFYINGPAYMASTER_ADDRESS, ENTRYPOINT_0_7_BYTECODE, CREATOR_0_7_BYTECODE } from "./AATestArtifacts.sol"; -import { UserOperationLib } from "contracts/prebuilts/account/utils/UserOperationLib.sol"; -import { VerifyingPaymaster } from "./VerifyingPaymaster.sol"; - -import "contracts/external-deps/openzeppelin/utils/cryptography/ECDSA.sol"; -import "forge-std/Test.sol"; -import "forge-std/console.sol"; - -import { MockERC20 } from "../../mocks/MockERC20.sol"; - -interface IVerifyingPaymaster { - function owner() external view returns (address); - - function getHash( - PackedUserOperation calldata userOp, - uint48 validUntil, - uint48 validAfter - ) external view returns (bytes32); -} - -interface VmModified { - function cool(address _target) external; - - function keyExists(string calldata, string calldata) external returns (bool); - - function parseJsonKeys(string calldata json, string calldata key) external pure returns (string[] memory keys); -} - -uint256 constant OV_FIXED = 21000; -uint256 constant OV_PER_USEROP = 18300; -uint256 constant OV_PER_WORD = 4; -uint256 constant OV_PER_ZERO_BYTE = 4; -uint256 constant OV_PER_NONZERO_BYTE = 16; - -abstract contract AAGasProfileBase is Test { - string public name; - string public scenarioName; - uint256 sum; - string jsonObj; - IEntryPoint public entryPoint; - address payable public beneficiary; - IAccount public account; - address public owner; - uint256 public key; - VerifyingPaymaster public paymaster; - address public verifier; - uint256 public verifierKey; - bool public writeGasProfile = false; - - function(PackedUserOperation memory) internal view returns (bytes memory) paymasterData; - function(PackedUserOperation memory) internal view returns (bytes memory) dummyPaymasterData; - - function initializeTest(string memory _name) internal { - writeGasProfile = vm.envOr("WRITE_GAS_PROFILE", false); - name = _name; - address _testEntrypoint = address(new EntryPoint()); - entryPoint = IEntryPoint(payable(address(0x0000000071727De22E5E9d8BAf0edAc6f37da032))); - vm.etch(address(entryPoint), ENTRYPOINT_0_7_BYTECODE); // ENTRYPOINT_0_7_BYTECODE - vm.etch(0xEFC2c1444eBCC4Db75e7613d20C6a62fF67A167C, CREATOR_0_7_BYTECODE); - beneficiary = payable(makeAddr("beneficiary")); - vm.deal(beneficiary, 1e18); - paymasterData = emptyPaymasterAndData; - dummyPaymasterData = emptyPaymasterAndData; - (verifier, verifierKey) = makeAddrAndKey("VERIFIER"); - address _testPaymaster = address(new VerifyingPaymaster(entryPoint, verifier)); - paymaster = VerifyingPaymaster(VERIFYINGPAYMASTER_ADDRESS); - vm.etch(address(paymaster), _testPaymaster.code); // VERIFYINGPAYMASTER_BYTECODE - } - - function setAccount() internal { - (owner, key) = makeAddrAndKey("Owner"); - account = getAccountAddr(owner); - vm.deal(address(account), 1e18); - } - - function packPaymasterStaticFields( - address paymaster, - uint256 validationGasLimit, - uint256 postOpGasLimit, - uint48 validUntil, - uint48 validAfter, - bytes memory signature - ) internal pure returns (bytes memory) { - // Pack the static fields using abi.encodePacked - bytes memory packed = abi.encodePacked( - paymaster, - uint128(validationGasLimit), - uint128(postOpGasLimit), - uint256(validUntil), // Padding to make it 32 bytes - uint256(validAfter) // Padding to make it 32 bytes - ); - - // Append the signature to the packed data - packed = abi.encodePacked(packed, signature); - - return packed; - } - - function fillUserOp(bytes memory _data) internal view returns (PackedUserOperation memory op) { - op.sender = address(account); - op.nonce = entryPoint.getNonce(address(account), 0); - if (address(account).code.length == 0) { - op.initCode = getInitCode(owner); - } - - uint128 verificationGasLimit = 500000; - uint128 callGasLimit = 500000; - bytes32 packedGasLimits = (bytes32(uint256(verificationGasLimit)) << 128) | bytes32(uint256(callGasLimit)); - - bytes memory paymasterData = packPaymasterStaticFields( - address(paymaster), - 100_000, - 100_000, - type(uint48).max, - 0, - hex"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" - ); - - op.callData = _data; - op.accountGasLimits = packedGasLimits; - op.preVerificationGas = 500000; - op.gasFees = (bytes32(uint256(1)) << 128) | bytes32(uint256(1)); - op.signature = getDummySig(op); - op.paymasterAndData = dummyPaymasterData(op); - op.preVerificationGas = calculatePreVerificationGas(op); - op.paymasterAndData = paymasterData; - - bytes32 paymasterHash = paymaster.getHash(op, type(uint48).max, 0); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(verifierKey, ECDSA.toEthSignedMessageHash(paymasterHash)); - bytes memory signature = abi.encodePacked(r, s, v); - - op.paymasterAndData = packPaymasterStaticFields( - address(paymaster), - 100_000, - 100_000, - type(uint48).max, - 0, - signature - ); - op.signature = getSignature(op); - } - - function signUserOpHash( - uint256 _key, - PackedUserOperation memory _op - ) internal view returns (bytes memory signature) { - bytes32 hash = entryPoint.getUserOpHash(_op); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_key, ECDSA.toEthSignedMessageHash(hash)); - signature = abi.encodePacked(r, s, v); - } - - function executeUserOp(PackedUserOperation memory _op, string memory _test, uint256 _value) internal { - PackedUserOperation[] memory ops = new PackedUserOperation[](1); - ops[0] = _op; - uint256 eth_before; - if (_op.paymasterAndData.length > 0) { - eth_before = entryPoint.balanceOf(address(paymaster)); - } else { - eth_before = entryPoint.balanceOf(address(account)) + address(account).balance; - } - // vm.cool to be introduced to foundry - //VmModified(address(vm)).cool(address(entryPoint)); - //VmModified(address(vm)).cool(address(account)); - entryPoint.handleOps(ops, beneficiary); - uint256 eth_after; - if (_op.paymasterAndData.length > 0) { - eth_after = entryPoint.balanceOf(address(paymaster)); - } else { - eth_after = entryPoint.balanceOf(address(account)) + address(account).balance + _value; - } - if (!writeGasProfile) { - console.log("case - %s", _test); - console.log(" gasUsed : ", eth_before - eth_after); - console.log(" calldatacost : ", calldataCost(pack(_op))); - } - if (writeGasProfile && bytes(scenarioName).length > 0) { - uint256 gasUsed = eth_before - eth_after; - vm.serializeUint(jsonObj, _test, gasUsed); - sum += gasUsed; - } - } - - function testCreation() internal { - PackedUserOperation memory op = fillUserOp(fillData(address(0), 0, "")); - executeUserOp(op, "creation", 0); - } - - function testTransferNative(address _recipient, uint256 _amount) internal { - vm.skip(writeGasProfile); - createAccount(owner); - _amount = bound(_amount, 1, address(account).balance / 2); - PackedUserOperation memory op = fillUserOp(fillData(_recipient, _amount, "")); - executeUserOp(op, "native", _amount); - } - - function testTransferNative() internal { - createAccount(owner); - uint256 amount = 5e17; - address recipient = makeAddr("recipient"); - PackedUserOperation memory op = fillUserOp(fillData(recipient, amount, "")); - executeUserOp(op, "native", amount); - } - - function testTransferERC20() internal { - createAccount(owner); - MockERC20 mockERC20 = new MockERC20(); - mockERC20.mint(address(account), 1e18); - uint256 amount = 5e17; - address recipient = makeAddr("recipient"); - uint256 balance = mockERC20.balanceOf(recipient); - PackedUserOperation memory op = fillUserOp( - fillData(address(mockERC20), 0, abi.encodeWithSelector(mockERC20.transfer.selector, recipient, amount)) - ); - executeUserOp(op, "erc20", 0); - assertEq(mockERC20.balanceOf(recipient), balance + amount); - } - - function testBenchmark1Vanila() external { - scenarioName = "vanila"; - jsonObj = string(abi.encodePacked(scenarioName, " ", name)); - entryPoint.depositTo{ value: 1000e18 }(address(paymaster)); - testCreation(); - testTransferNative(); - testTransferERC20(); - if (writeGasProfile) { - string memory res = vm.serializeUint(jsonObj, "sum", sum); - console.log(res); - vm.writeJson(res, string.concat("./results/", scenarioName, "_", name, ".json")); - } - } - - function testBenchmark2Paymaster() external { - scenarioName = "paymaster"; - jsonObj = string(abi.encodePacked(scenarioName, " ", name)); - entryPoint.depositTo{ value: 1000e18 }(address(paymaster)); - paymasterData = validatePaymasterAndData; - dummyPaymasterData = getDummyPaymasterAndData; - - testCreation(); - testTransferNative(); - testTransferERC20(); - if (writeGasProfile) { - string memory res = vm.serializeUint(jsonObj, "sum", sum); - console.log(res); - vm.writeJson(res, string.concat("./results/", scenarioName, "_", name, ".json")); - } - } - - function testBenchmark3Deposit() external { - scenarioName = "deposit"; - jsonObj = string(abi.encodePacked(scenarioName, " ", name)); - entryPoint.depositTo{ value: 1000e18 }(address(paymaster)); - entryPoint.depositTo{ value: 1000e18 }(address(account)); - testCreation(); - testTransferNative(); - testTransferERC20(); - if (writeGasProfile) { - string memory res = vm.serializeUint(jsonObj, "sum", sum); - console.log(res); - vm.writeJson(res, string.concat("./results/", scenarioName, "_", name, ".json")); - } - } - - function emptyPaymasterAndData(PackedUserOperation memory _op) internal pure returns (bytes memory ret) {} - - function validatePaymasterAndData(PackedUserOperation memory _op) internal view returns (bytes memory ret) { - bytes32 hash = paymaster.getHash(_op, 0, 0); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(verifierKey, ECDSA.toEthSignedMessageHash(hash)); - ret = abi.encodePacked(address(paymaster), uint256(0), uint256(0), r, s, uint8(v)); - } - - function getDummyPaymasterAndData(PackedUserOperation memory _op) internal view returns (bytes memory ret) { - ret = abi.encodePacked( - address(paymaster), - uint256(0), - uint256(0), - hex"fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" - ); - } - - function pack(PackedUserOperation memory _op) internal pure returns (bytes memory) { - bytes memory packed = abi.encode( - _op.sender, - _op.nonce, - _op.initCode, - _op.callData, - _op.accountGasLimits, - _op.preVerificationGas, - _op.gasFees, - _op.paymasterAndData, - _op.signature - ); - return packed; - } - - function calldataCost(bytes memory packed) internal view returns (uint256) { - uint256 cost = 0; - for (uint256 i = 0; i < packed.length; i++) { - if (packed[i] == 0) { - cost += OV_PER_ZERO_BYTE; - } else { - cost += OV_PER_NONZERO_BYTE; - } - } - return cost; - } - - // NOTE: this can vary depending on the bundler, this equation is referencing eth-infinitism bundler's pvg calculation - function calculatePreVerificationGas(PackedUserOperation memory _op) internal view returns (uint256) { - bytes memory packed = pack(_op); - uint256 calculated = OV_FIXED + OV_PER_USEROP + (OV_PER_WORD * (packed.length + 31)) / 32; - calculated += calldataCost(packed); - return calculated; - } - - function createAccount(address _owner) internal virtual; - - function getSignature(PackedUserOperation memory _op) internal view virtual returns (bytes memory); - - function getDummySig(PackedUserOperation memory _op) internal pure virtual returns (bytes memory); - - function fillData(address _to, uint256 _amount, bytes memory _data) internal view virtual returns (bytes memory); - - function getAccountAddr(address _owner) internal view virtual returns (IAccount _account); - - function getInitCode(address _owner) internal view virtual returns (bytes memory); -} diff --git a/src/test/smart-wallet/utils/BasePaymaster.sol b/src/test/smart-wallet/utils/BasePaymaster.sol deleted file mode 100644 index 3a14aae76..000000000 --- a/src/test/smart-wallet/utils/BasePaymaster.sol +++ /dev/null @@ -1,152 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -/* solhint-disable reason-string */ - -import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; -import "contracts/prebuilts/account/interfaces/IPaymaster.sol"; -import "contracts/prebuilts/account/interfaces/IEntryPoint.sol"; -import "contracts/prebuilts/account/utils/UserOperationLib.sol"; -/** - * Helper class for creating a paymaster. - * provides helper methods for staking. - * Validates that the postOp is called only by the entryPoint. - */ -abstract contract BasePaymaster is IPaymaster, Ownable { - IEntryPoint public immutable entryPoint; - - uint256 internal constant PAYMASTER_VALIDATION_GAS_OFFSET = UserOperationLib.PAYMASTER_VALIDATION_GAS_OFFSET; - uint256 internal constant PAYMASTER_POSTOP_GAS_OFFSET = UserOperationLib.PAYMASTER_POSTOP_GAS_OFFSET; - uint256 internal constant PAYMASTER_DATA_OFFSET = UserOperationLib.PAYMASTER_DATA_OFFSET; - - constructor(IEntryPoint _entryPoint) { - _transferOwnership(msg.sender); - _validateEntryPointInterface(_entryPoint); - entryPoint = _entryPoint; - } - - //sanity check: make sure this EntryPoint was compiled against the same - // IEntryPoint of this paymaster - function _validateEntryPointInterface(IEntryPoint _entryPoint) internal virtual { - require( - IERC165(address(_entryPoint)).supportsInterface(type(IEntryPoint).interfaceId), - "IEntryPoint interface mismatch" - ); - } - - /// @inheritdoc IPaymaster - function validatePaymasterUserOp( - PackedUserOperation calldata userOp, - bytes32 userOpHash, - uint256 maxCost - ) external override returns (bytes memory context, uint256 validationData) { - _requireFromEntryPoint(); - return _validatePaymasterUserOp(userOp, userOpHash, maxCost); - } - - /** - * Validate a user operation. - * @param userOp - The user operation. - * @param userOpHash - The hash of the user operation. - * @param maxCost - The maximum cost of the user operation. - */ - function _validatePaymasterUserOp( - PackedUserOperation calldata userOp, - bytes32 userOpHash, - uint256 maxCost - ) internal virtual returns (bytes memory context, uint256 validationData); - - /// @inheritdoc IPaymaster - function postOp( - PostOpMode mode, - bytes calldata context, - uint256 actualGasCost, - uint256 actualUserOpFeePerGas - ) external override { - _requireFromEntryPoint(); - _postOp(mode, context, actualGasCost, actualUserOpFeePerGas); - } - - /** - * Post-operation handler. - * (verified to be called only through the entryPoint) - * @dev If subclass returns a non-empty context from validatePaymasterUserOp, - * it must also implement this method. - * @param mode - Enum with the following options: - * opSucceeded - User operation succeeded. - * opReverted - User op reverted. The paymaster still has to pay for gas. - * postOpReverted - never passed in a call to postOp(). - * @param context - The context value returned by validatePaymasterUserOp - * @param actualGasCost - Actual gas used so far (without this postOp call). - * @param actualUserOpFeePerGas - the gas price this UserOp pays. This value is based on the UserOp's maxFeePerGas - * and maxPriorityFee (and basefee) - * It is not the same as tx.gasprice, which is what the bundler pays. - */ - function _postOp( - PostOpMode mode, - bytes calldata context, - uint256 actualGasCost, - uint256 actualUserOpFeePerGas - ) internal virtual { - (mode, context, actualGasCost, actualUserOpFeePerGas); // unused params - // subclass must override this method if validatePaymasterUserOp returns a context - revert("must override"); - } - - /** - * Add a deposit for this paymaster, used for paying for transaction fees. - */ - function deposit() public payable { - entryPoint.depositTo{ value: msg.value }(address(this)); - } - - /** - * Withdraw value from the deposit. - * @param withdrawAddress - Target to send to. - * @param amount - Amount to withdraw. - */ - function withdrawTo(address payable withdrawAddress, uint256 amount) public onlyOwner { - entryPoint.withdrawTo(withdrawAddress, amount); - } - - /** - * Add stake for this paymaster. - * This method can also carry eth value to add to the current stake. - * @param unstakeDelaySec - The unstake delay for this paymaster. Can only be increased. - */ - function addStake(uint32 unstakeDelaySec) external payable onlyOwner { - entryPoint.addStake{ value: msg.value }(unstakeDelaySec); - } - - /** - * Return current paymaster's deposit on the entryPoint. - */ - function getDeposit() public view returns (uint256) { - return entryPoint.balanceOf(address(this)); - } - - /** - * Unlock the stake, in order to withdraw it. - * The paymaster can't serve requests once unlocked, until it calls addStake again - */ - function unlockStake() external onlyOwner { - entryPoint.unlockStake(); - } - - /** - * Withdraw the entire paymaster's stake. - * stake must be unlocked first (and then wait for the unstakeDelay to be over) - * @param withdrawAddress - The address to send withdrawn value. - */ - function withdrawStake(address payable withdrawAddress) external onlyOwner { - entryPoint.withdrawStake(withdrawAddress); - } - - /** - * Validate the call is made from a valid entrypoint - */ - function _requireFromEntryPoint() internal virtual { - require(msg.sender == address(entryPoint), "Sender not EntryPoint"); - } -} diff --git a/src/test/smart-wallet/utils/MessageHashUtils.sol b/src/test/smart-wallet/utils/MessageHashUtils.sol deleted file mode 100644 index 06c61b8f7..000000000 --- a/src/test/smart-wallet/utils/MessageHashUtils.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol) - -pragma solidity ^0.8.20; - -import { Strings } from "contracts/lib/Strings.sol"; - -/** - * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing. - * - * The library provides methods for generating a hash of a message that conforms to the - * https://eips.ethereum.org/EIPS/eip-191[ERC-191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712] - * specifications. - */ -library MessageHashUtils { - /** - * @dev Returns the keccak256 digest of an ERC-191 signed data with version - * `0x45` (`personal_sign` messages). - * - * The digest is calculated by prefixing a bytes32 `messageHash` with - * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the - * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. - * - * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with - * keccak256, although any bytes32 value can be safely used because the final digest will - * be re-hashed. - * - * See {ECDSA-recover}. - */ - function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) { - /// @solidity memory-safe-assembly - assembly { - mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash - mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix - digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20) - } - } - - /** - * @dev Returns the keccak256 digest of an ERC-191 signed data with version - * `0x45` (`personal_sign` messages). - * - * The digest is calculated by prefixing an arbitrary `message` with - * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the - * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. - * - * See {ECDSA-recover}. - */ - function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) { - return - keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message)); - } - - /** - * @dev Returns the keccak256 digest of an ERC-191 signed data with version - * `0x00` (data with intended validator). - * - * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended - * `validator` address. Then hashing the result. - * - * See {ECDSA-recover}. - */ - function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { - return keccak256(abi.encodePacked(hex"19_00", validator, data)); - } - - /** - * @dev Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version `0x01`). - * - * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with - * `\x19\x01` and hashing the result. It corresponds to the hash signed by the - * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. - * - * See {ECDSA-recover}. - */ - function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) { - /// @solidity memory-safe-assembly - assembly { - let ptr := mload(0x40) - mstore(ptr, hex"19_01") - mstore(add(ptr, 0x02), domainSeparator) - mstore(add(ptr, 0x22), structHash) - digest := keccak256(ptr, 0x42) - } - } -} diff --git a/src/test/smart-wallet/utils/VerifyingPaymaster.sol b/src/test/smart-wallet/utils/VerifyingPaymaster.sol deleted file mode 100644 index 82da19733..000000000 --- a/src/test/smart-wallet/utils/VerifyingPaymaster.sol +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.23; - -/* solhint-disable reason-string */ -/* solhint-disable no-inline-assembly */ - -import "./BasePaymaster.sol"; -import "contracts/prebuilts/account/utils/UserOperationLib.sol"; -import "contracts/prebuilts/account/utils/Helpers.sol"; -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import { MessageHashUtils } from "./MessageHashUtils.sol"; - -/** - * A sample paymaster that uses external service to decide whether to pay for the UserOp. - * The paymaster trusts an external signer to sign the transaction. - * The calling user must pass the UserOp to that external signer first, which performs - * whatever off-chain verification before signing the UserOp. - * Note that this signature is NOT a replacement for the account-specific signature: - * - the paymaster checks a signature to agree to PAY for GAS. - * - the account checks a signature to prove identity and account ownership. - */ -contract VerifyingPaymaster is BasePaymaster { - using UserOperationLib for PackedUserOperation; - - address public immutable verifyingSigner; - - uint256 private constant VALID_TIMESTAMP_OFFSET = PAYMASTER_DATA_OFFSET; - - uint256 private constant SIGNATURE_OFFSET = VALID_TIMESTAMP_OFFSET + 64; - - constructor(IEntryPoint _entryPoint, address _verifyingSigner) BasePaymaster(_entryPoint) { - verifyingSigner = _verifyingSigner; - } - - /** - * return the hash we're going to sign off-chain (and validate on-chain) - * this method is called by the off-chain service, to sign the request. - * it is called on-chain from the validatePaymasterUserOp, to validate the signature. - * note that this signature covers all fields of the UserOperation, except the "paymasterAndData", - * which will carry the signature itself. - */ - function getHash( - PackedUserOperation calldata userOp, - uint48 validUntil, - uint48 validAfter - ) public view returns (bytes32) { - //can't use userOp.hash(), since it contains also the paymasterAndData itself. - address sender = userOp.getSender(); - return - keccak256( - abi.encode( - sender, - userOp.nonce, - keccak256(userOp.initCode), - keccak256(userOp.callData), - userOp.accountGasLimits, - uint256(bytes32(userOp.paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET:PAYMASTER_DATA_OFFSET])), - userOp.preVerificationGas, - userOp.gasFees, - block.chainid, - address(this), - validUntil, - validAfter - ) - ); - } - - /** - * verify our external signer signed this request. - * the "paymasterAndData" is expected to be the paymaster and a signature over the entire request params - * paymasterAndData[:20] : address(this) - * paymasterAndData[20:84] : abi.encode(validUntil, validAfter) - * paymasterAndData[84:] : signature - */ - function _validatePaymasterUserOp( - PackedUserOperation calldata userOp, - bytes32 /*userOpHash*/, - uint256 requiredPreFund - ) internal view override returns (bytes memory context, uint256 validationData) { - (requiredPreFund); - - (uint48 validUntil, uint48 validAfter, bytes calldata signature) = parsePaymasterAndData( - userOp.paymasterAndData - ); - - //ECDSA library supports both 64 and 65-byte long signatures. - // we only "require" it here so that the revert reason on invalid signature will be of "VerifyingPaymaster", and not "ECDSA" - require( - signature.length == 64 || signature.length == 65, - "VerifyingPaymaster: invalid signature length in paymasterAndData" - ); - - bytes32 hash = MessageHashUtils.toEthSignedMessageHash(getHash(userOp, validUntil, validAfter)); - - //don't revert on signature failure: return SIG_VALIDATION_FAILED - if (verifyingSigner != ECDSA.recover(hash, signature)) { - return ("", _packValidationData(true, validUntil, validAfter)); - } - - //no need for other on-chain validation: entire UserOp should have been checked - // by the external service prior to signing it. - return ("", _packValidationData(false, validUntil, validAfter)); - } - - function parsePaymasterAndData( - bytes calldata paymasterAndData - ) public view returns (uint48 validUntil, uint48 validAfter, bytes calldata signature) { - (validUntil, validAfter) = abi.decode(paymasterAndData[VALID_TIMESTAMP_OFFSET:], (uint48, uint48)); - signature = paymasterAndData[SIGNATURE_OFFSET:]; - } -} diff --git a/src/test/split-BTT/distribute-erc20/distribute.t.sol b/src/test/split-BTT/distribute-erc20/distribute.t.sol deleted file mode 100644 index 4c2d9c139..000000000 --- a/src/test/split-BTT/distribute-erc20/distribute.t.sol +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MySplit is Split {} - -contract SplitTest_DistributeERC20 is BaseTest { - address payable public implementation; - address payable public proxy; - - address[] public payees; - uint256[] public shares; - - address internal caller; - string internal _contractURI; - - MySplit internal splitContract; - - event PaymentReleased(address to, uint256 amount); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = payable(address(new MySplit())); - - // create 5 payees and shares - for (uint160 i = 0; i < 5; i++) { - payees.push(getActor(i + 100)); - shares.push(i + 100); - } - - caller = getActor(1); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = payable( - address( - new TWProxy( - implementation, - abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) - ) - ) - ); - - splitContract = MySplit(proxy); - _contractURI = "ipfs://contracturi"; - - erc20.mint(address(splitContract), 100 ether); - } - - function test_distribute() public { - uint256[] memory pendingAmounts = new uint256[](payees.length); - - // get pending payments - for (uint256 i = 0; i < 5; i++) { - pendingAmounts[i] = splitContract.releasable(IERC20Upgradeable(address(erc20)), payees[i]); - } - - // distribute - splitContract.distribute(IERC20Upgradeable(address(erc20))); - - uint256 totalPaid; - for (uint256 i = 0; i < 5; i++) { - totalPaid += pendingAmounts[i]; - - assertEq(splitContract.released(IERC20Upgradeable(address(erc20)), payees[i]), pendingAmounts[i]); - assertEq(erc20.balanceOf(payees[i]), pendingAmounts[i]); - } - assertEq(splitContract.totalReleased(IERC20Upgradeable(address(erc20))), totalPaid); - - assertEq(erc20.balanceOf(address(splitContract)), 100 ether - totalPaid); - } -} diff --git a/src/test/split-BTT/distribute-erc20/distribute.tree b/src/test/split-BTT/distribute-erc20/distribute.tree deleted file mode 100644 index 320921cca..000000000 --- a/src/test/split-BTT/distribute-erc20/distribute.tree +++ /dev/null @@ -1,6 +0,0 @@ -distribute() -├── it should update released mapping for all payees account by respective pending payments ✅ -├── it should update total released by total pending payments ✅ -├── it should send correct pending payment amounts of erc20 tokens to each account ✅ -├── it should reduce balance of contract by total paid in this call ✅ - \ No newline at end of file diff --git a/src/test/split-BTT/distribute-native-token/distribute.t.sol b/src/test/split-BTT/distribute-native-token/distribute.t.sol deleted file mode 100644 index fd524c40d..000000000 --- a/src/test/split-BTT/distribute-native-token/distribute.t.sol +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MySplit is Split {} - -contract SplitTest_DistributeNativeToken is BaseTest { - address payable public implementation; - address payable public proxy; - - address[] public payees; - uint256[] public shares; - - address internal caller; - string internal _contractURI; - - MySplit internal splitContract; - - event PaymentReleased(address to, uint256 amount); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = payable(address(new MySplit())); - - // create 5 payees and shares - for (uint160 i = 0; i < 5; i++) { - payees.push(getActor(i + 100)); - shares.push(i + 100); - } - - caller = getActor(1); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = payable( - address( - new TWProxy( - implementation, - abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) - ) - ) - ); - - splitContract = MySplit(proxy); - _contractURI = "ipfs://contracturi"; - - vm.deal(address(splitContract), 100 ether); - } - - function test_distribute() public { - uint256[] memory pendingAmounts = new uint256[](payees.length); - - // get pending payments - for (uint256 i = 0; i < 5; i++) { - pendingAmounts[i] = splitContract.releasable(payees[i]); - } - - // distribute - splitContract.distribute(); - - uint256 totalPaid; - for (uint256 i = 0; i < 5; i++) { - totalPaid += pendingAmounts[i]; - - assertEq(splitContract.released(payees[i]), pendingAmounts[i]); - assertEq(payees[i].balance, pendingAmounts[i]); - } - assertEq(splitContract.totalReleased(), totalPaid); - - assertEq(address(splitContract).balance, 100 ether - totalPaid); - } -} diff --git a/src/test/split-BTT/distribute-native-token/distribute.tree b/src/test/split-BTT/distribute-native-token/distribute.tree deleted file mode 100644 index d75f4cc53..000000000 --- a/src/test/split-BTT/distribute-native-token/distribute.tree +++ /dev/null @@ -1,6 +0,0 @@ -distribute() -├── it should update released mapping for all payees account by respective pending payments ✅ -├── it should update total released by total pending payments ✅ -├── it should send correct pending payment amounts of native tokens to each account ✅ -├── it should reduce balance of contract by total paid in this call ✅ - \ No newline at end of file diff --git a/src/test/split-BTT/initialize/initialize.t.sol b/src/test/split-BTT/initialize/initialize.t.sol deleted file mode 100644 index bc75ae266..000000000 --- a/src/test/split-BTT/initialize/initialize.t.sol +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MySplit is Split {} - -contract SplitTest_Initialize is BaseTest { - address payable public implementation; - address payable public proxy; - - address[] public payees; - uint256[] public shares; - - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - function setUp() public override { - super.setUp(); - - // create 5 payees and shares - for (uint160 i = 0; i < 5; i++) { - payees.push(getActor(i + 100)); - shares.push(i + 100); - } - - // Deploy implementation. - implementation = payable(address(new MySplit())); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = payable( - address( - new TWProxy( - implementation, - abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) - ) - ) - ); - } - - function test_initialize_initializingImplementation() public { - vm.expectRevert("Initializable: contract is already initialized"); - Split(implementation).initialize(deployer, CONTRACT_URI, forwarders(), payees, shares); - } - - modifier whenNotImplementation() { - _; - } - - function test_initialize_proxyAlreadyInitialized() public whenNotImplementation { - vm.expectRevert("Initializable: contract is already initialized"); - MySplit(proxy).initialize(deployer, CONTRACT_URI, forwarders(), payees, shares); - } - - modifier whenProxyNotInitialized() { - proxy = payable(address(new TWProxy(implementation, ""))); - _; - } - - function test_initialize_payeeLengthZero() public whenNotImplementation whenProxyNotInitialized { - address[] memory _payees; - uint256[] memory _shares; - vm.expectRevert("PaymentSplitter: no payees"); - MySplit(proxy).initialize(deployer, CONTRACT_URI, forwarders(), _payees, _shares); - } - - modifier whenPayeeLengthNotZero() { - _; - } - - function test_initialize_payeesSharesUnequalLength() - public - whenNotImplementation - whenProxyNotInitialized - whenPayeeLengthNotZero - { - uint256[] memory _shares; - vm.expectRevert("PaymentSplitter: payees and shares length mismatch"); - MySplit(proxy).initialize(deployer, CONTRACT_URI, forwarders(), payees, _shares); - } - - modifier whenEqualLengths() { - _; - } - - function test_initialize() - public - whenNotImplementation - whenProxyNotInitialized - whenPayeeLengthNotZero - whenEqualLengths - { - MySplit(proxy).initialize(deployer, CONTRACT_URI, forwarders(), payees, shares); - - // check state - MySplit splitContract = MySplit(proxy); - - address[] memory _trustedForwarders = forwarders(); - for (uint256 i = 0; i < _trustedForwarders.length; i++) { - assertTrue(splitContract.isTrustedForwarder(_trustedForwarders[i])); - } - - uint256 totalShares; - for (uint160 i = 0; i < 5; i++) { - uint256 _shares = splitContract.shares(payees[i]); - assertEq(_shares, shares[i]); - - totalShares += _shares; - } - assertEq(totalShares, splitContract.totalShares()); - assertEq(splitContract.payeeCount(), payees.length); - assertEq(splitContract.contractURI(), CONTRACT_URI); - assertTrue(splitContract.hasRole(bytes32(0x00), deployer)); - } - - function test_initialize_event_RoleGranted_DefaultAdmin() - public - whenNotImplementation - whenProxyNotInitialized - whenPayeeLengthNotZero - whenEqualLengths - { - bytes32 _defaultAdminRole = bytes32(0x00); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_defaultAdminRole, deployer, deployer); - MySplit(proxy).initialize(deployer, CONTRACT_URI, forwarders(), payees, shares); - } -} diff --git a/src/test/split-BTT/initialize/initialize.tree b/src/test/split-BTT/initialize/initialize.tree deleted file mode 100644 index 4647d7f70..000000000 --- a/src/test/split-BTT/initialize/initialize.tree +++ /dev/null @@ -1,25 +0,0 @@ -initialize( - address _defaultAdmin, - string memory _contractURI, - address[] memory _trustedForwarders, - address[] memory _payees, - uint256[] memory _shares -) -├── when initializing the implementation contract (not proxy) -│ └── it should revert ✅ -└── when it is a proxy to the implementation - └── when it is already initialized - │ └── it should revert ✅ - └── when it is not initialized - └── when `_payees` length is zero - │ └── it should revert ✅ - └── `_payees` length is not zero - └── when `_payees` length not equal to `_shares` length - │ └── it should revert ✅ - └── when `_payees` length equal to `_shares` length - └── it should set trustedForwarder mapping to true for all addresses in `_trustedForwarders` ✅ - └── it should correctly save `_payees` and `_shares` in state ✅ - └── it should set contractURI to `_contractURI` param value ✅ - └── it should grant 0x00 (DEFAULT_ADMIN_ROLE) to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - diff --git a/src/test/split-BTT/other-functions/other.t.sol b/src/test/split-BTT/other-functions/other.t.sol deleted file mode 100644 index 2bf87b871..000000000 --- a/src/test/split-BTT/other-functions/other.t.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MySplit is Split {} - -contract SplitTest_OtherFunctions is BaseTest { - address payable public implementation; - address payable public proxy; - - address[] public payees; - uint256[] public shares; - - address internal caller; - string internal _contractURI; - - MySplit internal splitContract; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = payable(address(new MySplit())); - - // create 5 payees and shares - for (uint160 i = 0; i < 5; i++) { - payees.push(getActor(i + 100)); - shares.push(i + 100); - } - - caller = getActor(1); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = payable( - address( - new TWProxy( - implementation, - abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) - ) - ) - ); - - splitContract = MySplit(proxy); - _contractURI = "ipfs://contracturi"; - } - - function test_contractType() public { - assertEq(splitContract.contractType(), bytes32("Split")); - } - - function test_contractVersion() public { - assertEq(splitContract.contractVersion(), uint8(1)); - } -} diff --git a/src/test/split-BTT/other-functions/other.tree b/src/test/split-BTT/other-functions/other.tree deleted file mode 100644 index dd1798caa..000000000 --- a/src/test/split-BTT/other-functions/other.tree +++ /dev/null @@ -1,5 +0,0 @@ -contractType() -├── it should return bytes32("TokenERC721") ✅ - -contractVersion() -├── it should return uint8(1) ✅ diff --git a/src/test/split-BTT/release-erc20/release.t.sol b/src/test/split-BTT/release-erc20/release.t.sol deleted file mode 100644 index c5499f664..000000000 --- a/src/test/split-BTT/release-erc20/release.t.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MySplit is Split {} - -contract SplitTest_ReleaseERC20 is BaseTest { - address payable public implementation; - address payable public proxy; - - address[] public payees; - uint256[] public shares; - - address internal caller; - string internal _contractURI; - - MySplit internal splitContract; - - event ERC20PaymentReleased(IERC20Upgradeable indexed token, address to, uint256 amount); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = payable(address(new MySplit())); - - // create 5 payees and shares - for (uint160 i = 0; i < 5; i++) { - payees.push(getActor(i + 100)); - shares.push(i + 100); - } - - caller = getActor(1); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = payable( - address( - new TWProxy( - implementation, - abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) - ) - ) - ); - - splitContract = MySplit(proxy); - _contractURI = "ipfs://contracturi"; - } - - function test_release_zeroShares() public { - vm.expectRevert("PaymentSplitter: account has no shares"); - splitContract.release(IERC20Upgradeable(address(erc20)), payable(address(0x123))); // arbitrary address - } - - modifier whenNonZeroShares() { - _; - } - - function test_release_pendingPaymentZero() public { - vm.expectRevert("PaymentSplitter: account is not due payment"); - splitContract.release(IERC20Upgradeable(address(erc20)), payable(payees[1])); - } - - modifier whenPendingPaymentNonZero() { - erc20.mint(address(splitContract), 100 ether); - _; - } - - function test_release() public whenPendingPaymentNonZero { - address _payeeOne = payees[1]; // select a payee from the array - uint256 pendingPayment = splitContract.releasable(IERC20Upgradeable(address(erc20)), _payeeOne); - - splitContract.release(IERC20Upgradeable(address(erc20)), payable(_payeeOne)); - - uint256 totalReleased = splitContract.totalReleased(IERC20Upgradeable(address(erc20))); - assertEq(splitContract.released(IERC20Upgradeable(address(erc20)), _payeeOne), pendingPayment); - assertEq(totalReleased, pendingPayment); - assertEq(erc20.balanceOf(_payeeOne), pendingPayment); - - // check for another payee - address _payeeThree = payees[3]; - pendingPayment = splitContract.releasable(IERC20Upgradeable(address(erc20)), _payeeThree); - - splitContract.release(IERC20Upgradeable(address(erc20)), payable(_payeeThree)); - - assertEq(splitContract.released(IERC20Upgradeable(address(erc20)), _payeeThree), pendingPayment); - assertEq(splitContract.totalReleased(IERC20Upgradeable(address(erc20))), totalReleased + pendingPayment); - assertEq(erc20.balanceOf(_payeeThree), pendingPayment); - - assertEq( - erc20.balanceOf(address(splitContract)), - 100 ether - erc20.balanceOf(_payeeOne) - erc20.balanceOf(_payeeThree) - ); - } - - function test_release_event_PaymentReleased() public whenPendingPaymentNonZero { - address _payeeOne = payees[1]; // select a payee from the array - uint256 pendingPayment = splitContract.releasable(IERC20Upgradeable(address(erc20)), _payeeOne); - - vm.expectEmit(true, false, false, true); - emit ERC20PaymentReleased(IERC20Upgradeable(address(erc20)), _payeeOne, pendingPayment); - splitContract.release(IERC20Upgradeable(address(erc20)), payable(_payeeOne)); - } -} diff --git a/src/test/split-BTT/release-erc20/release.tree b/src/test/split-BTT/release-erc20/release.tree deleted file mode 100644 index 217ea3b6c..000000000 --- a/src/test/split-BTT/release-erc20/release.tree +++ /dev/null @@ -1,12 +0,0 @@ -release(address payable account) -├── when account has zero shares - │ └── it should revert ✅ - └── when account has non-zero shares - └── when pending payment is zero - │ └── it should revert ✅ - └── when pending payment is not zero - └── it should update released mapping for the account by pending payment ✅ - └── it should update total released by pending payment ✅ - └── it should send pending payment amount of erc20 token to account ✅ - └── it should emit ERC20PaymentReleased event ✅ - \ No newline at end of file diff --git a/src/test/split-BTT/release-native-token/release.t.sol b/src/test/split-BTT/release-native-token/release.t.sol deleted file mode 100644 index 18210e0da..000000000 --- a/src/test/split-BTT/release-native-token/release.t.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MySplit is Split {} - -contract SplitTest_ReleaseNativeToken is BaseTest { - address payable public implementation; - address payable public proxy; - - address[] public payees; - uint256[] public shares; - - address internal caller; - string internal _contractURI; - - MySplit internal splitContract; - - event PaymentReleased(address to, uint256 amount); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = payable(address(new MySplit())); - - // create 5 payees and shares - for (uint160 i = 0; i < 5; i++) { - payees.push(getActor(i + 100)); - shares.push(i + 100); - } - - caller = getActor(1); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = payable( - address( - new TWProxy( - implementation, - abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) - ) - ) - ); - - splitContract = MySplit(proxy); - _contractURI = "ipfs://contracturi"; - } - - function test_release_zeroShares() public { - vm.expectRevert("PaymentSplitter: account has no shares"); - splitContract.release(payable(address(0x123))); // arbitrary address - } - - modifier whenNonZeroShares() { - _; - } - - function test_release_pendingPaymentZero() public { - vm.expectRevert("PaymentSplitter: account is not due payment"); - splitContract.release(payable(payees[1])); - } - - modifier whenPendingPaymentNonZero() { - vm.deal(address(splitContract), 100 ether); - _; - } - - function test_release() public whenPendingPaymentNonZero { - address _payeeOne = payees[1]; // select a payee from the array - uint256 pendingPayment = splitContract.releasable(_payeeOne); - - splitContract.release(payable(_payeeOne)); - - uint256 totalReleased = splitContract.totalReleased(); - assertEq(splitContract.released(_payeeOne), pendingPayment); - assertEq(totalReleased, pendingPayment); - assertEq(_payeeOne.balance, pendingPayment); - - // check for another payee - address _payeeThree = payees[3]; - pendingPayment = splitContract.releasable(_payeeThree); - - splitContract.release(payable(_payeeThree)); - - assertEq(splitContract.released(_payeeThree), pendingPayment); - assertEq(splitContract.totalReleased(), totalReleased + pendingPayment); - assertEq(_payeeThree.balance, pendingPayment); - - assertEq(address(splitContract).balance, 100 ether - _payeeOne.balance - _payeeThree.balance); - } - - function test_release_event_PaymentReleased() public whenPendingPaymentNonZero { - address _payeeOne = payees[1]; // select a payee from the array - uint256 pendingPayment = splitContract.releasable(_payeeOne); - - vm.expectEmit(false, false, false, true); - emit PaymentReleased(_payeeOne, pendingPayment); - splitContract.release(payable(_payeeOne)); - } -} diff --git a/src/test/split-BTT/release-native-token/release.tree b/src/test/split-BTT/release-native-token/release.tree deleted file mode 100644 index afa44e86d..000000000 --- a/src/test/split-BTT/release-native-token/release.tree +++ /dev/null @@ -1,12 +0,0 @@ -release(address payable account) -├── when account has zero shares - │ └── it should revert ✅ - └── when account has non-zero shares - └── when pending payment is zero - │ └── it should revert ✅ - └── when pending payment is not zero - └── it should update released mapping for the account by pending payment ✅ - └── it should update total released by pending payment ✅ - └── it should send pending payment amount of native tokens to account ✅ - └── it should emit PaymentReleased event ✅ - \ No newline at end of file diff --git a/src/test/split-BTT/set-contract-uri/setContractURI.t.sol b/src/test/split-BTT/set-contract-uri/setContractURI.t.sol deleted file mode 100644 index af3cc85d1..000000000 --- a/src/test/split-BTT/set-contract-uri/setContractURI.t.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MySplit is Split {} - -contract SplitTest_SetContractURI is BaseTest { - address payable public implementation; - address payable public proxy; - - address[] public payees; - uint256[] public shares; - - address internal caller; - string internal _contractURI; - - MySplit internal splitContract; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = payable(address(new MySplit())); - - // create 5 payees and shares - for (uint160 i = 0; i < 5; i++) { - payees.push(getActor(i + 100)); - shares.push(i + 100); - } - - caller = getActor(1); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = payable( - address( - new TWProxy( - implementation, - abi.encodeCall(Split.initialize, (deployer, CONTRACT_URI, forwarders(), payees, shares)) - ) - ) - ); - - splitContract = MySplit(proxy); - _contractURI = "ipfs://contracturi"; - } - - function test_setContractURI_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - splitContract.setContractURI(_contractURI); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - splitContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setContractURI_empty() public whenCallerAuthorized { - vm.prank(address(caller)); - splitContract.setContractURI(""); - - // get contract uri - assertEq(splitContract.contractURI(), ""); - } - - function test_setContractURI_notEmpty() public whenCallerAuthorized { - vm.prank(address(caller)); - splitContract.setContractURI(_contractURI); - - // get contract uri - assertEq(splitContract.contractURI(), _contractURI); - } -} diff --git a/src/test/split-BTT/set-contract-uri/setContractURI.tree b/src/test/split-BTT/set-contract-uri/setContractURI.tree deleted file mode 100644 index 8fc480b19..000000000 --- a/src/test/split-BTT/set-contract-uri/setContractURI.tree +++ /dev/null @@ -1,8 +0,0 @@ -setContractURI(string calldata _uri) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── when `uri` is empty - │ └── it should update contract URI to empty string ✅ - └── when `uri` is not empty - └── it should update contract URI to `_uri` ✅ \ No newline at end of file diff --git a/src/test/staking/EditionStake.t.sol b/src/test/staking/EditionStake.t.sol deleted file mode 100644 index 0df3b9e91..000000000 --- a/src/test/staking/EditionStake.t.sol +++ /dev/null @@ -1,1153 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { EditionStake } from "contracts/prebuilts/staking/EditionStake.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; - -contract EditionStakeTest is BaseTest { - EditionStake internal stakeContract; - - address internal stakerOne; - address internal stakerTwo; - - uint256 internal defaultTimeUnit; - uint256 internal defaultRewardsPerUnitTime; - - function setUp() public override { - super.setUp(); - - defaultTimeUnit = 60; - defaultRewardsPerUnitTime = 1; - - stakerOne = address(0x345); - stakerTwo = address(0x567); - - erc1155.mint(stakerOne, 0, 100); // mint 100 tokens with id 0 to stakerOne - erc1155.mint(stakerOne, 1, 100); // mint 100 tokens with id 1 to stakerOne - - erc1155.mint(stakerTwo, 0, 100); // mint 100 tokens with id 0 to stakerTwo - erc1155.mint(stakerTwo, 1, 100); // mint 100 tokens with id 1 to stakerTwo - - erc20.mint(deployer, 1000 ether); // mint reward tokens to contract admin - - stakeContract = EditionStake(payable(getContract("EditionStake"))); - - // set approvals - vm.prank(stakerOne); - erc1155.setApprovalForAll(address(stakeContract), true); - - vm.prank(stakerTwo); - erc1155.setApprovalForAll(address(stakeContract), true); - - vm.startPrank(deployer); - erc20.approve(address(stakeContract), type(uint256).max); - stakeContract.depositRewardTokens(100 ether); - // erc20.transfer(address(stakeContract), 100 ether); - vm.stopPrank(); - assertEq(stakeContract.getRewardTokenBalance(), 100 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: Stake - - with default time-unit and rewards - - different token-ids staked by stakers - //////////////////////////////////////////////////////////////*/ - - function test_state_stake_defaults_differentTokens() public { - //================ first staker ====================== - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate_one = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc1155.balanceOf(address(stakeContract), 0), 50); - assertEq(erc1155.balanceOf(address(stakerOne), 0), 50); - - // check available rewards right after staking - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq(_amountStaked, 50); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(100); - vm.warp(1000); - - // check available rewards after warp - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - //================ second staker ====================== - vm.roll(200); - vm.warp(2000); - - // stake 20 tokens with token-id 1 - vm.prank(stakerTwo); - stakeContract.stake(1, 20); - uint256 timeOfLastUpdate_two = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc1155.balanceOf(address(stakeContract), 1), 20); - assertEq(erc1155.balanceOf(address(stakerTwo), 1), 80); - - // check available rewards right after staking - (_amountStaked, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerTwo); - - assertEq(_amountStaked, 20); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(300); - vm.warp(3000); - - // check available rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerTwo); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_two) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - function test_revert_stake_stakingZeroTokens() public { - // stake 0 tokens - - vm.prank(stakerOne); - vm.expectRevert("Staking 0 tokens"); - stakeContract.stake(0, 0); - } - - function test_revert_stake_notBalanceOrApproved() public { - // stake unowned tokens - vm.prank(stakerOne); - vm.expectRevert("ERC1155: insufficient balance for transfer"); - stakeContract.stake(2, 10); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: Stake - - with default time-unit and rewards - - same token-id staked by stakers - //////////////////////////////////////////////////////////////*/ - - function test_state_stake_defaults_sameToken() public { - //================ first staker ====================== - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate_one = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc1155.balanceOf(address(stakeContract), 0), 50); - assertEq(erc1155.balanceOf(address(stakerOne), 0), 50); - - // check available rewards right after staking - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq(_amountStaked, 50); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(100); - vm.warp(1000); - - // check available rewards after warp - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - //================ second staker ====================== - vm.roll(200); - vm.warp(2000); - - // stake 20 tokens with token-id 0 - vm.prank(stakerTwo); - stakeContract.stake(0, 20); - uint256 timeOfLastUpdate_two = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc1155.balanceOf(address(stakeContract), 0), 20 + 50); // sum of staked tokens by both stakers - assertEq(erc1155.balanceOf(address(stakerTwo), 0), 80); - - // check available rewards right after staking - (_amountStaked, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerTwo); - - assertEq(_amountStaked, 20); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(300); - vm.warp(3000); - - // check available rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerTwo); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_two) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: claimRewards - - default timeUnit and rewards - - different token-ids staked by stakers - //////////////////////////////////////////////////////////////*/ - - function test_state_claimRewards_defaults_differentTokens() public { - //================ setup stakerOne ====================== - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate_one = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - - uint256 rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerOne); - stakeContract.claimRewards(0); - uint256 rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - erc20.balanceOf(stakerOne), - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards after claiming - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq(_amountStaked, 50); - assertEq(_availableRewards, 0); - - //================ setup stakerTwo ====================== - - // stake 20 tokens with token-id 1 - vm.prank(stakerTwo); - stakeContract.stake(1, 20); - uint256 timeOfLastUpdate_two = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(200); - vm.warp(2000); - - rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerTwo); - stakeContract.claimRewards(1); - rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - erc20.balanceOf(stakerTwo), - ((((block.timestamp - timeOfLastUpdate_two) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - ((((block.timestamp - timeOfLastUpdate_two) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards after claiming -- stakerTwo - (_amountStaked, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerTwo); - assertEq(_amountStaked, 20); - assertEq(_availableRewards, 0); - - // check available rewards -- stakerOne - (_amountStaked, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - assertEq(_amountStaked, 50); - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_two) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - function test_revert_claimRewards_noRewards() public { - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - - //=================== try to claim rewards for a different token - - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(1); - - //=================== try to claim rewards in same block - - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(0); - - //======= withdraw tokens and claim rewards - vm.roll(100); - vm.warp(1000); - - vm.prank(stakerOne); - stakeContract.withdraw(0, 50); - vm.prank(stakerOne); - stakeContract.claimRewards(0); - - //===== try to claim rewards again - vm.roll(200); - vm.warp(2000); - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(0); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: claimRewards - - default timeUnit and rewards - - same token-ids staked by stakers - //////////////////////////////////////////////////////////////*/ - - function test_state_claimRewards_defaults_sameToken() public { - //================ setup stakerOne ====================== - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate_one = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - - uint256 rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerOne); - stakeContract.claimRewards(0); - uint256 rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - erc20.balanceOf(stakerOne), - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards after claiming - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq(_amountStaked, 50); - assertEq(_availableRewards, 0); - - //================ setup stakerTwo ====================== - - // stake 20 tokens with token-id 1 - vm.prank(stakerTwo); - stakeContract.stake(0, 20); - uint256 timeOfLastUpdate_two = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(200); - vm.warp(2000); - - rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerTwo); - stakeContract.claimRewards(0); - rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - erc20.balanceOf(stakerTwo), - ((((block.timestamp - timeOfLastUpdate_two) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - ((((block.timestamp - timeOfLastUpdate_two) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards after claiming -- stakerTwo - (_amountStaked, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerTwo); - assertEq(_amountStaked, 20); - assertEq(_availableRewards, 0); - - // check available rewards -- stakerOne - (_amountStaked, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - assertEq(_amountStaked, 50); - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_two) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - - set rewards for token0 - - default time unit - //////////////////////////////////////////////////////////////*/ - - function test_state_setRewardsPerUnitTime_token0() public { - // set value and check - uint256 rewardsPerUnitTime = 50; - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(0, rewardsPerUnitTime); - assertEq(rewardsPerUnitTime, stakeContract.getRewardsPerUnitTime(0)); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(0, 200); - assertEq(200, stakeContract.getRewardsPerUnitTime(0)); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 50) * rewardsPerUnitTime) / defaultTimeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * 50) * 200) / defaultTimeUnit) - ); - - // =========== token 1 - //================ stake tokens - - vm.prank(stakerOne); - stakeContract.stake(1, 20); - timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime for token-0 - vm.roll(400); - vm.warp(4000); - - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(0, 300); - assertEq(300, stakeContract.getRewardsPerUnitTime(0)); - newTimeOfLastUpdate = block.timestamp; - - // check available rewards for token-1 -- should use defaultRewardsPerUnitTime for calculation - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - //====== check rewards after some time - vm.roll(500); - vm.warp(5000); - - (, _newRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - assertEq( - _newRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - - set rewards for both tokens - - default time unit - //////////////////////////////////////////////////////////////*/ - - function test_state_setRewardsPerUnitTime_bothTokens() public { - // set value and check - uint256 rewardsPerUnitTime = 50; - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(0, rewardsPerUnitTime); - assertEq(rewardsPerUnitTime, stakeContract.getRewardsPerUnitTime(0)); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(0, 200); - assertEq(200, stakeContract.getRewardsPerUnitTime(0)); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 50) * rewardsPerUnitTime) / defaultTimeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * 50) * 200) / defaultTimeUnit) - ); - - // =========== token 1 - //================ stake tokens - - vm.prank(stakerOne); - stakeContract.stake(1, 20); - timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and set rewardsPerUnitTime for token-1 - vm.roll(400); - vm.warp(4000); - - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(1, 300); - assertEq(300, stakeContract.getRewardsPerUnitTime(1)); - newTimeOfLastUpdate = block.timestamp; - - // check available rewards for token-1 -- should use defaultRewardsPerUnitTime for calculation - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - //====== check rewards after some time - vm.roll(500); - vm.warp(5000); - - (, _newRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - // should calculate based on newTimeOfLastUpdate and rewardsPerUnitTime (not default) - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * 20) * 300) / defaultTimeUnit) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - - default rewards - - set time unit for token0 - //////////////////////////////////////////////////////////////*/ - - function test_state_setTimeUnit_token0() public { - // set value and check - uint80 timeUnit = 100; - vm.prank(deployer); - stakeContract.setTimeUnit(0, timeUnit); - assertEq(timeUnit, stakeContract.getTimeUnit(0)); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set timeUnit - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setTimeUnit(0, 200); - assertEq(200, stakeContract.getTimeUnit(0)); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for timeUnit for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 50) * defaultRewardsPerUnitTime) / timeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * 50) * defaultRewardsPerUnitTime) / 200) - ); - - // =========== token 1 - //================ stake tokens - - vm.prank(stakerOne); - stakeContract.stake(1, 20); - timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime for token-0 - vm.roll(400); - vm.warp(4000); - - vm.prank(deployer); - stakeContract.setTimeUnit(0, 10); - assertEq(10, stakeContract.getTimeUnit(0)); - newTimeOfLastUpdate = block.timestamp; - - // check available rewards for token-1 -- should use defaultTimeUnit for calculation - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - //====== check rewards after some time - vm.roll(500); - vm.warp(5000); - - (, _newRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - assertEq( - _newRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - - default rewards - - set time unit for both tokens - //////////////////////////////////////////////////////////////*/ - - function test_state_setTimeUnit_bothTokens() public { - // set value and check - uint80 timeUnit = 100; - vm.prank(deployer); - stakeContract.setTimeUnit(0, timeUnit); - assertEq(timeUnit, stakeContract.getTimeUnit(0)); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setTimeUnit(0, 200); - assertEq(200, stakeContract.getTimeUnit(0)); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for timeUnit for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 50) * defaultRewardsPerUnitTime) / timeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * 50) * defaultRewardsPerUnitTime) / 200) - ); - - // =========== token 1 - //================ stake tokens - - vm.prank(stakerOne); - stakeContract.stake(1, 20); - timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and set timeUnit for token-1 - vm.roll(400); - vm.warp(4000); - - vm.prank(deployer); - stakeContract.setTimeUnit(1, 300); - assertEq(300, stakeContract.getTimeUnit(1)); - newTimeOfLastUpdate = block.timestamp; - - // check available rewards for token-1 -- should use defaultTimeUnit for calculation - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - //====== check rewards after some time - vm.roll(500); - vm.warp(5000); - - (, _newRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - // should calculate based on newTimeOfLastUpdate and new time unit (not default) - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / 300) - ); - } - - function test_revert_setRewardsPerUnitTime_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setRewardsPerUnitTime(0, 1); - } - - function test_revert_setTimeUnit_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setTimeUnit(0, 1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: withdraw - - different token-ids staked by stakers - //////////////////////////////////////////////////////////////*/ - - function test_state_withdraw_differentTokens() public { - //================ stake different tokens ====================== - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - - vm.prank(stakerTwo); - stakeContract.stake(1, 20); - - uint256 timeOfLastUpdate = block.timestamp; - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - // withdraw partially for stakerOne - vm.prank(stakerOne); - stakeContract.withdraw(0, 40); - uint256 timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(erc1155.balanceOf(stakerOne, 0), 90); - assertEq(erc1155.balanceOf(address(stakeContract), 0), 10); - assertEq(erc1155.balanceOf(stakerTwo, 1), 80); - assertEq(erc1155.balanceOf(address(stakeContract), 1), 20); - - // check available rewards after withdraw - (, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerTwo); - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards some time after withdraw - vm.roll(200); - vm.warp(2000); - - // check rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - (((((timeOfLastUpdateLatest - timeOfLastUpdate) * 50)) * defaultRewardsPerUnitTime) / defaultTimeUnit) + - (((((block.timestamp - timeOfLastUpdateLatest) * 10)) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // withdraw partially for stakerTwo - vm.prank(stakerTwo); - stakeContract.withdraw(1, 10); - timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(erc1155.balanceOf(stakerOne, 0), 90); - assertEq(erc1155.balanceOf(address(stakeContract), 0), 10); - assertEq(erc1155.balanceOf(stakerTwo, 1), 90); - assertEq(erc1155.balanceOf(address(stakeContract), 1), 10); - - // check rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerTwo); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 20)) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check rewards for stakerTwo after some time - vm.roll(300); - vm.warp(3000); - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerTwo); - - assertEq( - _availableRewards, - (((((timeOfLastUpdateLatest - timeOfLastUpdate) * 20)) * defaultRewardsPerUnitTime) / defaultTimeUnit) + - (((((block.timestamp - timeOfLastUpdateLatest) * 10)) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: withdraw - - same token-ids staked by stakers - //////////////////////////////////////////////////////////////*/ - - function test_state_withdraw_sameToken() public { - //================ stake different tokens ====================== - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - - vm.prank(stakerTwo); - stakeContract.stake(0, 20); - - uint256 timeOfLastUpdate = block.timestamp; - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - // withdraw partially for stakerOne - vm.prank(stakerOne); - stakeContract.withdraw(0, 40); - uint256 timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(erc1155.balanceOf(stakerOne, 0), 90); - assertEq(erc1155.balanceOf(stakerTwo, 0), 80); - assertEq(erc1155.balanceOf(address(stakeContract), 0), 10 + 20); - - // check available rewards after withdraw - (, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerTwo); - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards some time after withdraw - vm.roll(200); - vm.warp(2000); - - // check rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - (((((timeOfLastUpdateLatest - timeOfLastUpdate) * 50)) * defaultRewardsPerUnitTime) / defaultTimeUnit) + - (((((block.timestamp - timeOfLastUpdateLatest) * 10)) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // withdraw partially for stakerTwo - vm.prank(stakerTwo); - stakeContract.withdraw(0, 10); - timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(erc1155.balanceOf(stakerOne, 0), 90); - assertEq(erc1155.balanceOf(stakerTwo, 0), 90); - assertEq(erc1155.balanceOf(address(stakeContract), 0), 10 + 10); - - // check rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerTwo); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 20)) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check rewards for stakerTwo after some time - vm.roll(300); - vm.warp(3000); - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerTwo); - - assertEq( - _availableRewards, - (((((timeOfLastUpdateLatest - timeOfLastUpdate) * 20)) * defaultRewardsPerUnitTime) / defaultTimeUnit) + - (((((block.timestamp - timeOfLastUpdateLatest) * 10)) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - function test_revert_withdraw_withdrawingZeroTokens() public { - vm.expectRevert("Withdrawing 0 tokens"); - stakeContract.withdraw(0, 0); - } - - function test_revert_withdraw_withdrawingMoreThanStaked() public { - // stake tokens - vm.prank(stakerOne); - stakeContract.stake(0, 50); - - vm.prank(stakerTwo); - stakeContract.stake(1, 20); - - vm.prank(stakerTwo); - stakeContract.stake(0, 20); - - // view staked tokens - vm.roll(200); - vm.warp(2000); - (uint256[] memory _tokensStaked, uint256[] memory _tokenAmounts, uint256 _totalRewards) = stakeContract - .getStakeInfo(stakerOne); - - console.log("==== staker one ===="); - for (uint256 i = 0; i < _tokensStaked.length; i++) { - console.log(_tokensStaked[i], _tokenAmounts[i]); - } - - (_tokensStaked, _tokenAmounts, _totalRewards) = stakeContract.getStakeInfo(stakerTwo); - - console.log("==== staker two ===="); - for (uint256 i = 0; i < _tokensStaked.length; i++) { - console.log(_tokensStaked[i], _tokenAmounts[i]); - } - - // trying to withdraw more than staked - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(0, 60); - - // withdraw partially - vm.prank(stakerOne); - stakeContract.withdraw(0, 30); - - // trying to withdraw more than staked - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(0, 60); - - // re-stake - vm.prank(stakerOne); - stakeContract.stake(0, 30); - - // trying to withdraw more than staked - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(0, 60); - - // trying to withdraw different tokens - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(1, 20); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_revert_zeroTimeUnit_adminLockTokens() public { - //================ stake tokens - vm.warp(1); - - // User stakes tokens - vm.prank(stakerOne); - stakeContract.stake(0, 50); - - // set default timeUnit to zero - uint80 newTimeUnit = 0; - vm.prank(deployer); - vm.expectRevert("time-unit can't be 0"); - stakeContract.setDefaultTimeUnit(newTimeUnit); - - // set timeUnit to zero - vm.prank(deployer); - vm.expectRevert("time-unit can't be 0"); - stakeContract.setTimeUnit(0, newTimeUnit); - - // stakerOne and stakerTwo can withdraw their tokens - // vm.expectRevert(stdError.divisionError); - vm.prank(stakerOne); - stakeContract.withdraw(0, 50); - } - - function test_Macro_EditionDirectSafeTransferLocksToken() public { - uint256 tokenId = 0; - - // stakerOne mistakenly safe-transfers direct to the staking contract - vm.prank(stakerOne); - vm.expectRevert("Direct transfer"); - erc1155.safeTransferFrom(stakerOne, address(stakeContract), tokenId, 100, ""); - - // show that the transferred tokens were not properly staked - // (uint256 tokensStaked, uint256 rewards) = stakeContract.getStakeInfoForToken(tokenId, stakerOne); - // assertEq(0, tokensStaked); - - // // show that stakerOne cannot recover the tokens - // vm.expectRevert(); - // vm.prank(stakerOne); - // stakeContract.withdraw(tokenId, 100); - } -} - -contract Macro_EditionStakeTest is BaseTest { - EditionStake internal stakeContract; - - uint256 internal defaultTimeUnit; - uint256 internal defaultRewardsPerUnitTime; - uint64 internal tokenAmount = 100; - address internal stakerOne = address(0x345); - address internal stakerTwo = address(0x567); - - function setUp() public override { - super.setUp(); - - defaultTimeUnit = 60; - defaultRewardsPerUnitTime = 1; - - // mint erc1155 tokens to stakers - erc1155.mint(stakerOne, 1, tokenAmount); - erc1155.mint(stakerTwo, 2, tokenAmount); - - // mint reward tokens to contract admin - erc20.mint(deployer, 1000 ether); - - stakeContract = EditionStake(payable(getContract("EditionStake"))); - - // set approval - vm.prank(stakerOne); - erc1155.setApprovalForAll(address(stakeContract), true); - vm.prank(stakerTwo); - erc1155.setApprovalForAll(address(stakeContract), true); - } - - // Demostrate setting unitTime to 0 locks the tokens irreversibly - function testEdition_adminLockTokens() public { - //================ stake tokens - vm.warp(1); - - // Two users stake 1 tokens each - vm.prank(stakerOne); - stakeContract.stake(1, tokenAmount); - vm.prank(stakerTwo); - stakeContract.stake(2, tokenAmount); - - // set timeUnit to zero - uint80 newTimeUnit = 0; - vm.prank(deployer); - vm.expectRevert("time-unit can't be 0"); - stakeContract.setDefaultTimeUnit(newTimeUnit); - - // stakerOne and stakerTwo can't withdraw their tokens - // vm.expectRevert(stdError.divisionError); - vm.prank(stakerOne); - stakeContract.withdraw(1, tokenAmount); - - // vm.expectRevert(stdError.divisionError); - vm.prank(stakerTwo); - stakeContract.withdraw(2, tokenAmount); - - // timeUnit can't be changed back to a nonzero value - newTimeUnit = 40; - // vm.expectRevert(stdError.divisionError); - vm.prank(deployer); - stakeContract.setDefaultTimeUnit(newTimeUnit); - } - - // Demostrate setting rewardsPerTimeUnit to a high value locks the tokens irreversibly - function testEdition_demostrate_adminRewardsLock() public { - //================ stake tokens - vm.warp(1); - - // Two users stake 1 tokens each - vm.prank(stakerOne); - stakeContract.stake(1, tokenAmount); - vm.prank(stakerTwo); - stakeContract.stake(2, tokenAmount); - - // set rewardsPerTimeUnit to max value - uint256 rewardsPerTimeUnit = type(uint256).max; - vm.prank(deployer); - stakeContract.setDefaultRewardsPerUnitTime(rewardsPerTimeUnit); - - vm.warp(1 days); - - // stakerOne and stakerTwo can't withdraw their tokens - // vm.expectRevert(stdError.arithmeticError); - vm.prank(stakerOne); - stakeContract.withdraw(1, tokenAmount); - - // vm.expectRevert(stdError.arithmeticError); - vm.prank(stakerTwo); - stakeContract.withdraw(2, tokenAmount); - - // timeUnit can't be changed back - rewardsPerTimeUnit = 60; - // vm.expectRevert(stdError.arithmeticError); - vm.prank(deployer); - stakeContract.setDefaultRewardsPerUnitTime(rewardsPerTimeUnit); - } -} diff --git a/src/test/staking/EditionStake_EthReward.t.sol b/src/test/staking/EditionStake_EthReward.t.sol deleted file mode 100644 index 2f829cdb9..000000000 --- a/src/test/staking/EditionStake_EthReward.t.sol +++ /dev/null @@ -1,1063 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { EditionStake } from "contracts/prebuilts/staking/EditionStake.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; - -contract EditionStakeEthRewardTest is BaseTest { - EditionStake internal stakeContract; - - address internal stakerOne; - address internal stakerTwo; - - uint256 internal defaultTimeUnit; - uint256 internal defaultRewardsPerUnitTime; - - function setUp() public override { - super.setUp(); - - defaultTimeUnit = 60; - defaultRewardsPerUnitTime = 1; - - stakerOne = address(0x345); - stakerTwo = address(0x567); - - erc1155.mint(stakerOne, 0, 100); // mint 100 tokens with id 0 to stakerOne - erc1155.mint(stakerOne, 1, 100); // mint 100 tokens with id 1 to stakerOne - - erc1155.mint(stakerTwo, 0, 100); // mint 100 tokens with id 0 to stakerTwo - erc1155.mint(stakerTwo, 1, 100); // mint 100 tokens with id 1 to stakerTwo - - vm.deal(deployer, 1000 ether); // mint reward tokens (Eth) to contract admin - - stakeContract = EditionStake( - payable( - deployContractProxy( - "EditionStake", - abi.encodeCall( - EditionStake.initialize, - (deployer, CONTRACT_URI, forwarders(), NATIVE_TOKEN, address(erc1155), 60, 1) - ) - ) - ) - ); - - // set approvals - vm.prank(stakerOne); - erc1155.setApprovalForAll(address(stakeContract), true); - - vm.prank(stakerTwo); - erc1155.setApprovalForAll(address(stakeContract), true); - - vm.startPrank(deployer); - stakeContract.depositRewardTokens{ value: 100 ether }(100 ether); - // erc20.transfer(address(stakeContract), 100 ether); - vm.stopPrank(); - assertEq(stakeContract.getRewardTokenBalance(), 100 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: Stake - - with default time-unit and rewards - - different token-ids staked by stakers - //////////////////////////////////////////////////////////////*/ - - function test_state_stake_defaults_differentTokens() public { - //================ first staker ====================== - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate_one = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc1155.balanceOf(address(stakeContract), 0), 50); - assertEq(erc1155.balanceOf(address(stakerOne), 0), 50); - - // check available rewards right after staking - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq(_amountStaked, 50); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(100); - vm.warp(1000); - - // check available rewards after warp - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - //================ second staker ====================== - vm.roll(200); - vm.warp(2000); - - // stake 20 tokens with token-id 1 - vm.prank(stakerTwo); - stakeContract.stake(1, 20); - uint256 timeOfLastUpdate_two = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc1155.balanceOf(address(stakeContract), 1), 20); - assertEq(erc1155.balanceOf(address(stakerTwo), 1), 80); - - // check available rewards right after staking - (_amountStaked, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerTwo); - - assertEq(_amountStaked, 20); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(300); - vm.warp(3000); - - // check available rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerTwo); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_two) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - function test_revert_stake_stakingZeroTokens() public { - // stake 0 tokens - - vm.prank(stakerOne); - vm.expectRevert("Staking 0 tokens"); - stakeContract.stake(0, 0); - } - - function test_revert_stake_notBalanceOrApproved() public { - // stake unowned tokens - vm.prank(stakerOne); - vm.expectRevert("ERC1155: insufficient balance for transfer"); - stakeContract.stake(2, 10); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: Stake - - with default time-unit and rewards - - same token-id staked by stakers - //////////////////////////////////////////////////////////////*/ - - function test_state_stake_defaults_sameToken() public { - //================ first staker ====================== - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate_one = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc1155.balanceOf(address(stakeContract), 0), 50); - assertEq(erc1155.balanceOf(address(stakerOne), 0), 50); - - // check available rewards right after staking - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq(_amountStaked, 50); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(100); - vm.warp(1000); - - // check available rewards after warp - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - //================ second staker ====================== - vm.roll(200); - vm.warp(2000); - - // stake 20 tokens with token-id 0 - vm.prank(stakerTwo); - stakeContract.stake(0, 20); - uint256 timeOfLastUpdate_two = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc1155.balanceOf(address(stakeContract), 0), 20 + 50); // sum of staked tokens by both stakers - assertEq(erc1155.balanceOf(address(stakerTwo), 0), 80); - - // check available rewards right after staking - (_amountStaked, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerTwo); - - assertEq(_amountStaked, 20); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(300); - vm.warp(3000); - - // check available rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerTwo); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_two) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: claimRewards - - default timeUnit and rewards - - different token-ids staked by stakers - //////////////////////////////////////////////////////////////*/ - - function test_state_claimRewards_defaults_differentTokens() public { - //================ setup stakerOne ====================== - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate_one = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - - uint256 rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerOne); - stakeContract.claimRewards(0); - uint256 rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - stakerOne.balance, - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards after claiming - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq(_amountStaked, 50); - assertEq(_availableRewards, 0); - - //================ setup stakerTwo ====================== - - // stake 20 tokens with token-id 1 - vm.prank(stakerTwo); - stakeContract.stake(1, 20); - uint256 timeOfLastUpdate_two = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(200); - vm.warp(2000); - - rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerTwo); - stakeContract.claimRewards(1); - rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - stakerTwo.balance, - ((((block.timestamp - timeOfLastUpdate_two) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - ((((block.timestamp - timeOfLastUpdate_two) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards after claiming -- stakerTwo - (_amountStaked, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerTwo); - assertEq(_amountStaked, 20); - assertEq(_availableRewards, 0); - - // check available rewards -- stakerOne - (_amountStaked, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - assertEq(_amountStaked, 50); - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_two) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - function test_revert_claimRewards_noRewards() public { - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - - //=================== try to claim rewards for a different token - - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(1); - - //=================== try to claim rewards in same block - - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(0); - - //======= withdraw tokens and claim rewards - vm.roll(100); - vm.warp(1000); - - vm.prank(stakerOne); - stakeContract.withdraw(0, 50); - vm.prank(stakerOne); - stakeContract.claimRewards(0); - - //===== try to claim rewards again - vm.roll(200); - vm.warp(2000); - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(0); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: claimRewards - - default timeUnit and rewards - - same token-ids staked by stakers - //////////////////////////////////////////////////////////////*/ - - function test_state_claimRewards_defaults_sameToken() public { - //================ setup stakerOne ====================== - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate_one = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - - uint256 rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerOne); - stakeContract.claimRewards(0); - uint256 rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - stakerOne.balance, - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - ((((block.timestamp - timeOfLastUpdate_one) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards after claiming - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq(_amountStaked, 50); - assertEq(_availableRewards, 0); - - //================ setup stakerTwo ====================== - - // stake 20 tokens with token-id 1 - vm.prank(stakerTwo); - stakeContract.stake(0, 20); - uint256 timeOfLastUpdate_two = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(200); - vm.warp(2000); - - rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerTwo); - stakeContract.claimRewards(0); - rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - stakerTwo.balance, - ((((block.timestamp - timeOfLastUpdate_two) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - ((((block.timestamp - timeOfLastUpdate_two) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards after claiming -- stakerTwo - (_amountStaked, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerTwo); - assertEq(_amountStaked, 20); - assertEq(_availableRewards, 0); - - // check available rewards -- stakerOne - (_amountStaked, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - assertEq(_amountStaked, 50); - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_two) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - - set rewards for token0 - - default time unit - //////////////////////////////////////////////////////////////*/ - - function test_state_setRewardsPerUnitTime_token0() public { - // set value and check - uint256 rewardsPerUnitTime = 50; - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(0, rewardsPerUnitTime); - assertEq(rewardsPerUnitTime, stakeContract.getRewardsPerUnitTime(0)); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(0, 200); - assertEq(200, stakeContract.getRewardsPerUnitTime(0)); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 50) * rewardsPerUnitTime) / defaultTimeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * 50) * 200) / defaultTimeUnit) - ); - - // =========== token 1 - //================ stake tokens - - vm.prank(stakerOne); - stakeContract.stake(1, 20); - timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime for token-0 - vm.roll(400); - vm.warp(4000); - - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(0, 300); - assertEq(300, stakeContract.getRewardsPerUnitTime(0)); - newTimeOfLastUpdate = block.timestamp; - - // check available rewards for token-1 -- should use defaultRewardsPerUnitTime for calculation - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - //====== check rewards after some time - vm.roll(500); - vm.warp(5000); - - (, _newRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - assertEq( - _newRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - - set rewards for both tokens - - default time unit - //////////////////////////////////////////////////////////////*/ - - function test_state_setRewardsPerUnitTime_bothTokens() public { - // set value and check - uint256 rewardsPerUnitTime = 50; - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(0, rewardsPerUnitTime); - assertEq(rewardsPerUnitTime, stakeContract.getRewardsPerUnitTime(0)); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(0, 200); - assertEq(200, stakeContract.getRewardsPerUnitTime(0)); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 50) * rewardsPerUnitTime) / defaultTimeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * 50) * 200) / defaultTimeUnit) - ); - - // =========== token 1 - //================ stake tokens - - vm.prank(stakerOne); - stakeContract.stake(1, 20); - timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and set rewardsPerUnitTime for token-1 - vm.roll(400); - vm.warp(4000); - - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(1, 300); - assertEq(300, stakeContract.getRewardsPerUnitTime(1)); - newTimeOfLastUpdate = block.timestamp; - - // check available rewards for token-1 -- should use defaultRewardsPerUnitTime for calculation - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - //====== check rewards after some time - vm.roll(500); - vm.warp(5000); - - (, _newRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - // should calculate based on newTimeOfLastUpdate and rewardsPerUnitTime (not default) - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * 20) * 300) / defaultTimeUnit) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - - default rewards - - set time unit for token0 - //////////////////////////////////////////////////////////////*/ - - function test_state_setTimeUnit_token0() public { - // set value and check - uint80 timeUnit = 100; - vm.prank(deployer); - stakeContract.setTimeUnit(0, timeUnit); - assertEq(timeUnit, stakeContract.getTimeUnit(0)); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set timeUnit - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setTimeUnit(0, 200); - assertEq(200, stakeContract.getTimeUnit(0)); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for timeUnit for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 50) * defaultRewardsPerUnitTime) / timeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * 50) * defaultRewardsPerUnitTime) / 200) - ); - - // =========== token 1 - //================ stake tokens - - vm.prank(stakerOne); - stakeContract.stake(1, 20); - timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime for token-0 - vm.roll(400); - vm.warp(4000); - - vm.prank(deployer); - stakeContract.setTimeUnit(0, 10); - assertEq(10, stakeContract.getTimeUnit(0)); - newTimeOfLastUpdate = block.timestamp; - - // check available rewards for token-1 -- should use defaultTimeUnit for calculation - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - //====== check rewards after some time - vm.roll(500); - vm.warp(5000); - - (, _newRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - assertEq( - _newRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - - default rewards - - set time unit for both tokens - //////////////////////////////////////////////////////////////*/ - - function test_state_setTimeUnit_bothTokens() public { - // set value and check - uint80 timeUnit = 100; - vm.prank(deployer); - stakeContract.setTimeUnit(0, timeUnit); - assertEq(timeUnit, stakeContract.getTimeUnit(0)); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setTimeUnit(0, 200); - assertEq(200, stakeContract.getTimeUnit(0)); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for timeUnit for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 50) * defaultRewardsPerUnitTime) / timeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * 50) * defaultRewardsPerUnitTime) / 200) - ); - - // =========== token 1 - //================ stake tokens - - vm.prank(stakerOne); - stakeContract.stake(1, 20); - timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and set timeUnit for token-1 - vm.roll(400); - vm.warp(4000); - - vm.prank(deployer); - stakeContract.setTimeUnit(1, 300); - assertEq(300, stakeContract.getTimeUnit(1)); - newTimeOfLastUpdate = block.timestamp; - - // check available rewards for token-1 -- should use defaultTimeUnit for calculation - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - //====== check rewards after some time - vm.roll(500); - vm.warp(5000); - - (, _newRewards) = stakeContract.getStakeInfoForToken(1, stakerOne); - - // should calculate based on newTimeOfLastUpdate and new time unit (not default) - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / 300) - ); - } - - function test_revert_setRewardsPerUnitTime_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setRewardsPerUnitTime(0, 1); - } - - function test_revert_setTimeUnit_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setTimeUnit(0, 1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: withdraw - - different token-ids staked by stakers - //////////////////////////////////////////////////////////////*/ - - function test_state_withdraw_differentTokens() public { - //================ stake different tokens ====================== - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - - vm.prank(stakerTwo); - stakeContract.stake(1, 20); - - uint256 timeOfLastUpdate = block.timestamp; - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - // withdraw partially for stakerOne - vm.prank(stakerOne); - stakeContract.withdraw(0, 40); - uint256 timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(erc1155.balanceOf(stakerOne, 0), 90); - assertEq(erc1155.balanceOf(address(stakeContract), 0), 10); - assertEq(erc1155.balanceOf(stakerTwo, 1), 80); - assertEq(erc1155.balanceOf(address(stakeContract), 1), 20); - - // check available rewards after withdraw - (, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerTwo); - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards some time after withdraw - vm.roll(200); - vm.warp(2000); - - // check rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - (((((timeOfLastUpdateLatest - timeOfLastUpdate) * 50)) * defaultRewardsPerUnitTime) / defaultTimeUnit) + - (((((block.timestamp - timeOfLastUpdateLatest) * 10)) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // withdraw partially for stakerTwo - vm.prank(stakerTwo); - stakeContract.withdraw(1, 10); - timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(erc1155.balanceOf(stakerOne, 0), 90); - assertEq(erc1155.balanceOf(address(stakeContract), 0), 10); - assertEq(erc1155.balanceOf(stakerTwo, 1), 90); - assertEq(erc1155.balanceOf(address(stakeContract), 1), 10); - - // check rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerTwo); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 20)) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check rewards for stakerTwo after some time - vm.roll(300); - vm.warp(3000); - (, _availableRewards) = stakeContract.getStakeInfoForToken(1, stakerTwo); - - assertEq( - _availableRewards, - (((((timeOfLastUpdateLatest - timeOfLastUpdate) * 20)) * defaultRewardsPerUnitTime) / defaultTimeUnit) + - (((((block.timestamp - timeOfLastUpdateLatest) * 10)) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: withdraw - - same token-ids staked by stakers - //////////////////////////////////////////////////////////////*/ - - function test_state_withdraw_sameToken() public { - //================ stake different tokens ====================== - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(0, 50); - - vm.prank(stakerTwo); - stakeContract.stake(0, 20); - - uint256 timeOfLastUpdate = block.timestamp; - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - // withdraw partially for stakerOne - vm.prank(stakerOne); - stakeContract.withdraw(0, 40); - uint256 timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(erc1155.balanceOf(stakerOne, 0), 90); - assertEq(erc1155.balanceOf(stakerTwo, 0), 80); - assertEq(erc1155.balanceOf(address(stakeContract), 0), 10 + 20); - - // check available rewards after withdraw - (, uint256 _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 50) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerTwo); - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * 20) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check available rewards some time after withdraw - vm.roll(200); - vm.warp(2000); - - // check rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerOne); - - assertEq( - _availableRewards, - (((((timeOfLastUpdateLatest - timeOfLastUpdate) * 50)) * defaultRewardsPerUnitTime) / defaultTimeUnit) + - (((((block.timestamp - timeOfLastUpdateLatest) * 10)) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // withdraw partially for stakerTwo - vm.prank(stakerTwo); - stakeContract.withdraw(0, 10); - timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(erc1155.balanceOf(stakerOne, 0), 90); - assertEq(erc1155.balanceOf(stakerTwo, 0), 90); - assertEq(erc1155.balanceOf(address(stakeContract), 0), 10 + 10); - - // check rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerTwo); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 20)) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - - // check rewards for stakerTwo after some time - vm.roll(300); - vm.warp(3000); - (, _availableRewards) = stakeContract.getStakeInfoForToken(0, stakerTwo); - - assertEq( - _availableRewards, - (((((timeOfLastUpdateLatest - timeOfLastUpdate) * 20)) * defaultRewardsPerUnitTime) / defaultTimeUnit) + - (((((block.timestamp - timeOfLastUpdateLatest) * 10)) * defaultRewardsPerUnitTime) / defaultTimeUnit) - ); - } - - function test_revert_withdraw_withdrawingZeroTokens() public { - vm.expectRevert("Withdrawing 0 tokens"); - stakeContract.withdraw(0, 0); - } - - function test_revert_withdraw_withdrawingMoreThanStaked() public { - // stake tokens - vm.prank(stakerOne); - stakeContract.stake(0, 50); - - vm.prank(stakerTwo); - stakeContract.stake(1, 20); - - vm.prank(stakerTwo); - stakeContract.stake(0, 20); - - // view staked tokens - vm.roll(200); - vm.warp(2000); - (uint256[] memory _tokensStaked, uint256[] memory _tokenAmounts, uint256 _totalRewards) = stakeContract - .getStakeInfo(stakerOne); - - console.log("==== staker one ===="); - for (uint256 i = 0; i < _tokensStaked.length; i++) { - console.log(_tokensStaked[i], _tokenAmounts[i]); - } - - (_tokensStaked, _tokenAmounts, _totalRewards) = stakeContract.getStakeInfo(stakerTwo); - - console.log("==== staker two ===="); - for (uint256 i = 0; i < _tokensStaked.length; i++) { - console.log(_tokensStaked[i], _tokenAmounts[i]); - } - - // trying to withdraw more than staked - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(0, 60); - - // withdraw partially - vm.prank(stakerOne); - stakeContract.withdraw(0, 30); - - // trying to withdraw more than staked - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(0, 60); - - // re-stake - vm.prank(stakerOne); - stakeContract.stake(0, 30); - - // trying to withdraw more than staked - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(0, 60); - - // trying to withdraw different tokens - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(1, 20); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_revert_zeroTimeUnit_adminLockTokens() public { - //================ stake tokens - vm.warp(1); - - // User stakes tokens - vm.prank(stakerOne); - stakeContract.stake(0, 50); - - // set default timeUnit to zero - uint80 newTimeUnit = 0; - vm.prank(deployer); - vm.expectRevert("time-unit can't be 0"); - stakeContract.setDefaultTimeUnit(newTimeUnit); - - // set timeUnit to zero - vm.prank(deployer); - vm.expectRevert("time-unit can't be 0"); - stakeContract.setTimeUnit(0, newTimeUnit); - - // stakerOne and stakerTwo can withdraw their tokens - // vm.expectRevert(stdError.divisionError); - vm.prank(stakerOne); - stakeContract.withdraw(0, 50); - } - - function test_Macro_EditionDirectSafeTransferLocksToken() public { - uint256 tokenId = 0; - - // stakerOne mistakenly safe-transfers direct to the staking contract - vm.prank(stakerOne); - vm.expectRevert("Direct transfer"); - erc1155.safeTransferFrom(stakerOne, address(stakeContract), tokenId, 100, ""); - - // show that the transferred tokens were not properly staked - // (uint256 tokensStaked, uint256 rewards) = stakeContract.getStakeInfoForToken(tokenId, stakerOne); - // assertEq(0, tokensStaked); - - // // show that stakerOne cannot recover the tokens - // vm.expectRevert(); - // vm.prank(stakerOne); - // stakeContract.withdraw(tokenId, 100); - } -} diff --git a/src/test/staking/NFTStake.t.sol b/src/test/staking/NFTStake.t.sol deleted file mode 100644 index 8acd02214..000000000 --- a/src/test/staking/NFTStake.t.sol +++ /dev/null @@ -1,583 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { NFTStake } from "contracts/prebuilts/staking/NFTStake.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; - -contract NFTStakeTest is BaseTest { - NFTStake internal stakeContract; - - address internal stakerOne; - address internal stakerTwo; - - uint256 internal timeUnit; - uint256 internal rewardsPerUnitTime; - - function setUp() public override { - super.setUp(); - - timeUnit = 60; - rewardsPerUnitTime = 1; - - stakerOne = address(0x345); - stakerTwo = address(0x567); - - erc721.mint(stakerOne, 5); // mint token id 0 to 4 - erc721.mint(stakerTwo, 5); // mint token id 5 to 9 - erc20.mint(deployer, 1000 ether); // mint reward tokens to contract admin - - stakeContract = NFTStake(payable(getContract("NFTStake"))); - - // set approvals - vm.prank(stakerOne); - erc721.setApprovalForAll(address(stakeContract), true); - - vm.prank(stakerTwo); - erc721.setApprovalForAll(address(stakeContract), true); - - vm.startPrank(deployer); - erc20.approve(address(stakeContract), type(uint256).max); - stakeContract.depositRewardTokens(100 ether); - // erc20.transfer(address(stakeContract), 100 ether); - vm.stopPrank(); - assertEq(stakeContract.getRewardTokenBalance(), 100 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: Stake - //////////////////////////////////////////////////////////////*/ - - function test_state_stake() public { - //================ first staker ====================== - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - uint256 timeOfLastUpdate_one = block.timestamp; - - // check balances/ownership of staked tokens - for (uint256 i = 0; i < _tokenIdsOne.length; i++) { - assertEq(erc721.ownerOf(_tokenIdsOne[i]), address(stakeContract)); - assertEq(stakeContract.stakerAddress(_tokenIdsOne[i]), stakerOne); - } - assertEq(erc721.balanceOf(stakerOne), 2); - assertEq(erc721.balanceOf(address(stakeContract)), _tokenIdsOne.length); - - // check available rewards right after staking - (uint256[] memory _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_amountStaked.length, _tokenIdsOne.length); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(100); - vm.warp(1000); - - // check available rewards after warp - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * _tokenIdsOne.length) * rewardsPerUnitTime) / timeUnit) - ); - - //================ second staker ====================== - vm.roll(200); - vm.warp(2000); - uint256[] memory _tokenIdsTwo = new uint256[](2); - _tokenIdsTwo[0] = 5; - _tokenIdsTwo[1] = 6; - - // stake 2 tokens - vm.prank(stakerTwo); - stakeContract.stake(_tokenIdsTwo); - uint256 timeOfLastUpdate_two = block.timestamp; - - // check balances/ownership of staked tokens - for (uint256 i = 0; i < _tokenIdsTwo.length; i++) { - assertEq(erc721.ownerOf(_tokenIdsTwo[i]), address(stakeContract)); - assertEq(stakeContract.stakerAddress(_tokenIdsTwo[i]), stakerTwo); - } - assertEq(erc721.balanceOf(stakerTwo), 3); - assertEq(erc721.balanceOf(address(stakeContract)), _tokenIdsTwo.length + _tokenIdsOne.length); - - // check available rewards right after staking - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq(_amountStaked.length, _tokenIdsTwo.length); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(300); - vm.warp(3000); - - // check available rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * _tokenIdsOne.length) * rewardsPerUnitTime) / timeUnit) - ); - - // check available rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_two) * _tokenIdsTwo.length) * rewardsPerUnitTime) / timeUnit) - ); - } - - function test_revert_stake_stakingZeroTokens() public { - // stake 0 tokens - uint256[] memory _tokenIds; - - vm.prank(stakerOne); - vm.expectRevert("Staking 0 tokens"); - stakeContract.stake(_tokenIds); - } - - function test_revert_stake_notStaker() public { - // stake unowned tokens - uint256[] memory _tokenIds = new uint256[](1); - _tokenIds[0] = 6; - - vm.prank(stakerOne); - vm.expectRevert("ERC721: transfer from incorrect owner"); - stakeContract.stake(_tokenIds); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: claimRewards - //////////////////////////////////////////////////////////////*/ - - function test_state_claimRewards() public { - //================ first staker ====================== - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - uint256 timeOfLastUpdate_one = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - - uint256 rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerOne); - stakeContract.claimRewards(); - uint256 rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - erc20.balanceOf(stakerOne), - ((((block.timestamp - timeOfLastUpdate_one) * _tokenIdsOne.length) * rewardsPerUnitTime) / timeUnit) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - ((((block.timestamp - timeOfLastUpdate_one) * _tokenIdsOne.length) * rewardsPerUnitTime) / timeUnit) - ); - - // check available rewards after claiming - (uint256[] memory _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_amountStaked.length, _tokenIdsOne.length); - assertEq(_availableRewards, 0); - } - - function test_revert_claimRewards_noRewards() public { - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - - //=================== try to claim rewards in same block - - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(); - - //======= withdraw tokens and claim rewards - vm.roll(100); - vm.warp(1000); - - vm.prank(stakerOne); - stakeContract.withdraw(_tokenIdsOne); - vm.prank(stakerOne); - stakeContract.claimRewards(); - - //===== try to claim rewards again - vm.roll(200); - vm.warp(2000); - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - //////////////////////////////////////////////////////////////*/ - - function test_state_setRewardsPerUnitTime() public { - // check current value - assertEq(rewardsPerUnitTime, stakeContract.getRewardsPerUnitTime()); - - // set new value and check - uint256 newRewardsPerUnitTime = 50; - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(newRewardsPerUnitTime); - assertEq(newRewardsPerUnitTime, stakeContract.getRewardsPerUnitTime()); - - //================ stake tokens - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(200); - assertEq(200, stakeContract.getRewardsPerUnitTime()); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * _tokenIdsOne.length) * newRewardsPerUnitTime) / timeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * _tokenIdsOne.length) * 200) / timeUnit) - ); - } - - function test_revert_setRewardsPerUnitTime_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setRewardsPerUnitTime(1); - } - - function test_state_setTimeUnit() public { - // check current value - assertEq(timeUnit, stakeContract.getTimeUnit()); - - // set new value and check - uint256 newTimeUnit = 2 minutes; - vm.prank(deployer); - stakeContract.setTimeUnit(newTimeUnit); - assertEq(newTimeUnit, stakeContract.getTimeUnit()); - - //================ stake tokens - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setTimeUnit(1 seconds); - assertEq(1 seconds, stakeContract.getTimeUnit()); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * _tokenIdsOne.length) * rewardsPerUnitTime) / newTimeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _newRewards, - _availableRewards + - ((((block.timestamp - newTimeOfLastUpdate) * _tokenIdsOne.length) * rewardsPerUnitTime) / (1 seconds)) - ); - } - - function test_revert_setTimeUnit_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setTimeUnit(1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: withdraw - //////////////////////////////////////////////////////////////*/ - - function test_state_withdraw() public { - //================ first staker ====================== - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - uint256 timeOfLastUpdate = block.timestamp; - - // check balances/ownership of staked tokens - for (uint256 i = 0; i < _tokenIdsOne.length; i++) { - assertEq(erc721.ownerOf(_tokenIdsOne[i]), address(stakeContract)); - assertEq(stakeContract.stakerAddress(_tokenIdsOne[i]), stakerOne); - } - assertEq(erc721.balanceOf(stakerOne), 2); - assertEq(erc721.balanceOf(address(stakeContract)), _tokenIdsOne.length); - - // check available rewards right after staking - (uint256[] memory _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_amountStaked.length, _tokenIdsOne.length); - assertEq(_availableRewards, 0); - - console.log("==== staked tokens before withdraw ===="); - for (uint256 i = 0; i < _amountStaked.length; i++) { - console.log(_amountStaked[i]); - } - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - uint256[] memory _tokensToWithdraw = new uint256[](1); - _tokensToWithdraw[0] = 1; - - vm.prank(stakerOne); - stakeContract.withdraw(_tokensToWithdraw); - - // check balances/ownership after withdraw - for (uint256 i = 0; i < _tokensToWithdraw.length; i++) { - assertEq(erc721.ownerOf(_tokensToWithdraw[i]), stakerOne); - assertEq(stakeContract.stakerAddress(_tokensToWithdraw[i]), address(0)); - } - assertEq(erc721.balanceOf(stakerOne), 3); - assertEq(erc721.balanceOf(address(stakeContract)), 2); - - // check available rewards after withdraw - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - assertEq(_availableRewards, ((((block.timestamp - timeOfLastUpdate) * 3) * rewardsPerUnitTime) / timeUnit)); - - console.log("==== staked tokens after withdraw ===="); - for (uint256 i = 0; i < _amountStaked.length; i++) { - console.log(_amountStaked[i]); - } - - uint256 timeOfLastUpdateLatest = block.timestamp; - - // check available rewards some time after withdraw - vm.roll(200); - vm.warp(2000); - - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - (((((timeOfLastUpdateLatest - timeOfLastUpdate) * 3)) * rewardsPerUnitTime) / timeUnit) + - (((((block.timestamp - timeOfLastUpdateLatest) * 2)) * rewardsPerUnitTime) / timeUnit) - ); - - // stake again - vm.prank(stakerOne); - stakeContract.stake(_tokensToWithdraw); - - _tokensToWithdraw[0] = 5; - vm.prank(stakerTwo); - stakeContract.stake(_tokensToWithdraw); - // check available rewards after re-staking - (_amountStaked, ) = stakeContract.getStakeInfo(stakerOne); - - console.log("==== staked tokens after re-staking ===="); - for (uint256 i = 0; i < _amountStaked.length; i++) { - console.log(_amountStaked[i]); - } - } - - function test_revert_withdraw_withdrawingZeroTokens() public { - uint256[] memory _tokensToWithdraw; - - vm.expectRevert("Withdrawing 0 tokens"); - stakeContract.withdraw(_tokensToWithdraw); - } - - function test_revert_withdraw_notStaker() public { - // stake tokens - uint256[] memory _tokenIds = new uint256[](2); - _tokenIds[0] = 0; - _tokenIds[1] = 1; - - vm.prank(stakerOne); - stakeContract.stake(_tokenIds); - - // trying to withdraw zero tokens - uint256[] memory _tokensToWithdraw = new uint256[](1); - _tokensToWithdraw[0] = 2; - - vm.prank(stakerOne); - vm.expectRevert("Not staker"); - stakeContract.withdraw(_tokensToWithdraw); - } - - function test_revert_withdraw_withdrawingMoreThanStaked() public { - // stake tokens - uint256[] memory _tokenIds = new uint256[](1); - _tokenIds[0] = 0; - - vm.prank(stakerOne); - stakeContract.stake(_tokenIds); - - // trying to withdraw tokens not staked by caller - uint256[] memory _tokensToWithdraw = new uint256[](2); - _tokensToWithdraw[0] = 0; - _tokensToWithdraw[1] = 1; - - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(_tokensToWithdraw); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_revert_zeroTimeUnit_adminLockTokens() public { - //================ stake tokens - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](1); - uint256[] memory _tokenIdsTwo = new uint256[](1); - _tokenIdsOne[0] = 0; - _tokenIdsTwo[0] = 5; - - // Two different users stake 1 tokens each - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - vm.prank(stakerTwo); - stakeContract.stake(_tokenIdsTwo); - - // set timeUnit to zero - uint256 newTimeUnit = 0; - vm.prank(deployer); - vm.expectRevert("time-unit can't be 0"); - stakeContract.setTimeUnit(newTimeUnit); - - // stakerOne and stakerTwo can withdraw their tokens - // vm.expectRevert(stdError.divisionError); - vm.prank(stakerOne); - stakeContract.withdraw(_tokenIdsOne); - - // vm.expectRevert(stdError.divisionError); - vm.prank(stakerTwo); - stakeContract.withdraw(_tokenIdsTwo); - } - - function test_revert_largeRewardsPerUnitTime_adminRewardsLock() public { - //================ stake tokens - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](1); - uint256[] memory _tokenIdsTwo = new uint256[](1); - - uint256 stakerOneToken = erc721.nextTokenIdToMint(); - erc721.mint(stakerOne, 5); // mint token id 0 to 4 - uint256 stakerTwoToken = erc721.nextTokenIdToMint(); - erc721.mint(stakerTwo, 5); // mint token id 5 to 9 - _tokenIdsOne[0] = stakerOneToken; - _tokenIdsTwo[0] = stakerTwoToken; - - // Two users stake 1 tokens each - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - vm.prank(stakerTwo); - stakeContract.stake(_tokenIdsTwo); - - // set rewardsPerTimeUnit to max value - uint256 rewardsPerTimeUnit = type(uint256).max; - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(rewardsPerTimeUnit); - - vm.warp(1 days); - - // stakerOne and stakerTwo can't withdraw their tokens - // vm.expectRevert(stdError.arithmeticError); - vm.prank(stakerOne); - stakeContract.withdraw(_tokenIdsOne); - - // vm.expectRevert(stdError.arithmeticError); - vm.prank(stakerTwo); - stakeContract.withdraw(_tokenIdsTwo); - - // rewardsPerTimeUnit can't be changed - rewardsPerTimeUnit = 60; - // vm.expectRevert(stdError.arithmeticError); - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(rewardsPerTimeUnit); - } - - function test_Macro_NFTDirectSafeTransferLocksToken() public { - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = 0; - - // stakerOne mistakenly safe-transfers direct to the staking contract - vm.prank(stakerOne); - vm.expectRevert("Direct transfer"); - erc721.safeTransferFrom(stakerOne, address(stakeContract), tokenIds[0]); - - // show that the transferred token was not properly staked - // (uint256[] memory tokensStaked, uint256 rewards) = stakeContract.getStakeInfo(stakerOne); - // assertEq(0, tokensStaked.length); - - // // show that stakerOne cannot recover the token - // vm.expectRevert(); - // vm.prank(stakerOne); - // stakeContract.withdraw(tokenIds); - } -} diff --git a/src/test/staking/NFTStake_EthReward.t.sol b/src/test/staking/NFTStake_EthReward.t.sol deleted file mode 100644 index 459f111e5..000000000 --- a/src/test/staking/NFTStake_EthReward.t.sol +++ /dev/null @@ -1,592 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { NFTStake } from "contracts/prebuilts/staking/NFTStake.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; - -contract NFTStakeEthRewardTest is BaseTest { - NFTStake internal stakeContract; - - address internal stakerOne; - address internal stakerTwo; - - uint256 internal timeUnit; - uint256 internal rewardsPerUnitTime; - - function setUp() public override { - super.setUp(); - - timeUnit = 60; - rewardsPerUnitTime = 1; - - stakerOne = address(0x345); - stakerTwo = address(0x567); - - erc721.mint(stakerOne, 5); // mint token id 0 to 4 - erc721.mint(stakerTwo, 5); // mint token id 5 to 9 - vm.deal(deployer, 1000 ether); // mint reward tokens (Eth) to contract admin - - stakeContract = NFTStake( - payable( - deployContractProxy( - "NFTStake", - abi.encodeCall( - NFTStake.initialize, - (deployer, CONTRACT_URI, forwarders(), NATIVE_TOKEN, address(erc721), 60, 1) - ) - ) - ) - ); - - // set approvals - vm.prank(stakerOne); - erc721.setApprovalForAll(address(stakeContract), true); - - vm.prank(stakerTwo); - erc721.setApprovalForAll(address(stakeContract), true); - - vm.startPrank(deployer); - stakeContract.depositRewardTokens{ value: 100 ether }(100 ether); - // erc20.transfer(address(stakeContract), 100 ether); - vm.stopPrank(); - assertEq(stakeContract.getRewardTokenBalance(), 100 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: Stake - //////////////////////////////////////////////////////////////*/ - - function test_state_stake() public { - //================ first staker ====================== - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - uint256 timeOfLastUpdate_one = block.timestamp; - - // check balances/ownership of staked tokens - for (uint256 i = 0; i < _tokenIdsOne.length; i++) { - assertEq(erc721.ownerOf(_tokenIdsOne[i]), address(stakeContract)); - assertEq(stakeContract.stakerAddress(_tokenIdsOne[i]), stakerOne); - } - assertEq(erc721.balanceOf(stakerOne), 2); - assertEq(erc721.balanceOf(address(stakeContract)), _tokenIdsOne.length); - - // check available rewards right after staking - (uint256[] memory _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_amountStaked.length, _tokenIdsOne.length); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(100); - vm.warp(1000); - - // check available rewards after warp - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * _tokenIdsOne.length) * rewardsPerUnitTime) / timeUnit) - ); - - //================ second staker ====================== - vm.roll(200); - vm.warp(2000); - uint256[] memory _tokenIdsTwo = new uint256[](2); - _tokenIdsTwo[0] = 5; - _tokenIdsTwo[1] = 6; - - // stake 2 tokens - vm.prank(stakerTwo); - stakeContract.stake(_tokenIdsTwo); - uint256 timeOfLastUpdate_two = block.timestamp; - - // check balances/ownership of staked tokens - for (uint256 i = 0; i < _tokenIdsTwo.length; i++) { - assertEq(erc721.ownerOf(_tokenIdsTwo[i]), address(stakeContract)); - assertEq(stakeContract.stakerAddress(_tokenIdsTwo[i]), stakerTwo); - } - assertEq(erc721.balanceOf(stakerTwo), 3); - assertEq(erc721.balanceOf(address(stakeContract)), _tokenIdsTwo.length + _tokenIdsOne.length); - - // check available rewards right after staking - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq(_amountStaked.length, _tokenIdsTwo.length); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(300); - vm.warp(3000); - - // check available rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_one) * _tokenIdsOne.length) * rewardsPerUnitTime) / timeUnit) - ); - - // check available rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate_two) * _tokenIdsTwo.length) * rewardsPerUnitTime) / timeUnit) - ); - } - - function test_revert_stake_stakingZeroTokens() public { - // stake 0 tokens - uint256[] memory _tokenIds; - - vm.prank(stakerOne); - vm.expectRevert("Staking 0 tokens"); - stakeContract.stake(_tokenIds); - } - - function test_revert_stake_notStaker() public { - // stake unowned tokens - uint256[] memory _tokenIds = new uint256[](1); - _tokenIds[0] = 6; - - vm.prank(stakerOne); - vm.expectRevert("ERC721: transfer from incorrect owner"); - stakeContract.stake(_tokenIds); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: claimRewards - //////////////////////////////////////////////////////////////*/ - - function test_state_claimRewards() public { - //================ first staker ====================== - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - uint256 timeOfLastUpdate_one = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - - uint256 rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerOne); - stakeContract.claimRewards(); - uint256 rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - stakerOne.balance, - ((((block.timestamp - timeOfLastUpdate_one) * _tokenIdsOne.length) * rewardsPerUnitTime) / timeUnit) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - ((((block.timestamp - timeOfLastUpdate_one) * _tokenIdsOne.length) * rewardsPerUnitTime) / timeUnit) - ); - - // check available rewards after claiming - (uint256[] memory _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_amountStaked.length, _tokenIdsOne.length); - assertEq(_availableRewards, 0); - } - - function test_revert_claimRewards_noRewards() public { - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - - //=================== try to claim rewards in same block - - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(); - - //======= withdraw tokens and claim rewards - vm.roll(100); - vm.warp(1000); - - vm.prank(stakerOne); - stakeContract.withdraw(_tokenIdsOne); - vm.prank(stakerOne); - stakeContract.claimRewards(); - - //===== try to claim rewards again - vm.roll(200); - vm.warp(2000); - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - //////////////////////////////////////////////////////////////*/ - - function test_state_setRewardsPerUnitTime() public { - // check current value - assertEq(rewardsPerUnitTime, stakeContract.getRewardsPerUnitTime()); - - // set new value and check - uint256 newRewardsPerUnitTime = 50; - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(newRewardsPerUnitTime); - assertEq(newRewardsPerUnitTime, stakeContract.getRewardsPerUnitTime()); - - //================ stake tokens - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(200); - assertEq(200, stakeContract.getRewardsPerUnitTime()); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * _tokenIdsOne.length) * newRewardsPerUnitTime) / timeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _newRewards, - _availableRewards + ((((block.timestamp - newTimeOfLastUpdate) * _tokenIdsOne.length) * 200) / timeUnit) - ); - } - - function test_revert_setRewardsPerUnitTime_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setRewardsPerUnitTime(1); - } - - function test_state_setTimeUnit() public { - // check current value - assertEq(timeUnit, stakeContract.getTimeUnit()); - - // set new value and check - uint256 newTimeUnit = 2 minutes; - vm.prank(deployer); - stakeContract.setTimeUnit(newTimeUnit); - assertEq(newTimeUnit, stakeContract.getTimeUnit()); - - //================ stake tokens - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setTimeUnit(1 seconds); - assertEq(1 seconds, stakeContract.getTimeUnit()); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((block.timestamp - timeOfLastUpdate) * _tokenIdsOne.length) * rewardsPerUnitTime) / newTimeUnit) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _newRewards, - _availableRewards + - ((((block.timestamp - newTimeOfLastUpdate) * _tokenIdsOne.length) * rewardsPerUnitTime) / (1 seconds)) - ); - } - - function test_revert_setTimeUnit_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setTimeUnit(1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: withdraw - //////////////////////////////////////////////////////////////*/ - - function test_state_withdraw() public { - //================ first staker ====================== - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](3); - _tokenIdsOne[0] = 0; - _tokenIdsOne[1] = 1; - _tokenIdsOne[2] = 2; - - // stake 3 tokens - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - uint256 timeOfLastUpdate = block.timestamp; - - // check balances/ownership of staked tokens - for (uint256 i = 0; i < _tokenIdsOne.length; i++) { - assertEq(erc721.ownerOf(_tokenIdsOne[i]), address(stakeContract)); - assertEq(stakeContract.stakerAddress(_tokenIdsOne[i]), stakerOne); - } - assertEq(erc721.balanceOf(stakerOne), 2); - assertEq(erc721.balanceOf(address(stakeContract)), _tokenIdsOne.length); - - // check available rewards right after staking - (uint256[] memory _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_amountStaked.length, _tokenIdsOne.length); - assertEq(_availableRewards, 0); - - console.log("==== staked tokens before withdraw ===="); - for (uint256 i = 0; i < _amountStaked.length; i++) { - console.log(_amountStaked[i]); - } - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - uint256[] memory _tokensToWithdraw = new uint256[](1); - _tokensToWithdraw[0] = 1; - - vm.prank(stakerOne); - stakeContract.withdraw(_tokensToWithdraw); - - // check balances/ownership after withdraw - for (uint256 i = 0; i < _tokensToWithdraw.length; i++) { - assertEq(erc721.ownerOf(_tokensToWithdraw[i]), stakerOne); - assertEq(stakeContract.stakerAddress(_tokensToWithdraw[i]), address(0)); - } - assertEq(erc721.balanceOf(stakerOne), 3); - assertEq(erc721.balanceOf(address(stakeContract)), 2); - - // check available rewards after withdraw - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - assertEq(_availableRewards, ((((block.timestamp - timeOfLastUpdate) * 3) * rewardsPerUnitTime) / timeUnit)); - - console.log("==== staked tokens after withdraw ===="); - for (uint256 i = 0; i < _amountStaked.length; i++) { - console.log(_amountStaked[i]); - } - - uint256 timeOfLastUpdateLatest = block.timestamp; - - // check available rewards some time after withdraw - vm.roll(200); - vm.warp(2000); - - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - (((((timeOfLastUpdateLatest - timeOfLastUpdate) * 3)) * rewardsPerUnitTime) / timeUnit) + - (((((block.timestamp - timeOfLastUpdateLatest) * 2)) * rewardsPerUnitTime) / timeUnit) - ); - - // stake again - vm.prank(stakerOne); - stakeContract.stake(_tokensToWithdraw); - - _tokensToWithdraw[0] = 5; - vm.prank(stakerTwo); - stakeContract.stake(_tokensToWithdraw); - // check available rewards after re-staking - (_amountStaked, ) = stakeContract.getStakeInfo(stakerOne); - - console.log("==== staked tokens after re-staking ===="); - for (uint256 i = 0; i < _amountStaked.length; i++) { - console.log(_amountStaked[i]); - } - } - - function test_revert_withdraw_withdrawingZeroTokens() public { - uint256[] memory _tokensToWithdraw; - - vm.expectRevert("Withdrawing 0 tokens"); - stakeContract.withdraw(_tokensToWithdraw); - } - - function test_revert_withdraw_notStaker() public { - // stake tokens - uint256[] memory _tokenIds = new uint256[](2); - _tokenIds[0] = 0; - _tokenIds[1] = 1; - - vm.prank(stakerOne); - stakeContract.stake(_tokenIds); - - // trying to withdraw zero tokens - uint256[] memory _tokensToWithdraw = new uint256[](1); - _tokensToWithdraw[0] = 2; - - vm.prank(stakerOne); - vm.expectRevert("Not staker"); - stakeContract.withdraw(_tokensToWithdraw); - } - - function test_revert_withdraw_withdrawingMoreThanStaked() public { - // stake tokens - uint256[] memory _tokenIds = new uint256[](1); - _tokenIds[0] = 0; - - vm.prank(stakerOne); - stakeContract.stake(_tokenIds); - - // trying to withdraw tokens not staked by caller - uint256[] memory _tokensToWithdraw = new uint256[](2); - _tokensToWithdraw[0] = 0; - _tokensToWithdraw[1] = 1; - - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(_tokensToWithdraw); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_revert_zeroTimeUnit_adminLockTokens() public { - //================ stake tokens - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](1); - uint256[] memory _tokenIdsTwo = new uint256[](1); - _tokenIdsOne[0] = 0; - _tokenIdsTwo[0] = 5; - - // Two different users stake 1 tokens each - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - vm.prank(stakerTwo); - stakeContract.stake(_tokenIdsTwo); - - // set timeUnit to zero - uint256 newTimeUnit = 0; - vm.prank(deployer); - vm.expectRevert("time-unit can't be 0"); - stakeContract.setTimeUnit(newTimeUnit); - - // stakerOne and stakerTwo can withdraw their tokens - // vm.expectRevert(stdError.divisionError); - vm.prank(stakerOne); - stakeContract.withdraw(_tokenIdsOne); - - // vm.expectRevert(stdError.divisionError); - vm.prank(stakerTwo); - stakeContract.withdraw(_tokenIdsTwo); - } - - function test_revert_largeRewardsPerUnitTime_adminRewardsLock() public { - //================ stake tokens - vm.warp(1); - uint256[] memory _tokenIdsOne = new uint256[](1); - uint256[] memory _tokenIdsTwo = new uint256[](1); - - uint256 stakerOneToken = erc721.nextTokenIdToMint(); - erc721.mint(stakerOne, 5); // mint token id 0 to 4 - uint256 stakerTwoToken = erc721.nextTokenIdToMint(); - erc721.mint(stakerTwo, 5); // mint token id 5 to 9 - _tokenIdsOne[0] = stakerOneToken; - _tokenIdsTwo[0] = stakerTwoToken; - - // Two users stake 1 tokens each - vm.prank(stakerOne); - stakeContract.stake(_tokenIdsOne); - vm.prank(stakerTwo); - stakeContract.stake(_tokenIdsTwo); - - // set rewardsPerTimeUnit to max value - uint256 rewardsPerTimeUnit = type(uint256).max; - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(rewardsPerTimeUnit); - - vm.warp(1 days); - - // stakerOne and stakerTwo can't withdraw their tokens - // vm.expectRevert(stdError.arithmeticError); - vm.prank(stakerOne); - stakeContract.withdraw(_tokenIdsOne); - - // vm.expectRevert(stdError.arithmeticError); - vm.prank(stakerTwo); - stakeContract.withdraw(_tokenIdsTwo); - - // rewardsPerTimeUnit can't be changed - rewardsPerTimeUnit = 60; - // vm.expectRevert(stdError.arithmeticError); - vm.prank(deployer); - stakeContract.setRewardsPerUnitTime(rewardsPerTimeUnit); - } - - function test_Macro_NFTDirectSafeTransferLocksToken() public { - uint256[] memory tokenIds = new uint256[](1); - tokenIds[0] = 0; - - // stakerOne mistakenly safe-transfers direct to the staking contract - vm.prank(stakerOne); - vm.expectRevert("Direct transfer"); - erc721.safeTransferFrom(stakerOne, address(stakeContract), tokenIds[0]); - - // show that the transferred token was not properly staked - // (uint256[] memory tokensStaked, uint256 rewards) = stakeContract.getStakeInfo(stakerOne); - // assertEq(0, tokensStaked.length); - - // // show that stakerOne cannot recover the token - // vm.expectRevert(); - // vm.prank(stakerOne); - // stakeContract.withdraw(tokenIds); - } -} diff --git a/src/test/staking/TokenStake.t.sol b/src/test/staking/TokenStake.t.sol deleted file mode 100644 index f2a7d63ae..000000000 --- a/src/test/staking/TokenStake.t.sol +++ /dev/null @@ -1,876 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { TokenStake } from "contracts/prebuilts/staking/TokenStake.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; - -contract TokenStakeTest is BaseTest { - TokenStake internal stakeContract; - - address internal stakerOne; - address internal stakerTwo; - - uint80 internal timeUnit; - uint256 internal rewardRatioNumerator; - uint256 internal rewardRatioDenominator; - - function setUp() public override { - super.setUp(); - - timeUnit = 60; - rewardRatioNumerator = 3; - rewardRatioDenominator = 50; - - stakerOne = address(0x345); - stakerTwo = address(0x567); - - erc20Aux.mint(stakerOne, 1000); // mint 1000 tokens to stakerOne - erc20Aux.mint(stakerTwo, 1000); // mint 1000 tokens to stakerTwo - - erc20.mint(deployer, 1000 ether); // mint reward tokens to contract admin - - stakeContract = TokenStake(payable(getContract("TokenStake"))); - - // set approvals - vm.prank(stakerOne); - erc20Aux.approve(address(stakeContract), type(uint256).max); - - vm.prank(stakerTwo); - erc20Aux.approve(address(stakeContract), type(uint256).max); - - vm.startPrank(deployer); - erc20.approve(address(stakeContract), type(uint256).max); - stakeContract.depositRewardTokens(100 ether); - // erc20.transfer(address(stakeContract), 100 ether); - vm.stopPrank(); - assertEq(stakeContract.getRewardTokenBalance(), 100 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: Stake - //////////////////////////////////////////////////////////////*/ - - function test_state_stake() public { - //================ first staker ====================== - vm.warp(1); - - // stake 400 tokens - vm.prank(stakerOne); - stakeContract.stake(400); - uint256 timeOfLastUpdate_one = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc20Aux.balanceOf(address(stakeContract)), 400); - assertEq(erc20Aux.balanceOf(address(stakerOne)), 600); - - // check available rewards right after staking - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_amountStaked, 400); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(100); - vm.warp(1000); - - // check available rewards after warp - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate_one) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - //================ second staker ====================== - vm.roll(200); - vm.warp(2000); - - // stake 20 tokens with token-id 0 - vm.prank(stakerTwo); - stakeContract.stake(200); - uint256 timeOfLastUpdate_two = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc20Aux.balanceOf(address(stakeContract)), 200 + 400); // sum of staked tokens by both stakers - assertEq(erc20Aux.balanceOf(address(stakerTwo)), 800); - - // check available rewards right after staking - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq(_amountStaked, 200); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(300); - vm.warp(3000); - - // check available rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate_one) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check available rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate_two) * 200) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - } - - function test_revert_stake_stakingZeroTokens() public { - // stake 0 tokens - - vm.prank(stakerOne); - vm.expectRevert("Staking 0 tokens"); - stakeContract.stake(0); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: claimRewards - //////////////////////////////////////////////////////////////*/ - - function test_state_claimRewards() public { - //================ setup stakerOne ====================== - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake(400); - uint256 timeOfLastUpdate_one = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - - uint256 rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerOne); - stakeContract.claimRewards(); - uint256 rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - erc20.balanceOf(stakerOne), - (((((block.timestamp - timeOfLastUpdate_one) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - (((((block.timestamp - timeOfLastUpdate_one) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check available rewards after claiming - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_amountStaked, 400); - assertEq(_availableRewards, 0); - - //================ setup stakerTwo ====================== - - // stake 20 tokens with token-id 1 - vm.prank(stakerTwo); - stakeContract.stake(200); - uint256 timeOfLastUpdate_two = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(200); - vm.warp(2000); - - rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerTwo); - stakeContract.claimRewards(); - rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - erc20.balanceOf(stakerTwo), - (((((block.timestamp - timeOfLastUpdate_two) * 200) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - (((((block.timestamp - timeOfLastUpdate_two) * 200) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check available rewards after claiming -- stakerTwo - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - assertEq(_amountStaked, 200); - assertEq(_availableRewards, 0); - - // check available rewards -- stakerOne - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - assertEq(_amountStaked, 400); - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate_two) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - } - - function test_revert_claimRewards_noRewards() public { - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(400); - - //=================== try to claim rewards in same block - - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(); - - //======= withdraw tokens and claim rewards - vm.roll(100); - vm.warp(1000); - - vm.prank(stakerOne); - stakeContract.withdraw(400); - vm.prank(stakerOne); - stakeContract.claimRewards(); - - //===== try to claim rewards again - vm.roll(200); - vm.warp(2000); - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - //////////////////////////////////////////////////////////////*/ - - function test_state_setRewardRatio() public { - // set value and check - vm.prank(deployer); - stakeContract.setRewardRatio(3, 70); - (uint256 numerator, uint256 denominator) = stakeContract.getRewardRatio(); - assertEq(3, numerator); - assertEq(70, denominator); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(400); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setRewardRatio(3, 80); - (numerator, denominator) = stakeContract.getRewardRatio(); - assertEq(3, numerator); - assertEq(80, denominator); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_availableRewards, (((((block.timestamp - timeOfLastUpdate) * 400) * 3) / timeUnit) / 70)); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _newRewards, - _availableRewards + (((((block.timestamp - newTimeOfLastUpdate) * 400) * 3) / timeUnit) / 80) - ); - } - - function test_state_setTimeUnit() public { - // set value and check - uint80 timeUnitToSet = 100; - vm.prank(deployer); - stakeContract.setTimeUnit(timeUnitToSet); - assertEq(timeUnitToSet, stakeContract.getTimeUnit()); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(400); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set timeUnit - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setTimeUnit(200); - assertEq(200, stakeContract.getTimeUnit()); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for timeUnit for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 400) * rewardRatioNumerator) / timeUnitToSet) / - rewardRatioDenominator) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _newRewards, - _availableRewards + - (((((block.timestamp - newTimeOfLastUpdate) * 400) * rewardRatioNumerator) / 200) / - rewardRatioDenominator) - ); - } - - function test_revert_setRewardRatio_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setRewardRatio(1, 2); - } - - function test_revert_setRewardRatio_divideByZero() public { - vm.prank(deployer); - vm.expectRevert("divide by 0"); - stakeContract.setRewardRatio(1, 0); - } - - function test_revert_setTimeUnit_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setTimeUnit(1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: withdraw - //////////////////////////////////////////////////////////////*/ - - function test_state_withdraw() public { - //================ stake different tokens ====================== - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(400); - - vm.prank(stakerTwo); - stakeContract.stake(200); - - uint256 timeOfLastUpdate = block.timestamp; - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - // withdraw partially for stakerOne - vm.prank(stakerOne); - stakeContract.withdraw(100); - uint256 timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(erc20Aux.balanceOf(stakerOne), 700); - assertEq(erc20Aux.balanceOf(stakerTwo), 800); - assertEq(erc20Aux.balanceOf(address(stakeContract)), (400 - 100) + 200); - - // check available rewards after withdraw - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 200) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check available rewards some time after withdraw - vm.roll(200); - vm.warp(2000); - - // check rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((((timeOfLastUpdateLatest - timeOfLastUpdate) * 400)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) + - ((((((block.timestamp - timeOfLastUpdateLatest) * 300)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // withdraw partially for stakerTwo - vm.prank(stakerTwo); - stakeContract.withdraw(100); - timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(erc20Aux.balanceOf(stakerOne), 700); - assertEq(erc20Aux.balanceOf(stakerTwo), 900); - assertEq(erc20Aux.balanceOf(address(stakeContract)), (400 - 100) + (200 - 100)); - - // check rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq( - _availableRewards, - ((((((block.timestamp - timeOfLastUpdate) * 200)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check rewards for stakerTwo after some time - vm.roll(300); - vm.warp(3000); - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq( - _availableRewards, - ((((((timeOfLastUpdateLatest - timeOfLastUpdate) * 200)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) + - ((((((block.timestamp - timeOfLastUpdateLatest) * 100)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - } - - function test_revert_withdraw_withdrawingZeroTokens() public { - vm.expectRevert("Withdrawing 0 tokens"); - stakeContract.withdraw(0); - } - - function test_revert_withdraw_withdrawingMoreThanStaked() public { - // stake tokens - vm.prank(stakerOne); - stakeContract.stake(400); - - vm.prank(stakerTwo); - stakeContract.stake(200); - - // trying to withdraw more than staked - vm.roll(200); - vm.warp(2000); - - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(500); - - // withdraw partially - vm.prank(stakerOne); - stakeContract.withdraw(300); - - // trying to withdraw more than staked - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(500); - - // re-stake - vm.prank(stakerOne); - stakeContract.stake(300); - - // trying to withdraw more than staked - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(500); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_revert_zeroTimeUnit_adminLockTokens() public { - //================ stake tokens - vm.warp(1); - - // User stakes tokens - vm.prank(stakerOne); - stakeContract.stake(400); - - // set timeUnit to zero - uint80 newTimeUnit = 0; - vm.prank(deployer); - vm.expectRevert("time-unit can't be 0"); - stakeContract.setTimeUnit(newTimeUnit); - - // stakerOne and stakerTwo can withdraw their tokens - // vm.expectRevert(stdError.divisionError); - vm.prank(stakerOne); - stakeContract.withdraw(400); - } -} - -contract MockERC20Decimals is MockERC20 { - uint8 private immutable DECIMALS; - - constructor(uint8 _decimals) MockERC20() { - DECIMALS = _decimals; - } - - function decimals() public view virtual override returns (uint8) { - return DECIMALS; - } -} - -// Test scenario where reward token has 6 decimals and staking token has 18 -contract Macro_TokenStake_Rewards6_Staking18_Test is BaseTest { - MockERC20Decimals public erc20_reward6; - MockERC20Decimals public erc20_staking18; - - TokenStake internal stakeContract_reward6_staking18; - - address internal stakerOne; - - uint80 internal timeUnit; - uint256 internal rewardRatioNumerator; - uint256 internal rewardRatioDenominator; - - function setUp() public override { - super.setUp(); - - erc20_reward6 = new MockERC20Decimals(6); - erc20_staking18 = new MockERC20Decimals(18); - - // every 60s earns 1 reward token per 2 tokens staked - timeUnit = 60; - rewardRatioNumerator = 1; - rewardRatioDenominator = 2; - - deployContractProxy( - "TokenStake", - abi.encodeCall( - TokenStake.initialize, - ( - deployer, - CONTRACT_URI, - forwarders(), - address(erc20_reward6), - address(erc20_staking18), - timeUnit, - rewardRatioNumerator, - rewardRatioDenominator - ) - ) - ); - - stakeContract_reward6_staking18 = TokenStake(payable(getContract("TokenStake"))); - - stakerOne = address(0x345); - - // mint 1000 tokens to stakerOne - erc20_staking18.mint(stakerOne, 1000e18); - - // mint 1000 reward tokens to contract admin - erc20_reward6.mint(deployer, 1000e6); - - // set approvals - vm.prank(stakerOne); - erc20_staking18.approve(address(stakeContract_reward6_staking18), type(uint256).max); - - // transfer 100 reward tokens - vm.startPrank(deployer); - erc20_reward6.approve(address(stakeContract_reward6_staking18), type(uint256).max); - // erc20_reward6.transfer(address(stakeContract_reward6_staking18), 100e6); - stakeContract_reward6_staking18.depositRewardTokens(100e6); - vm.stopPrank(); - } - - //===== Reward Token 6 Decimals, Staking Token 18 Decimals =====// - function test_Macro_reward6_staking18() public { - vm.warp(1); - - // stake 400 tokens - vm.prank(stakerOne); - stakeContract_reward6_staking18.stake(400e18); - uint256 timeOfLastUpdate = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc20_staking18.balanceOf(address(stakeContract_reward6_staking18)), 400e18); - assertEq(erc20_staking18.balanceOf(address(stakerOne)), 600e18); - - // check available rewards right after staking - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract_reward6_staking18.getStakeInfo(stakerOne); - - assertEq(_amountStaked, 400e18); - assertEq(_availableRewards, 0); - - //=================== warp ahead exactly 1 timeUnit: 60s - vm.roll(4); - vm.warp(61); - assertEq(timeUnit, block.timestamp - timeOfLastUpdate); - - // With 400 tokens staked, we expect 200 reward tokens earned - (, _availableRewards) = stakeContract_reward6_staking18.getStakeInfo(stakerOne); - console2.log("Expect 200 reward tokens. Amount earned: ", _availableRewards / 1e6); - assertEq(_availableRewards, 200e6); - } -} - -// Test scenario where reward token has 18 decimals and staking token has 6 -contract Macro_TokenStake_Rewards18_Staking6_Test is BaseTest { - MockERC20Decimals public erc20_reward18; - MockERC20Decimals public erc20_staking6; - - TokenStake internal stakeContract_reward18_staking6; - - address internal stakerOne; - - uint80 internal timeUnit; - uint256 internal rewardRatioNumerator; - uint256 internal rewardRatioDenominator; - - function setUp() public override { - super.setUp(); - - erc20_reward18 = new MockERC20Decimals(18); - erc20_staking6 = new MockERC20Decimals(6); - - // every 60s earns 1 reward token per 2 tokens staked - timeUnit = 60; - rewardRatioNumerator = 1; - rewardRatioDenominator = 2; - - deployContractProxy( - "TokenStake", - abi.encodeCall( - TokenStake.initialize, - ( - deployer, - CONTRACT_URI, - forwarders(), - address(erc20_reward18), - address(erc20_staking6), - timeUnit, - rewardRatioNumerator, - rewardRatioDenominator - ) - ) - ); - - stakeContract_reward18_staking6 = TokenStake(payable(getContract("TokenStake"))); - - stakerOne = address(0x345); - - // mint 1000 tokens to stakerOne - erc20_staking6.mint(stakerOne, 1000e6); - - // mint 1000 reward tokens to contract admin - erc20_reward18.mint(deployer, 1000e18); - - // set approvals - vm.prank(stakerOne); - erc20_staking6.approve(address(stakeContract_reward18_staking6), type(uint256).max); - - // transfer 100 reward tokens - vm.startPrank(deployer); - erc20_reward18.approve(address(stakeContract_reward18_staking6), type(uint256).max); - // erc20_reward18.transfer(address(stakeContract_reward18_staking6), 100e18); - stakeContract_reward18_staking6.depositRewardTokens(100e18); - vm.stopPrank(); - } - - //===== Reward Token 18 Decimals, Staking Token 6 Decimals =====// - function test_Macro_reward18_staking6() public { - vm.warp(1); - - // stake 400 tokens - vm.prank(stakerOne); - stakeContract_reward18_staking6.stake(400e6); - uint256 timeOfLastUpdate = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc20_staking6.balanceOf(address(stakeContract_reward18_staking6)), 400e6); - assertEq(erc20_staking6.balanceOf(address(stakerOne)), 600e6); - - // check available rewards right after staking - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract_reward18_staking6.getStakeInfo(stakerOne); - - assertEq(_amountStaked, 400e6); - assertEq(_availableRewards, 0); - - //=================== warp ahead exactly 1 timeUnit: 60s - vm.roll(4); - vm.warp(61); - assertEq(timeUnit, block.timestamp - timeOfLastUpdate); - - // With 400 tokens staked, we expect 200 reward tokens earned - (, _availableRewards) = stakeContract_reward18_staking6.getStakeInfo(stakerOne); - console2.log("Expect 200 reward tokens. Amount earned: ", _availableRewards / 1e18); - assertEq(_availableRewards, 200e18); - } -} - -contract Macro_TokenStakeTest is BaseTest { - TokenStake internal stakeContract; - - uint80 internal timeUnit; - uint256 internal rewardsPerUnitTime; - uint256 internal rewardRatioNumerator; - uint256 internal rewardRatioDenominator; - uint256 internal tokenAmount = 100; - address internal stakerOne = address(0x345); - address internal stakerTwo = address(0x567); - - function setUp() public override { - super.setUp(); - - timeUnit = 60; - rewardRatioNumerator = 3; - rewardRatioDenominator = 50; - // mint 1000 tokens to stakerOne - erc20Aux.mint(stakerOne, tokenAmount); - // mint 1000 tokens to stakerOne - erc20Aux.mint(stakerTwo, tokenAmount); - // mint reward tokens to contract admin - erc20.mint(deployer, 1000 ether); - - stakeContract = TokenStake(payable(getContract("TokenStake"))); - - // set approvals - vm.prank(stakerOne); - erc20Aux.approve(address(stakeContract), type(uint256).max); - vm.prank(stakerTwo); - erc20Aux.approve(address(stakeContract), type(uint256).max); - - vm.startPrank(deployer); - erc20.approve(address(stakeContract), type(uint256).max); - // erc20.transfer(address(stakeContract), 100 ether); - stakeContract.depositRewardTokens(100 ether); - vm.stopPrank(); - } - - // Demostrate setting unitTime to 0 locks the tokens irreversibly - function testToken_adminLockTokens() public { - //================ stake tokens - vm.warp(1); - - // Two users stake 1 tokens each - vm.prank(stakerOne); - stakeContract.stake(tokenAmount); - vm.prank(stakerTwo); - stakeContract.stake(tokenAmount); - - // set timeUnit to zero - uint80 newTimeUnit = 0; - vm.prank(deployer); - vm.expectRevert("time-unit can't be 0"); - stakeContract.setTimeUnit(newTimeUnit); - } - - function testToken_demostrate_adminRewardsLock() public { - //================ stake tokens - vm.warp(1); - // Two users stake 1 tokens each - vm.prank(stakerOne); - stakeContract.stake(tokenAmount); - vm.prank(stakerTwo); - stakeContract.stake(tokenAmount); - - // set timeUnit to a fraction of uint256 maximum value - uint256 newRewardsPerTimeUnit = type(uint256).max / 100; - vm.prank(deployer); - stakeContract.setRewardRatio(newRewardsPerTimeUnit, 1); - - vm.warp(1 days); - - // stakerOne and stakerTwo can't withdraw their tokens - // vm.expectRevert(stdError.arithmeticError); - vm.prank(stakerOne); - stakeContract.withdraw(tokenAmount); - - // vm.expectRevert(stdError.arithmeticError); - vm.prank(stakerTwo); - stakeContract.withdraw(tokenAmount); - - // rewardRatio can't be changed back - newRewardsPerTimeUnit = 60; - // vm.expectRevert(stdError.arithmeticError); - vm.prank(deployer); - stakeContract.setRewardRatio(newRewardsPerTimeUnit, 1); - } -} - -contract Macro_TokenStake_Tax is BaseTest { - TokenStake internal stakeContract; - uint256 internal tokenAmount = 100 ether; - address internal stakerOne = address(0x345); - address internal stakerTwo = address(0x567); - - function setUp() public override { - super.setUp(); - - stakeContract = TokenStake(payable(getContract("TokenStake"))); - - // mint reward tokens to contract admin - erc20.mint(deployer, tokenAmount); - // mint 100 tokens to stakers - erc20Aux.mint(stakerOne, tokenAmount); - erc20Aux.mint(stakerTwo, tokenAmount); - - // Activate Mock tax - erc20Aux.toggleTax(); - - vm.prank(stakerOne); - erc20Aux.approve(address(stakeContract), type(uint256).max); - - vm.startPrank(deployer); - erc20.approve(address(stakeContract), type(uint256).max); - // erc20.transfer(address(stakeContract), 100 ether); - stakeContract.depositRewardTokens(100 ether); - vm.stopPrank(); - } - - // Demonstrate griefer can drain staked tokens for other users - function testToken_demonstrate_inaccurate_amount() public { - // First user stakes 100 tokens - vm.prank(stakerOne); - stakeContract.stake(tokenAmount); - - // Since there is 10% tax only 90 should be in the contract - uint256 stakingTokenBalance = erc20Aux.balanceOf(address(stakeContract)); - assertEq(stakingTokenBalance, 90 ether); - // Assert the amount was correctly assigned - (uint256 stakingTokenAmount, ) = stakeContract.getStakeInfo(stakerOne); - assertEq(stakingTokenAmount, 90 ether); - - // Users stake and withdraw tokens, draining other users staked balances - // for (uint256 i = 1; i <= 9; i++) { - // address staker = vm.addr(i); - // erc20Aux.mint(staker, tokenAmount); - // vm.startPrank(staker); - // erc20Aux.approve(address(stakeContract), type(uint256).max); - // stakeContract.stake(tokenAmount); - // stakeContract.withdraw(tokenAmount); - // vm.stopPrank(); - // } - - // // Staked amount still remains unchanged for stakerOne - // (stakingTokenAmount, ) = stakeContract.getStakeInfo(stakerOne); - // assertEq(stakingTokenAmount, 100 ether); - - // // However there are no tokens left in the contract - // stakingTokenBalance = erc20Aux.balanceOf(address(stakeContract)); - // assertEq(stakingTokenBalance, 0 ether); - - // // StakerOne can't withdraw since there is no balance left - // vm.expectRevert("ERC20: transfer amount exceeds balance"); - // vm.prank(stakerOne); - // stakeContract.withdraw(stakingTokenAmount); - } -} diff --git a/src/test/staking/TokenStake_EthReward.t.sol b/src/test/staking/TokenStake_EthReward.t.sol deleted file mode 100644 index 9cef37deb..000000000 --- a/src/test/staking/TokenStake_EthReward.t.sol +++ /dev/null @@ -1,526 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { TokenStake } from "contracts/prebuilts/staking/TokenStake.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; - -contract TokenStakeEthRewardTest is BaseTest { - TokenStake internal stakeContract; - - address internal stakerOne; - address internal stakerTwo; - - uint256 internal timeUnit; - uint256 internal rewardRatioNumerator; - uint256 internal rewardRatioDenominator; - - function setUp() public override { - super.setUp(); - - timeUnit = 60; - rewardRatioNumerator = 3; - rewardRatioDenominator = 50; - - stakerOne = address(0x345); - stakerTwo = address(0x567); - - erc20Aux.mint(stakerOne, 1000); // mint 1000 tokens to stakerOne - erc20Aux.mint(stakerTwo, 1000); // mint 1000 tokens to stakerTwo - - vm.deal(deployer, 1000 ether); // mint reward tokens (Eth) to contract admin - - stakeContract = TokenStake( - payable( - deployContractProxy( - "TokenStake", - abi.encodeCall( - TokenStake.initialize, - (deployer, CONTRACT_URI, forwarders(), NATIVE_TOKEN, address(erc20Aux), 60, 3, 50) - ) - ) - ) - ); - - // set approvals - vm.prank(stakerOne); - erc20Aux.approve(address(stakeContract), type(uint256).max); - - vm.prank(stakerTwo); - erc20Aux.approve(address(stakeContract), type(uint256).max); - - vm.startPrank(deployer); - stakeContract.depositRewardTokens{ value: 100 ether }(100 ether); - // erc20.transfer(address(stakeContract), 100 ether); - vm.stopPrank(); - assertEq(stakeContract.getRewardTokenBalance(), 100 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: Stake - //////////////////////////////////////////////////////////////*/ - - function test_state_stake() public { - //================ first staker ====================== - vm.warp(1); - - // stake 400 tokens - vm.prank(stakerOne); - stakeContract.stake(400); - uint256 timeOfLastUpdate_one = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc20Aux.balanceOf(address(stakeContract)), 400); - assertEq(erc20Aux.balanceOf(address(stakerOne)), 600); - - // check available rewards right after staking - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_amountStaked, 400); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(100); - vm.warp(1000); - - // check available rewards after warp - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate_one) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - //================ second staker ====================== - vm.roll(200); - vm.warp(2000); - - // stake 20 tokens with token-id 0 - vm.prank(stakerTwo); - stakeContract.stake(200); - uint256 timeOfLastUpdate_two = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(erc20Aux.balanceOf(address(stakeContract)), 200 + 400); // sum of staked tokens by both stakers - assertEq(erc20Aux.balanceOf(address(stakerTwo)), 800); - - // check available rewards right after staking - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq(_amountStaked, 200); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(300); - vm.warp(3000); - - // check available rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate_one) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check available rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate_two) * 200) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - } - - function test_revert_stake_stakingZeroTokens() public { - // stake 0 tokens - - vm.prank(stakerOne); - vm.expectRevert("Staking 0 tokens"); - stakeContract.stake(0); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: claimRewards - //////////////////////////////////////////////////////////////*/ - - function test_state_claimRewards() public { - //================ setup stakerOne ====================== - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake(400); - uint256 timeOfLastUpdate_one = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - - uint256 rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerOne); - stakeContract.claimRewards(); - uint256 rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - stakerOne.balance, - (((((block.timestamp - timeOfLastUpdate_one) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - (((((block.timestamp - timeOfLastUpdate_one) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check available rewards after claiming - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_amountStaked, 400); - assertEq(_availableRewards, 0); - - //================ setup stakerTwo ====================== - - // stake 20 tokens with token-id 1 - vm.prank(stakerTwo); - stakeContract.stake(200); - uint256 timeOfLastUpdate_two = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(200); - vm.warp(2000); - - rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerTwo); - stakeContract.claimRewards(); - rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - stakerTwo.balance, - (((((block.timestamp - timeOfLastUpdate_two) * 200) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - (((((block.timestamp - timeOfLastUpdate_two) * 200) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check available rewards after claiming -- stakerTwo - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - assertEq(_amountStaked, 200); - assertEq(_availableRewards, 0); - - // check available rewards -- stakerOne - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - assertEq(_amountStaked, 400); - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate_two) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - } - - function test_revert_claimRewards_noRewards() public { - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(400); - - //=================== try to claim rewards in same block - - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(); - - //======= withdraw tokens and claim rewards - vm.roll(100); - vm.warp(1000); - - vm.prank(stakerOne); - stakeContract.withdraw(400); - vm.prank(stakerOne); - stakeContract.claimRewards(); - - //===== try to claim rewards again - vm.roll(200); - vm.warp(2000); - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - //////////////////////////////////////////////////////////////*/ - - function test_state_setRewardRatio() public { - // set value and check - vm.prank(deployer); - stakeContract.setRewardRatio(3, 70); - (uint256 numerator, uint256 denominator) = stakeContract.getRewardRatio(); - assertEq(3, numerator); - assertEq(70, denominator); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(400); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setRewardRatio(3, 80); - (numerator, denominator) = stakeContract.getRewardRatio(); - assertEq(3, numerator); - assertEq(80, denominator); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_availableRewards, (((((block.timestamp - timeOfLastUpdate) * 400) * 3) / timeUnit) / 70)); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _newRewards, - _availableRewards + (((((block.timestamp - newTimeOfLastUpdate) * 400) * 3) / timeUnit) / 80) - ); - } - - function test_state_setTimeUnit() public { - // set value and check - uint80 timeUnitToSet = 100; - vm.prank(deployer); - stakeContract.setTimeUnit(timeUnitToSet); - assertEq(timeUnitToSet, stakeContract.getTimeUnit()); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(400); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set timeUnit - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setTimeUnit(200); - assertEq(200, stakeContract.getTimeUnit()); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for timeUnit for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 400) * rewardRatioNumerator) / timeUnitToSet) / - rewardRatioDenominator) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _newRewards, - _availableRewards + - (((((block.timestamp - newTimeOfLastUpdate) * 400) * rewardRatioNumerator) / 200) / - rewardRatioDenominator) - ); - } - - function test_revert_setRewardRatio_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setRewardRatio(1, 2); - } - - function test_revert_setRewardRatio_divideByZero() public { - vm.prank(deployer); - vm.expectRevert("divide by 0"); - stakeContract.setRewardRatio(1, 0); - } - - function test_revert_setTimeUnit_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setTimeUnit(1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: withdraw - //////////////////////////////////////////////////////////////*/ - - function test_state_withdraw() public { - //================ stake different tokens ====================== - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake(400); - - vm.prank(stakerTwo); - stakeContract.stake(200); - - uint256 timeOfLastUpdate = block.timestamp; - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - // withdraw partially for stakerOne - vm.prank(stakerOne); - stakeContract.withdraw(100); - uint256 timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(erc20Aux.balanceOf(stakerOne), 700); - assertEq(erc20Aux.balanceOf(stakerTwo), 800); - assertEq(erc20Aux.balanceOf(address(stakeContract)), (400 - 100) + 200); - - // check available rewards after withdraw - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 200) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check available rewards some time after withdraw - vm.roll(200); - vm.warp(2000); - - // check rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((((timeOfLastUpdateLatest - timeOfLastUpdate) * 400)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) + - ((((((block.timestamp - timeOfLastUpdateLatest) * 300)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // withdraw partially for stakerTwo - vm.prank(stakerTwo); - stakeContract.withdraw(100); - timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(erc20Aux.balanceOf(stakerOne), 700); - assertEq(erc20Aux.balanceOf(stakerTwo), 900); - assertEq(erc20Aux.balanceOf(address(stakeContract)), (400 - 100) + (200 - 100)); - - // check rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq( - _availableRewards, - ((((((block.timestamp - timeOfLastUpdate) * 200)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check rewards for stakerTwo after some time - vm.roll(300); - vm.warp(3000); - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq( - _availableRewards, - ((((((timeOfLastUpdateLatest - timeOfLastUpdate) * 200)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) + - ((((((block.timestamp - timeOfLastUpdateLatest) * 100)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - } - - function test_revert_withdraw_withdrawingZeroTokens() public { - vm.expectRevert("Withdrawing 0 tokens"); - stakeContract.withdraw(0); - } - - function test_revert_withdraw_withdrawingMoreThanStaked() public { - // stake tokens - vm.prank(stakerOne); - stakeContract.stake(400); - - vm.prank(stakerTwo); - stakeContract.stake(200); - - // trying to withdraw more than staked - vm.roll(200); - vm.warp(2000); - - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(500); - - // withdraw partially - vm.prank(stakerOne); - stakeContract.withdraw(300); - - // trying to withdraw more than staked - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(500); - - // re-stake - vm.prank(stakerOne); - stakeContract.stake(300); - - // trying to withdraw more than staked - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(500); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_revert_zeroTimeUnit_adminLockTokens() public { - //================ stake tokens - vm.warp(1); - - // User stakes tokens - vm.prank(stakerOne); - stakeContract.stake(400); - - // set timeUnit to zero - uint80 newTimeUnit = 0; - vm.prank(deployer); - vm.expectRevert("time-unit can't be 0"); - stakeContract.setTimeUnit(newTimeUnit); - - // stakerOne and stakerTwo can withdraw their tokens - // vm.expectRevert(stdError.divisionError); - vm.prank(stakerOne); - stakeContract.withdraw(400); - } -} diff --git a/src/test/staking/TokenStake_EthStake.t.sol b/src/test/staking/TokenStake_EthStake.t.sol deleted file mode 100644 index 8630d08a9..000000000 --- a/src/test/staking/TokenStake_EthStake.t.sol +++ /dev/null @@ -1,520 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { TokenStake } from "contracts/prebuilts/staking/TokenStake.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; - -contract TokenStakeEthStakeTest is BaseTest { - TokenStake internal stakeContract; - - address internal stakerOne; - address internal stakerTwo; - - uint256 internal timeUnit; - uint256 internal rewardRatioNumerator; - uint256 internal rewardRatioDenominator; - - function setUp() public override { - super.setUp(); - - timeUnit = 60; - rewardRatioNumerator = 3; - rewardRatioDenominator = 50; - - stakerOne = address(0x345); - stakerTwo = address(0x567); - - vm.deal(stakerOne, 1000); // mint 1000 tokens to stakerOne - vm.deal(stakerTwo, 1000); // mint 1000 tokens to stakerTwo - - erc20.mint(deployer, 1000 ether); // mint reward tokens to contract admin - - stakeContract = TokenStake( - payable( - deployContractProxy( - "TokenStake", - abi.encodeCall( - TokenStake.initialize, - (deployer, CONTRACT_URI, forwarders(), address(erc20), NATIVE_TOKEN, 60, 3, 50) - ) - ) - ) - ); - - vm.startPrank(deployer); - erc20.approve(address(stakeContract), type(uint256).max); - stakeContract.depositRewardTokens(100 ether); - // erc20.transfer(address(stakeContract), 100 ether); - vm.stopPrank(); - assertEq(stakeContract.getRewardTokenBalance(), 100 ether); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: Stake - //////////////////////////////////////////////////////////////*/ - - function test_state_stake() public { - //================ first staker ====================== - vm.warp(1); - - // stake 400 tokens - vm.prank(stakerOne); - stakeContract.stake{ value: 400 }(400); - uint256 timeOfLastUpdate_one = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(weth.balanceOf(address(stakeContract)), 400); - assertEq(address(stakerOne).balance, 600); - - // check available rewards right after staking - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_amountStaked, 400); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(100); - vm.warp(1000); - - // check available rewards after warp - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate_one) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - //================ second staker ====================== - vm.roll(200); - vm.warp(2000); - - // stake 20 tokens with token-id 0 - vm.prank(stakerTwo); - stakeContract.stake{ value: 200 }(200); - uint256 timeOfLastUpdate_two = block.timestamp; - - // check balances/ownership of staked tokens - assertEq(weth.balanceOf(address(stakeContract)), 200 + 400); // sum of staked tokens by both stakers - assertEq(address(stakerTwo).balance, 800); - - // check available rewards right after staking - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq(_amountStaked, 200); - assertEq(_availableRewards, 0); - - //=================== warp timestamp to calculate rewards - vm.roll(300); - vm.warp(3000); - - // check available rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate_one) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check available rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate_two) * 200) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - } - - function test_revert_stake_stakingZeroTokens() public { - // stake 0 tokens - - vm.prank(stakerOne); - vm.expectRevert("Staking 0 tokens"); - stakeContract.stake(0); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: claimRewards - //////////////////////////////////////////////////////////////*/ - - function test_state_claimRewards() public { - //================ setup stakerOne ====================== - vm.warp(1); - - // stake 50 tokens with token-id 0 - vm.prank(stakerOne); - stakeContract.stake{ value: 400 }(400); - uint256 timeOfLastUpdate_one = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(100); - vm.warp(1000); - - uint256 rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerOne); - stakeContract.claimRewards(); - uint256 rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - erc20.balanceOf(stakerOne), - (((((block.timestamp - timeOfLastUpdate_one) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - (((((block.timestamp - timeOfLastUpdate_one) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check available rewards after claiming - (uint256 _amountStaked, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_amountStaked, 400); - assertEq(_availableRewards, 0); - - //================ setup stakerTwo ====================== - - // stake 20 tokens with token-id 1 - vm.prank(stakerTwo); - stakeContract.stake{ value: 200 }(200); - uint256 timeOfLastUpdate_two = block.timestamp; - - //=================== warp timestamp to claim rewards - vm.roll(200); - vm.warp(2000); - - rewardBalanceBefore = stakeContract.getRewardTokenBalance(); - vm.prank(stakerTwo); - stakeContract.claimRewards(); - rewardBalanceAfter = stakeContract.getRewardTokenBalance(); - - // check reward balances - assertEq( - erc20.balanceOf(stakerTwo), - (((((block.timestamp - timeOfLastUpdate_two) * 200) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - assertEq( - rewardBalanceAfter, - rewardBalanceBefore - - (((((block.timestamp - timeOfLastUpdate_two) * 200) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check available rewards after claiming -- stakerTwo - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - assertEq(_amountStaked, 200); - assertEq(_availableRewards, 0); - - // check available rewards -- stakerOne - (_amountStaked, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - assertEq(_amountStaked, 400); - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate_two) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - } - - function test_revert_claimRewards_noRewards() public { - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake{ value: 400 }(400); - - //=================== try to claim rewards in same block - - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(); - - //======= withdraw tokens and claim rewards - vm.roll(100); - vm.warp(1000); - - vm.prank(stakerOne); - stakeContract.withdraw(400); - vm.prank(stakerOne); - stakeContract.claimRewards(); - - //===== try to claim rewards again - vm.roll(200); - vm.warp(2000); - vm.prank(stakerOne); - vm.expectRevert("No rewards"); - stakeContract.claimRewards(); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: stake conditions - //////////////////////////////////////////////////////////////*/ - - function test_state_setRewardRatio() public { - // set value and check - vm.prank(deployer); - stakeContract.setRewardRatio(3, 70); - (uint256 numerator, uint256 denominator) = stakeContract.getRewardRatio(); - assertEq(3, numerator); - assertEq(70, denominator); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake{ value: 400 }(400); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set rewardsPerUnitTime - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setRewardRatio(3, 80); - (numerator, denominator) = stakeContract.getRewardRatio(); - assertEq(3, numerator); - assertEq(80, denominator); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for rewardsPerUnitTime for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq(_availableRewards, (((((block.timestamp - timeOfLastUpdate) * 400) * 3) / timeUnit) / 70)); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _newRewards, - _availableRewards + (((((block.timestamp - newTimeOfLastUpdate) * 400) * 3) / timeUnit) / 80) - ); - } - - function test_state_setTimeUnit() public { - // set value and check - uint80 timeUnitToSet = 100; - vm.prank(deployer); - stakeContract.setTimeUnit(timeUnitToSet); - assertEq(timeUnitToSet, stakeContract.getTimeUnit()); - - //================ stake tokens - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake{ value: 400 }(400); - uint256 timeOfLastUpdate = block.timestamp; - - //=================== warp timestamp and again set timeUnit - vm.roll(100); - vm.warp(1000); - - vm.prank(deployer); - stakeContract.setTimeUnit(200); - assertEq(200, stakeContract.getTimeUnit()); - uint256 newTimeOfLastUpdate = block.timestamp; - - // check available rewards -- should use previous value for timeUnit for calculation - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 400) * rewardRatioNumerator) / timeUnitToSet) / - rewardRatioDenominator) - ); - - //====== check rewards after some time - vm.roll(300); - vm.warp(3000); - - (, uint256 _newRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _newRewards, - _availableRewards + - (((((block.timestamp - newTimeOfLastUpdate) * 400) * rewardRatioNumerator) / 200) / - rewardRatioDenominator) - ); - } - - function test_revert_setRewardRatio_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setRewardRatio(1, 2); - } - - function test_revert_setRewardRatio_divideByZero() public { - vm.prank(deployer); - vm.expectRevert("divide by 0"); - stakeContract.setRewardRatio(1, 0); - } - - function test_revert_setTimeUnit_notAuthorized() public { - vm.expectRevert("Not authorized"); - stakeContract.setTimeUnit(1); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: withdraw - //////////////////////////////////////////////////////////////*/ - - function test_state_withdraw() public { - //================ stake different tokens ====================== - vm.warp(1); - - vm.prank(stakerOne); - stakeContract.stake{ value: 400 }(400); - - vm.prank(stakerTwo); - stakeContract.stake{ value: 200 }(200); - - uint256 timeOfLastUpdate = block.timestamp; - - //========== warp timestamp before withdraw - vm.roll(100); - vm.warp(1000); - - // withdraw partially for stakerOne - vm.prank(stakerOne); - stakeContract.withdraw(100); - uint256 timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(stakerOne.balance, 700); - assertEq(stakerTwo.balance, 800); - assertEq(weth.balanceOf(address(stakeContract)), (400 - 100) + 200); - - // check available rewards after withdraw - (, uint256 _availableRewards) = stakeContract.getStakeInfo(stakerOne); - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 400) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - assertEq( - _availableRewards, - (((((block.timestamp - timeOfLastUpdate) * 200) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check available rewards some time after withdraw - vm.roll(200); - vm.warp(2000); - - // check rewards for stakerOne - (, _availableRewards) = stakeContract.getStakeInfo(stakerOne); - - assertEq( - _availableRewards, - ((((((timeOfLastUpdateLatest - timeOfLastUpdate) * 400)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) + - ((((((block.timestamp - timeOfLastUpdateLatest) * 300)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // withdraw partially for stakerTwo - vm.prank(stakerTwo); - stakeContract.withdraw(100); - timeOfLastUpdateLatest = block.timestamp; - - // check balances/ownership after withdraw - assertEq(stakerOne.balance, 700); - assertEq(stakerTwo.balance, 900); - assertEq(weth.balanceOf(address(stakeContract)), (400 - 100) + (200 - 100)); - - // check rewards for stakerTwo - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq( - _availableRewards, - ((((((block.timestamp - timeOfLastUpdate) * 200)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - - // check rewards for stakerTwo after some time - vm.roll(300); - vm.warp(3000); - (, _availableRewards) = stakeContract.getStakeInfo(stakerTwo); - - assertEq( - _availableRewards, - ((((((timeOfLastUpdateLatest - timeOfLastUpdate) * 200)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) + - ((((((block.timestamp - timeOfLastUpdateLatest) * 100)) * rewardRatioNumerator) / timeUnit) / - rewardRatioDenominator) - ); - } - - function test_revert_withdraw_withdrawingZeroTokens() public { - vm.expectRevert("Withdrawing 0 tokens"); - stakeContract.withdraw(0); - } - - function test_revert_withdraw_withdrawingMoreThanStaked() public { - // stake tokens - vm.prank(stakerOne); - stakeContract.stake{ value: 400 }(400); - - vm.prank(stakerTwo); - stakeContract.stake{ value: 200 }(200); - - // trying to withdraw more than staked - vm.roll(200); - vm.warp(2000); - - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(500); - - // withdraw partially - vm.prank(stakerOne); - stakeContract.withdraw(300); - - // trying to withdraw more than staked - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(500); - - // re-stake - vm.prank(stakerOne); - stakeContract.stake{ value: 300 }(300); - - // trying to withdraw more than staked - vm.prank(stakerOne); - vm.expectRevert("Withdrawing more than staked"); - stakeContract.withdraw(500); - } - - /*/////////////////////////////////////////////////////////////// - Miscellaneous - //////////////////////////////////////////////////////////////*/ - - function test_revert_zeroTimeUnit_adminLockTokens() public { - //================ stake tokens - vm.warp(1); - - // User stakes tokens - vm.prank(stakerOne); - stakeContract.stake{ value: 400 }(400); - - // set timeUnit to zero - uint80 newTimeUnit = 0; - vm.prank(deployer); - vm.expectRevert("time-unit can't be 0"); - stakeContract.setTimeUnit(newTimeUnit); - - // stakerOne and stakerTwo can withdraw their tokens - // vm.expectRevert(stdError.divisionError); - vm.prank(stakerOne); - stakeContract.withdraw(400); - } -} diff --git a/src/test/token/TokenERC1155.t.sol b/src/test/token/TokenERC1155.t.sol deleted file mode 100644 index 516ada04c..000000000 --- a/src/test/token/TokenERC1155.t.sol +++ /dev/null @@ -1,951 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { TokenERC1155, IPlatformFee, NFTMetadata } from "contracts/prebuilts/token/TokenERC1155.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract TokenERC1155Test is BaseTest { - using Strings for uint256; - - event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri, uint256 quantityMinted); - event TokensMintedWithSignature( - address indexed signer, - address indexed mintedTo, - uint256 indexed tokenIdMinted, - TokenERC1155.MintRequest mintRequest - ); - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); - event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps); - event PrimarySaleRecipientUpdated(address indexed recipient); - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - - TokenERC1155 public tokenContract; - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - TokenERC1155.MintRequest _mintrequest; - bytes _signature; - - address internal deployerSigner; - address internal recipient; - - address private defaultFeeRecipient; - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - deployerSigner = signer; - recipient = address(0x123); - tokenContract = TokenERC1155(getContract("TokenERC1155")); - defaultFeeRecipient = tokenContract.DEFAULT_FEE_RECIPIENT(); - - erc20.mint(deployerSigner, 1_000); - vm.deal(deployerSigner, 1_000); - - erc20.mint(recipient, 1_000); - vm.deal(recipient, 1_000); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,uint256 tokenId,string uri,uint256 quantity,uint256 pricePerToken,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("TokenERC1155")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) - ); - - // construct default mintrequest - _mintrequest.to = recipient; - _mintrequest.royaltyRecipient = royaltyRecipient; - _mintrequest.royaltyBps = royaltyBps; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.tokenId = type(uint256).max; - _mintrequest.uri = "ipfs://"; - _mintrequest.quantity = 100; - _mintrequest.pricePerToken = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - TokenERC1155.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = bytes.concat( - abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - _request.tokenId, - keccak256(bytes(_request.uri)) - ), - abi.encode( - _request.quantity, - _request.pricePerToken, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ) - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - function test_compareEncoding() public { - bytes memory encodedRequestOne = abi.encode( - typehashMintRequest, - _mintrequest.to, - _mintrequest.royaltyRecipient, - _mintrequest.royaltyBps, - _mintrequest.primarySaleRecipient, - keccak256(bytes(_mintrequest.uri)), - _mintrequest.quantity, - _mintrequest.pricePerToken, - _mintrequest.currency, - _mintrequest.validityStartTimestamp, - _mintrequest.validityEndTimestamp, - _mintrequest.uid - ); - bytes memory encodedRequestTwo = bytes.concat( - abi.encode( - typehashMintRequest, - _mintrequest.to, - _mintrequest.royaltyRecipient, - _mintrequest.royaltyBps, - _mintrequest.primarySaleRecipient, - keccak256(bytes(_mintrequest.uri)) - ), - abi.encode( - _mintrequest.quantity, - _mintrequest.pricePerToken, - _mintrequest.currency, - _mintrequest.validityStartTimestamp, - _mintrequest.validityEndTimestamp, - _mintrequest.uid - ) - ); - bytes32 structHashOne = keccak256(encodedRequestOne); - bytes32 typedDataHashOne = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHashOne)); - - bytes32 structHashTwo = keccak256(encodedRequestTwo); - bytes32 typedDataHashTwo = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHashTwo)); - - assertEq(structHashOne, structHashTwo); - assertEq(typedDataHashOne, typedDataHashTwo); - console.logBytes32(structHashOne); - console.logBytes32(structHashTwo); - console.logBytes32(typedDataHashOne); - console.logBytes32(typedDataHashTwo); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintWithSignature` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintWithSignature_NewTokenId() public { - vm.warp(1000); - - // initial balances and state - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId); - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after minting - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.uri(nextTokenId), string(_mintrequest.uri)); - assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient + _mintrequest.quantity); - } - - function test_state_mintWithSignature_ExistingTokenId() public { - vm.warp(1000); - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - - // first mint of new tokenId - vm.prank(recipient); - tokenContract.mintWithSignature(_mintrequest, _signature); - - // initial balances and state - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId); - uint256 _uid = 1; - - // update mintrequest - _mintrequest.tokenId = nextTokenId; - _mintrequest.uid = bytes32(_uid); - _signature = signMintRequest(_mintrequest, privateKey); - - // mint existing tokenId - vm.prank(recipient); - tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after minting - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.uri(nextTokenId), string(_mintrequest.uri)); - assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient + _mintrequest.quantity); - } - - function test_revert_mintWithSignature_InvalidTokenId() public { - vm.warp(1000); - - // update mintrequest - _mintrequest.tokenId = 0; - _signature = signMintRequest(_mintrequest, privateKey); - - // mint non-existent tokenId - vm.prank(recipient); - vm.expectRevert("invalid id"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_state_mintWithSignature_NonZeroPrice_ERC20() public { - vm.warp(1000); - - // update mintrequest data - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - // approve erc20 tokens to tokenContract - vm.prank(recipient); - erc20.approve(address(tokenContract), _mintrequest.pricePerToken * _mintrequest.quantity); - - // initial balances and state - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId); - - uint256 erc20BalanceOfSeller = erc20.balanceOf(address(saleRecipient)); - uint256 erc20BalanceOfRecipient = erc20.balanceOf(address(recipient)); - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after minting - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.uri(nextTokenId), string(_mintrequest.uri)); - assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient + _mintrequest.quantity); - - // check erc20 balances after minting - uint256 defaultFee = ((_mintrequest.pricePerToken * _mintrequest.quantity) * 100) / MAX_BPS; - uint256 _platformFees = ((_mintrequest.pricePerToken * _mintrequest.quantity) * platformFeeBps) / MAX_BPS; - assertEq( - erc20.balanceOf(recipient), - erc20BalanceOfRecipient - (_mintrequest.pricePerToken * _mintrequest.quantity) - ); - assertEq( - erc20.balanceOf(address(saleRecipient)), - erc20BalanceOfSeller + (_mintrequest.pricePerToken * _mintrequest.quantity) - _platformFees - defaultFee - ); - assertEq(erc20.balanceOf(address(defaultFeeRecipient)), defaultFee); - } - - function test_state_mintWithSignature_NonZeroPrice_NativeToken() public { - vm.warp(1000); - - // update mintrequest data - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - // initial balances and state - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId); - - uint256 etherBalanceOfSeller = address(saleRecipient).balance; - uint256 etherBalanceOfRecipient = address(recipient).balance; - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature{ value: _mintrequest.pricePerToken * _mintrequest.quantity }( - _mintrequest, - _signature - ); - - // check state after minting - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.uri(nextTokenId), string(_mintrequest.uri)); - assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient + _mintrequest.quantity); - - // check balances after minting - uint256 defaultFee = ((_mintrequest.pricePerToken * _mintrequest.quantity) * 100) / MAX_BPS; - uint256 _platformFees = ((_mintrequest.pricePerToken * _mintrequest.quantity) * platformFeeBps) / MAX_BPS; - assertEq( - address(recipient).balance, - etherBalanceOfRecipient - (_mintrequest.pricePerToken * _mintrequest.quantity) - ); - assertEq( - address(saleRecipient).balance, - etherBalanceOfSeller + (_mintrequest.pricePerToken * _mintrequest.quantity) - _platformFees - defaultFee - ); - assertEq(address(defaultFeeRecipient).balance, defaultFee); - } - - function test_revert_mintWithSignature_MustSendTotalPrice() public { - vm.warp(1000); - - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("must send total price."); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_MsgValueNotZero() public { - vm.warp(1000); - - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - // shouldn't send native-token when it is not the currency - vm.prank(recipient); - vm.expectRevert("msg value not zero"); - tokenContract.mintWithSignature{ value: 1 }(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_InvalidSignature() public { - vm.warp(1000); - - uint256 incorrectKey = 3456; - _signature = signMintRequest(_mintrequest, incorrectKey); - - vm.prank(recipient); - vm.expectRevert("invalid signature"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_RequestExpired() public { - _signature = signMintRequest(_mintrequest, privateKey); - - // warp time more out of range - vm.warp(3000); - - vm.prank(recipient); - vm.expectRevert("request expired"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_RecipientUndefined() public { - vm.warp(1000); - - _mintrequest.to = address(0); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("recipient undefined"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_ZeroQuantity() public { - vm.warp(1000); - - _mintrequest.quantity = 0; - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("zero quantity"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_event_mintWithSignature() public { - vm.warp(1000); - - vm.expectEmit(true, true, true, true); - emit TokensMintedWithSignature(deployerSigner, recipient, 0, _mintrequest); - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintTo` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintTo() public { - string memory _tokenURI = "tokenURI"; - uint256 _amount = 100; - - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId); - - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, type(uint256).max, _tokenURI, _amount); - - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.uri(nextTokenId), _tokenURI); - assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient + _amount); - } - - function test_revert_mintTo_NotAuthorized() public { - string memory _tokenURI = "tokenURI"; - uint256 _amount = 100; - bytes32 role = keccak256("MINTER_ROLE"); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.mintTo(recipient, type(uint256).max, _tokenURI, _amount); - } - - function test_event_mintTo() public { - string memory _tokenURI = "tokenURI"; - uint256 _amount = 100; - - vm.expectEmit(true, true, true, true); - emit TokensMinted(recipient, 0, _tokenURI, _amount); - - // mint - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, type(uint256).max, _tokenURI, _amount); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `burn` - //////////////////////////////////////////////////////////////*/ - - function test_state_burn_TokenOwner() public { - string memory _tokenURI = "tokenURI"; - uint256 _amount = 100; - - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId); - - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, type(uint256).max, _tokenURI, _amount); - - vm.prank(recipient); - tokenContract.burn(recipient, nextTokenId, _amount); - - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.uri(nextTokenId), _tokenURI); - assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient); - } - - function test_state_burn_TokenOperator() public { - string memory _tokenURI = "tokenURI"; - uint256 _amount = 100; - - address operator = address(0x789); - - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId); - - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, type(uint256).max, _tokenURI, _amount); - - vm.prank(recipient); - tokenContract.setApprovalForAll(operator, true); - - vm.prank(operator); - tokenContract.burn(recipient, nextTokenId, _amount); - - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.uri(nextTokenId), _tokenURI); - assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient); - } - - function test_revert_burn_NotOwnerNorApproved() public { - string memory _tokenURI = "tokenURI"; - uint256 _amount = 100; - - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, type(uint256).max, _tokenURI, _amount); - - vm.prank(address(0x789)); - vm.expectRevert("ERC1155: caller is not owner nor approved."); - tokenContract.burn(recipient, nextTokenId, _amount); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: owner - //////////////////////////////////////////////////////////////*/ - - function test_state_setOwner() public { - address newOwner = address(0x123); - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.prank(deployerSigner); - tokenContract.grantRole(role, newOwner); - - vm.prank(deployerSigner); - tokenContract.setOwner(newOwner); - - assertEq(tokenContract.owner(), newOwner); - } - - function test_revert_setOwner_NotModuleAdmin() public { - vm.expectRevert("new owner not module admin."); - vm.prank(deployerSigner); - tokenContract.setOwner(address(0x1234)); - } - - function test_event_setOwner() public { - address newOwner = address(0x123); - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.startPrank(deployerSigner); - tokenContract.grantRole(role, newOwner); - - vm.expectEmit(true, true, true, true); - emit OwnerUpdated(deployerSigner, newOwner); - - tokenContract.setOwner(newOwner); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: royalty - //////////////////////////////////////////////////////////////*/ - - function test_state_setDefaultRoyaltyInfo() public { - address _royaltyRecipient = address(0x123); - uint256 _royaltyBps = 1000; - - vm.prank(deployerSigner); - tokenContract.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - - (address newRoyaltyRecipient, uint256 newRoyaltyBps) = tokenContract.getDefaultRoyaltyInfo(); - assertEq(newRoyaltyRecipient, _royaltyRecipient); - assertEq(newRoyaltyBps, _royaltyBps); - - (address receiver, uint256 royaltyAmount) = tokenContract.royaltyInfo(0, 100); - assertEq(receiver, _royaltyRecipient); - assertEq(royaltyAmount, (100 * 1000) / 10_000); - } - - function test_revert_setDefaultRoyaltyInfo_NotAuthorized() public { - address _royaltyRecipient = address(0x123); - uint256 _royaltyBps = 1000; - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - } - - function test_revert_setDefaultRoyaltyInfo_ExceedsRoyaltyBps() public { - address _royaltyRecipient = address(0x123); - uint256 _royaltyBps = 10001; - - vm.expectRevert("exceed royalty bps"); - vm.prank(deployerSigner); - tokenContract.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - } - - function test_state_setRoyaltyInfoForToken() public { - uint256 _tokenId = 1; - address _recipient = address(0x123); - uint256 _bps = 1000; - - vm.prank(deployerSigner); - tokenContract.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); - - (address receiver, uint256 royaltyAmount) = tokenContract.royaltyInfo(_tokenId, 100); - assertEq(receiver, _recipient); - assertEq(royaltyAmount, (100 * 1000) / 10_000); - } - - function test_revert_setRoyaltyInfo_NotAuthorized() public { - uint256 _tokenId = 1; - address _recipient = address(0x123); - uint256 _bps = 1000; - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); - } - - function test_revert_setRoyaltyInfoForToken_ExceedsRoyaltyBps() public { - uint256 _tokenId = 1; - address _recipient = address(0x123); - uint256 _bps = 10001; - - vm.expectRevert("exceed royalty bps"); - vm.prank(deployerSigner); - tokenContract.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); - } - - function test_event_defaultRoyalty() public { - address _royaltyRecipient = address(0x123); - uint256 _royaltyBps = 1000; - - vm.expectEmit(true, true, true, true); - emit DefaultRoyalty(_royaltyRecipient, _royaltyBps); - - vm.prank(deployerSigner); - tokenContract.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - } - - function test_event_royaltyForToken() public { - uint256 _tokenId = 1; - address _recipient = address(0x123); - uint256 _bps = 1000; - - vm.expectEmit(true, true, true, true); - emit RoyaltyForToken(_tokenId, _recipient, _bps); - - vm.prank(deployerSigner); - tokenContract.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: primary sale - //////////////////////////////////////////////////////////////*/ - - function test_state_setPrimarySaleRecipient() public { - address _primarySaleRecipient = address(0x123); - - vm.prank(deployerSigner); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - - address recipient_ = tokenContract.primarySaleRecipient(); - assertEq(recipient_, _primarySaleRecipient); - } - - function test_revert_setPrimarySaleRecipient_NotAuthorized() public { - address _primarySaleRecipient = address(0x123); - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - } - - function test_event_setPrimarySaleRecipient() public { - address _primarySaleRecipient = address(0x123); - - vm.expectEmit(true, true, true, true); - emit PrimarySaleRecipientUpdated(_primarySaleRecipient); - - vm.prank(deployerSigner); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: platform fee - //////////////////////////////////////////////////////////////*/ - - function test_state_PlatformFee_Flat_ERC20() public { - vm.warp(1000); - uint256 flatPlatformFee = 10; - - vm.startPrank(deployerSigner); - tokenContract.setFlatPlatformFeeInfo(platformFeeRecipient, flatPlatformFee); - tokenContract.setPlatformFeeType(IPlatformFee.PlatformFeeType.Flat); - vm.stopPrank(); - - // update mintrequest data - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - uint256 defaultFee = (_mintrequest.pricePerToken * _mintrequest.quantity * 100) / 10_000; - - // approve erc20 tokens to tokenContract - vm.prank(recipient); - erc20.approve(address(tokenContract), _mintrequest.pricePerToken * _mintrequest.quantity); - - // initial balances and state - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId); - - uint256 erc20BalanceOfSeller = erc20.balanceOf(address(saleRecipient)); - uint256 erc20BalanceOfRecipient = erc20.balanceOf(address(recipient)); - uint256 defaultFeeRecipientBefore = erc20.balanceOf(address(defaultFeeRecipient)); - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after minting - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.uri(nextTokenId), string(_mintrequest.uri)); - assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient + _mintrequest.quantity); - - // check erc20 balances after minting - assertEq( - erc20.balanceOf(recipient), - erc20BalanceOfRecipient - (_mintrequest.pricePerToken * _mintrequest.quantity) - ); - assertEq( - erc20.balanceOf(address(saleRecipient)), - erc20BalanceOfSeller + (_mintrequest.pricePerToken * _mintrequest.quantity) - flatPlatformFee - defaultFee - ); - assertEq(erc20.balanceOf(address(defaultFeeRecipient)), defaultFeeRecipientBefore + defaultFee); - } - - function test_state_PlatformFee_NativeToken() public { - vm.warp(1000); - uint256 flatPlatformFee = 10; - - vm.startPrank(deployerSigner); - tokenContract.setFlatPlatformFeeInfo(platformFeeRecipient, flatPlatformFee); - tokenContract.setPlatformFeeType(IPlatformFee.PlatformFeeType.Flat); - vm.stopPrank(); - - // update mintrequest data - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - // initial balances and state - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient, nextTokenId); - - uint256 etherBalanceOfSeller = address(saleRecipient).balance; - uint256 etherBalanceOfRecipient = address(recipient).balance; - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature{ value: _mintrequest.pricePerToken * _mintrequest.quantity }( - _mintrequest, - _signature - ); - - uint256 defaultFee = (_mintrequest.pricePerToken * _mintrequest.quantity * 100) / 10_000; - - // check state after minting - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.uri(nextTokenId), string(_mintrequest.uri)); - assertEq(tokenContract.balanceOf(recipient, nextTokenId), currentBalanceOfRecipient + _mintrequest.quantity); - - // check balances after minting - assertEq( - address(recipient).balance, - etherBalanceOfRecipient - (_mintrequest.pricePerToken * _mintrequest.quantity) - ); - assertEq( - address(saleRecipient).balance, - etherBalanceOfSeller + (_mintrequest.pricePerToken * _mintrequest.quantity) - flatPlatformFee - defaultFee - ); - assertEq(address(defaultFeeRecipient).balance, defaultFee); - } - - function test_revert_PlatformFeeGreaterThanPrice() public { - vm.warp(1000); - uint256 flatPlatformFee = 1 ether; - - vm.startPrank(deployerSigner); - tokenContract.setFlatPlatformFeeInfo(platformFeeRecipient, flatPlatformFee); - tokenContract.setPlatformFeeType(IPlatformFee.PlatformFeeType.Flat); - vm.stopPrank(); - - // update mintrequest data - _mintrequest.pricePerToken = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - // mint with signature - vm.prank(recipient); - vm.expectRevert("price less than platform fee"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_state_setPlatformFeeInfo() public { - address _platformFeeRecipient = address(0x123); - uint256 _platformFeeBps = 1000; - - vm.prank(deployerSigner); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - - (address recipient_, uint16 bps) = tokenContract.getPlatformFeeInfo(); - assertEq(_platformFeeRecipient, recipient_); - assertEq(_platformFeeBps, bps); - } - - function test_state_setFlatPlatformFee() public { - address _platformFeeRecipient = address(0x123); - uint256 _flatFee = 1000; - - vm.prank(deployerSigner); - tokenContract.setFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee); - - (address recipient_, uint256 fee) = tokenContract.getFlatPlatformFeeInfo(); - assertEq(_platformFeeRecipient, recipient_); - assertEq(_flatFee, fee); - } - - function test_state_setPlatformFeeType() public { - address _platformFeeRecipient = address(0x123); - uint256 _flatFee = 1000; - IPlatformFee.PlatformFeeType _feeType = IPlatformFee.PlatformFeeType.Flat; - - vm.prank(deployerSigner); - tokenContract.setFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee); - - vm.prank(deployerSigner); - tokenContract.setPlatformFeeType(_feeType); - - IPlatformFee.PlatformFeeType updatedFeeType = tokenContract.getPlatformFeeType(); - assertTrue(updatedFeeType == _feeType); - } - - function test_revert_setPlatformFeeInfo_ExceedsMaxBps() public { - address _platformFeeRecipient = address(0x123); - uint256 _platformFeeBps = 10001; - - vm.expectRevert("exceeds MAX_BPS"); - vm.prank(deployerSigner); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - function test_revert_setPlatformFeeInfo_NotAuthorized() public { - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setPlatformFeeInfo(address(1), 1000); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setFlatPlatformFeeInfo(address(1), 1000); - } - - function test_event_platformFeeInfo() public { - address _platformFeeRecipient = address(0x123); - uint256 _platformFeeBps = 1000; - - vm.expectEmit(true, true, true, true); - emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps); - - vm.prank(deployerSigner); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: contract metadata - //////////////////////////////////////////////////////////////*/ - - function test_state_setContractURI() public { - string memory uri = "uri_string"; - - vm.prank(deployerSigner); - tokenContract.setContractURI(uri); - - string memory _contractURI = tokenContract.contractURI(); - - assertEq(_contractURI, uri); - } - - function test_revert_setContractURI() public { - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setContractURI(""); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: setTokenURI - //////////////////////////////////////////////////////////////*/ - - function test_setTokenURI_state() public { - string memory uri = "uri_string"; - - vm.prank(deployerSigner); - tokenContract.setTokenURI(0, uri); - - string memory _tokenURI = tokenContract.uri(0); - - assertEq(_tokenURI, uri); - } - - function test_setTokenURI_revert_NotAuthorized() public { - string memory uri = "uri_string"; - - vm.expectRevert(abi.encodeWithSelector(NFTMetadata.NFTMetadataUnauthorized.selector)); - vm.prank(address(0x1)); - tokenContract.setTokenURI(0, uri); - } - - function test_setTokenURI_revert_Frozen() public { - string memory uri = "uri_string"; - - vm.startPrank(deployerSigner); - tokenContract.freezeMetadata(); - - vm.expectRevert(abi.encodeWithSelector(NFTMetadata.NFTMetadataFrozen.selector, 0)); - tokenContract.setTokenURI(0, uri); - } -} diff --git a/src/test/token/TokenERC20.t.sol b/src/test/token/TokenERC20.t.sol deleted file mode 100644 index 03e44d81c..000000000 --- a/src/test/token/TokenERC20.t.sol +++ /dev/null @@ -1,424 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { TokenERC20 } from "contracts/prebuilts/token/TokenERC20.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract TokenERC20Test is BaseTest { - using Strings for uint256; - - event TokensMinted(address indexed mintedTo, uint256 quantityMinted); - event TokensMintedWithSignature( - address indexed signer, - address indexed mintedTo, - TokenERC20.MintRequest mintRequest - ); - - event PrimarySaleRecipientUpdated(address indexed recipient); - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - - TokenERC20 public tokenContract; - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - bytes32 internal permitTypehash; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - TokenERC20.MintRequest _mintrequest; - bytes _signature; - - address internal deployerSigner; - address internal recipient; - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - deployerSigner = signer; - recipient = address(0x123); - tokenContract = TokenERC20(getContract("TokenERC20")); - - erc20.mint(deployerSigner, 1_000); - vm.deal(deployerSigner, 1_000); - - erc20.mint(recipient, 1_000); - vm.deal(recipient, 1_000); - - typehashMintRequest = keccak256( - "MintRequest(address to,address primarySaleRecipient,uint256 quantity,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes(NAME)); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) - ); - permitTypehash = keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ); - - // construct default mintrequest - _mintrequest.to = recipient; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.quantity = 100; - _mintrequest.price = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - TokenERC20.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.primarySaleRecipient, - _request.quantity, - _request.price, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintWithSignature` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintWithSignature_ZeroPrice() public { - vm.warp(1000); - - // initial balances and state - uint256 currentTotalSupply = tokenContract.totalSupply(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after minting - assertEq(tokenContract.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - } - - function test_state_mintWithSignature_NonZeroPrice_ERC20() public { - vm.warp(1000); - - // update mintrequest data - _mintrequest.price = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - // approve erc20 tokens to tokenContract - vm.prank(recipient); - erc20.approve(address(tokenContract), _mintrequest.price); - - // initial balances and state - uint256 currentTotalSupply = tokenContract.totalSupply(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); - - uint256 erc20BalanceOfSeller = erc20.balanceOf(address(saleRecipient)); - uint256 erc20BalanceOfRecipient = erc20.balanceOf(address(recipient)); - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after minting - assertEq(tokenContract.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - - // check erc20 balances after minting - uint256 _platformFees = (_mintrequest.price * platformFeeBps) / MAX_BPS; - assertEq(erc20.balanceOf(recipient), erc20BalanceOfRecipient - _mintrequest.price); - assertEq(erc20.balanceOf(address(saleRecipient)), erc20BalanceOfSeller + _mintrequest.price - _platformFees); - } - - function test_state_mintWithSignature_NonZeroPrice_NativeToken() public { - vm.warp(1000); - - // update mintrequest data - _mintrequest.price = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - // initial balances and state - uint256 currentTotalSupply = tokenContract.totalSupply(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); - - uint256 etherBalanceOfSeller = address(saleRecipient).balance; - uint256 etherBalanceOfRecipient = address(recipient).balance; - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature{ value: _mintrequest.price }(_mintrequest, _signature); - - // check state after minting - assertEq(tokenContract.totalSupply(), currentTotalSupply + _mintrequest.quantity); - assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient + _mintrequest.quantity); - - // check balances after minting - uint256 _platformFees = (_mintrequest.price * platformFeeBps) / MAX_BPS; - assertEq(address(recipient).balance, etherBalanceOfRecipient - _mintrequest.price); - assertEq(address(saleRecipient).balance, etherBalanceOfSeller + _mintrequest.price - _platformFees); - } - - function test_revert_mintWithSignature_MustSendTotalPrice() public { - vm.warp(1000); - - _mintrequest.price = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("must send total price."); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_MsgValueNotZero() public { - vm.warp(1000); - - _mintrequest.price = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - // shouldn't send native-token when it is not the currency - vm.prank(recipient); - vm.expectRevert("msg value not zero"); - tokenContract.mintWithSignature{ value: 1 }(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_InvalidSignature() public { - vm.warp(1000); - - uint256 incorrectKey = 3456; - _signature = signMintRequest(_mintrequest, incorrectKey); - - vm.prank(recipient); - vm.expectRevert("invalid signature"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_RequestExpired() public { - _signature = signMintRequest(_mintrequest, privateKey); - - // warp time more out of range - vm.warp(3000); - - vm.prank(recipient); - vm.expectRevert("request expired"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_RecipientUndefined() public { - vm.warp(1000); - - _mintrequest.to = address(0); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("recipient undefined"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_ZeroQuantity() public { - vm.warp(1000); - - _mintrequest.quantity = 0; - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("zero quantity"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_event_mintWithSignature() public { - vm.warp(1000); - - vm.expectEmit(true, true, true, true); - emit TokensMintedWithSignature(deployerSigner, recipient, _mintrequest); - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintTo` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintTo() public { - uint256 _amount = 100; - - uint256 currentTotalSupply = tokenContract.totalSupply(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); - - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, _amount); - - assertEq(tokenContract.totalSupply(), currentTotalSupply + _amount); - assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient + _amount); - } - - function test_revert_mintTo_NotAuthorized() public { - uint256 _amount = 100; - - vm.expectRevert("not minter."); - vm.prank(address(0x1)); - tokenContract.mintTo(recipient, _amount); - } - - function test_event_mintTo() public { - uint256 _amount = 100; - - vm.expectEmit(true, true, true, true); - emit TokensMinted(recipient, _amount); - - // mint - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, _amount); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: primary sale - //////////////////////////////////////////////////////////////*/ - - function test_state_setPrimarySaleRecipient() public { - address _primarySaleRecipient = address(0x123); - - vm.prank(deployerSigner); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - - address recipient_ = tokenContract.primarySaleRecipient(); - assertEq(recipient_, _primarySaleRecipient); - } - - function test_revert_setPrimarySaleRecipient_NotAuthorized() public { - address _primarySaleRecipient = address(0x123); - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - } - - function test_event_setPrimarySaleRecipient() public { - address _primarySaleRecipient = address(0x123); - - vm.expectEmit(true, true, true, true); - emit PrimarySaleRecipientUpdated(_primarySaleRecipient); - - vm.prank(deployerSigner); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: platform fee - //////////////////////////////////////////////////////////////*/ - - function test_state_setPlatformFeeInfo() public { - address _platformFeeRecipient = address(0x123); - uint256 _platformFeeBps = 1000; - - vm.prank(deployerSigner); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - - (address recipient_, uint16 bps) = tokenContract.getPlatformFeeInfo(); - assertEq(_platformFeeRecipient, recipient_); - assertEq(_platformFeeBps, bps); - } - - function test_revert_setPlatformFeeInfo_ExceedsMaxBps() public { - address _platformFeeRecipient = address(0x123); - uint256 _platformFeeBps = 10001; - - vm.expectRevert("exceeds MAX_BPS"); - vm.prank(deployerSigner); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - function test_revert_setPlatformFeeInfo_NotAuthorized() public { - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setPlatformFeeInfo(address(1), 1000); - } - - function test_event_platformFeeInfo() public { - address _platformFeeRecipient = address(0x123); - uint256 _platformFeeBps = 1000; - - vm.expectEmit(true, true, true, true); - emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps); - - vm.prank(deployerSigner); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: contract metadata - //////////////////////////////////////////////////////////////*/ - - function test_state_setContractURI() public { - string memory uri = "uri_string"; - - vm.prank(deployerSigner); - tokenContract.setContractURI(uri); - - string memory _contractURI = tokenContract.contractURI(); - - assertEq(_contractURI, uri); - } - - function test_revert_setContractURI_NotAuthorized() public { - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setContractURI(""); - } -} diff --git a/src/test/token/TokenERC721.t.sol b/src/test/token/TokenERC721.t.sol deleted file mode 100644 index bd9c0519a..000000000 --- a/src/test/token/TokenERC721.t.sol +++ /dev/null @@ -1,692 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import { TokenERC721, NFTMetadata } from "contracts/prebuilts/token/TokenERC721.sol"; - -// Test imports - -import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; - -contract TokenERC721Test is BaseTest { - using Strings for uint256; - - event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri); - event TokensMintedWithSignature( - address indexed signer, - address indexed mintedTo, - uint256 indexed tokenIdMinted, - TokenERC721.MintRequest mintRequest - ); - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); - event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps); - event PrimarySaleRecipientUpdated(address indexed recipient); - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - - TokenERC721 public tokenContract; - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - bytes private emptyEncodedBytes = abi.encode("", ""); - - TokenERC721.MintRequest _mintrequest; - bytes _signature; - - address internal deployerSigner; - address internal recipient; - - using stdStorage for StdStorage; - - function setUp() public override { - super.setUp(); - deployerSigner = signer; - recipient = address(0x123); - tokenContract = TokenERC721(getContract("TokenERC721")); - - erc20.mint(deployerSigner, 1_000); - vm.deal(deployerSigner, 1_000); - - erc20.mint(recipient, 1_000); - vm.deal(recipient, 1_000); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,string uri,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("TokenERC721")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) - ); - - // construct default mintrequest - _mintrequest.to = recipient; - _mintrequest.royaltyRecipient = royaltyRecipient; - _mintrequest.royaltyBps = royaltyBps; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.uri = "ipfs://"; - _mintrequest.price = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 1000; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - _signature = signMintRequest(_mintrequest, privateKey); - } - - function signMintRequest( - TokenERC721.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - keccak256(bytes(_request.uri)), - _request.price, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintWithSignature` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintWithSignature_ZeroPrice() public { - vm.warp(1000); - - // initial balances and state - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentTotalSupply = tokenContract.totalSupply(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after minting - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.tokenURI(nextTokenId), string(_mintrequest.uri)); - assertEq(tokenContract.totalSupply(), currentTotalSupply + 1); - assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient + 1); - assertEq(tokenContract.ownerOf(nextTokenId), recipient); - } - - function test_state_mintWithSignature_NonZeroPrice_ERC20() public { - vm.warp(1000); - - // update mintrequest data - _mintrequest.price = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - // approve erc20 tokens to tokenContract - vm.prank(recipient); - erc20.approve(address(tokenContract), 1); - - // initial balances and state - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentTotalSupply = tokenContract.totalSupply(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); - - uint256 erc20BalanceOfSeller = erc20.balanceOf(address(saleRecipient)); - uint256 erc20BalanceOfRecipient = erc20.balanceOf(address(recipient)); - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after minting - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.tokenURI(nextTokenId), string(_mintrequest.uri)); - assertEq(tokenContract.totalSupply(), currentTotalSupply + 1); - assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient + 1); - assertEq(tokenContract.ownerOf(nextTokenId), recipient); - - // check erc20 balances after minting - uint256 _platformFees = (_mintrequest.price * platformFeeBps) / MAX_BPS; - assertEq(erc20.balanceOf(recipient), erc20BalanceOfRecipient - _mintrequest.price); - assertEq(erc20.balanceOf(address(saleRecipient)), erc20BalanceOfSeller + _mintrequest.price - _platformFees); - } - - function test_state_mintWithSignature_NonZeroPrice_NativeToken() public { - vm.warp(1000); - - // update mintrequest data - _mintrequest.price = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - // initial balances and state - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentTotalSupply = tokenContract.totalSupply(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); - - uint256 etherBalanceOfSeller = address(saleRecipient).balance; - uint256 etherBalanceOfRecipient = address(recipient).balance; - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature{ value: 1 }(_mintrequest, _signature); - - // check state after minting - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.tokenURI(nextTokenId), string(_mintrequest.uri)); - assertEq(tokenContract.totalSupply(), currentTotalSupply + 1); - assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient + 1); - assertEq(tokenContract.ownerOf(nextTokenId), recipient); - - // check erc20 balances after minting - uint256 _platformFees = (_mintrequest.price * platformFeeBps) / MAX_BPS; - assertEq(address(recipient).balance, etherBalanceOfRecipient - _mintrequest.price); - assertEq(address(saleRecipient).balance, etherBalanceOfSeller + _mintrequest.price - _platformFees); - } - - function test_revert_mintWithSignature_MustSendTotalPrice() public { - vm.warp(1000); - - _mintrequest.price = 1; - _mintrequest.currency = address(NATIVE_TOKEN); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("must send total price."); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_MsgValueNotZero() public { - vm.warp(1000); - - _mintrequest.price = 1; - _mintrequest.currency = address(erc20); - _signature = signMintRequest(_mintrequest, privateKey); - - // shouldn't send native-token when it is not the currency - vm.prank(recipient); - vm.expectRevert("msg value not zero"); - tokenContract.mintWithSignature{ value: 1 }(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_InvalidSignature() public { - vm.warp(1000); - - uint256 incorrectKey = 3456; - _signature = signMintRequest(_mintrequest, incorrectKey); - - vm.prank(recipient); - vm.expectRevert("invalid signature"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_RequestExpired() public { - _signature = signMintRequest(_mintrequest, privateKey); - - // warp time more out of range - vm.warp(3000); - - vm.prank(recipient); - vm.expectRevert("request expired"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_revert_mintWithSignature_RecipientUndefined() public { - vm.warp(1000); - - _mintrequest.to = address(0); - _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(recipient); - vm.expectRevert("recipient undefined"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_event_mintWithSignature() public { - vm.warp(1000); - - vm.expectEmit(true, true, true, true); - emit TokensMintedWithSignature(deployerSigner, recipient, 0, _mintrequest); - - // mint with signature - vm.prank(recipient); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `mintTo` - //////////////////////////////////////////////////////////////*/ - - function test_state_mintTo() public { - string memory _tokenURI = "tokenURI"; - - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentTotalSupply = tokenContract.totalSupply(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); - - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, _tokenURI); - - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.tokenURI(nextTokenId), _tokenURI); - assertEq(tokenContract.totalSupply(), currentTotalSupply + 1); - assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient + 1); - assertEq(tokenContract.ownerOf(nextTokenId), recipient); - } - - function test_revert_mintTo_NotAuthorized() public { - string memory _tokenURI = "tokenURI"; - bytes32 role = keccak256("MINTER_ROLE"); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.mintTo(recipient, _tokenURI); - } - - function test_revert_mintTo_emptyURI() public { - // mint - vm.prank(deployerSigner); - vm.expectRevert("empty uri."); - tokenContract.mintTo(recipient, ""); - } - - function test_event_mintTo() public { - string memory _tokenURI = "tokenURI"; - - vm.expectEmit(true, true, true, true); - emit TokensMinted(recipient, 0, _tokenURI); - - // mint - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, _tokenURI); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: `burn` - //////////////////////////////////////////////////////////////*/ - - function test_state_burn_TokenOwner() public { - string memory _tokenURI = "tokenURI"; - - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentTotalSupply = tokenContract.totalSupply(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); - - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, _tokenURI); - - vm.prank(recipient); - tokenContract.burn(nextTokenId); - - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.tokenURI(nextTokenId), _tokenURI); - assertEq(tokenContract.totalSupply(), currentTotalSupply); - assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient); - - vm.expectRevert("ERC721: invalid token ID"); - assertEq(tokenContract.ownerOf(nextTokenId), address(0)); - } - - function test_state_burn_TokenOperator() public { - string memory _tokenURI = "tokenURI"; - - address operator = address(0x789); - - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - uint256 currentTotalSupply = tokenContract.totalSupply(); - uint256 currentBalanceOfRecipient = tokenContract.balanceOf(recipient); - - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, _tokenURI); - - vm.prank(recipient); - tokenContract.setApprovalForAll(operator, true); - - vm.prank(operator); - tokenContract.burn(nextTokenId); - - assertEq(tokenContract.nextTokenIdToMint(), nextTokenId + 1); - assertEq(tokenContract.tokenURI(nextTokenId), _tokenURI); - assertEq(tokenContract.totalSupply(), currentTotalSupply); - assertEq(tokenContract.balanceOf(recipient), currentBalanceOfRecipient); - - vm.expectRevert("ERC721: invalid token ID"); - assertEq(tokenContract.ownerOf(nextTokenId), address(0)); - } - - function test_revert_burn_NotOwnerNorApproved() public { - string memory _tokenURI = "tokenURI"; - - uint256 nextTokenId = tokenContract.nextTokenIdToMint(); - - vm.prank(deployerSigner); - tokenContract.mintTo(recipient, _tokenURI); - - vm.prank(address(0x789)); - vm.expectRevert("ERC721Burnable: caller is not owner nor approved"); - tokenContract.burn(nextTokenId); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: owner - //////////////////////////////////////////////////////////////*/ - - function test_state_setOwner() public { - address newOwner = address(0x123); - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.prank(deployerSigner); - tokenContract.grantRole(role, newOwner); - - vm.prank(deployerSigner); - tokenContract.setOwner(newOwner); - - assertEq(tokenContract.owner(), newOwner); - } - - function test_revert_setOwner_NotModuleAdmin() public { - vm.expectRevert("new owner not module admin."); - vm.prank(deployerSigner); - tokenContract.setOwner(address(0x1234)); - } - - function test_event_setOwner() public { - address newOwner = address(0x123); - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.startPrank(deployerSigner); - tokenContract.grantRole(role, newOwner); - - vm.expectEmit(true, true, true, true); - emit OwnerUpdated(deployerSigner, newOwner); - - tokenContract.setOwner(newOwner); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: royalty - //////////////////////////////////////////////////////////////*/ - - function test_state_setDefaultRoyaltyInfo() public { - address _royaltyRecipient = address(0x123); - uint256 _royaltyBps = 1000; - - vm.prank(deployerSigner); - tokenContract.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - - (address newRoyaltyRecipient, uint256 newRoyaltyBps) = tokenContract.getDefaultRoyaltyInfo(); - assertEq(newRoyaltyRecipient, _royaltyRecipient); - assertEq(newRoyaltyBps, _royaltyBps); - - (address receiver, uint256 royaltyAmount) = tokenContract.royaltyInfo(0, 100); - assertEq(receiver, _royaltyRecipient); - assertEq(royaltyAmount, (100 * 1000) / 10_000); - } - - function test_revert_setDefaultRoyaltyInfo_NotAuthorized() public { - address _royaltyRecipient = address(0x123); - uint256 _royaltyBps = 1000; - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - } - - function test_revert_setDefaultRoyaltyInfo_ExceedsRoyaltyBps() public { - address _royaltyRecipient = address(0x123); - uint256 _royaltyBps = 10001; - - vm.expectRevert("exceed royalty bps"); - vm.prank(deployerSigner); - tokenContract.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - } - - function test_state_setRoyaltyInfoForToken() public { - uint256 _tokenId = 1; - address _recipient = address(0x123); - uint256 _bps = 1000; - - vm.prank(deployerSigner); - tokenContract.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); - - (address receiver, uint256 royaltyAmount) = tokenContract.royaltyInfo(_tokenId, 100); - assertEq(receiver, _recipient); - assertEq(royaltyAmount, (100 * 1000) / 10_000); - } - - function test_revert_setRoyaltyInfo_NotAuthorized() public { - uint256 _tokenId = 1; - address _recipient = address(0x123); - uint256 _bps = 1000; - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); - } - - function test_revert_setRoyaltyInfoForToken_ExceedsRoyaltyBps() public { - uint256 _tokenId = 1; - address _recipient = address(0x123); - uint256 _bps = 10001; - - vm.expectRevert("exceed royalty bps"); - vm.prank(deployerSigner); - tokenContract.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); - } - - function test_event_defaultRoyalty() public { - address _royaltyRecipient = address(0x123); - uint256 _royaltyBps = 1000; - - vm.expectEmit(true, true, true, true); - emit DefaultRoyalty(_royaltyRecipient, _royaltyBps); - - vm.prank(deployerSigner); - tokenContract.setDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps); - } - - function test_event_royaltyForToken() public { - uint256 _tokenId = 1; - address _recipient = address(0x123); - uint256 _bps = 1000; - - vm.expectEmit(true, true, true, true); - emit RoyaltyForToken(_tokenId, _recipient, _bps); - - vm.prank(deployerSigner); - tokenContract.setRoyaltyInfoForToken(_tokenId, _recipient, _bps); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: primary sale - //////////////////////////////////////////////////////////////*/ - - function test_state_setPrimarySaleRecipient() public { - address _primarySaleRecipient = address(0x123); - - vm.prank(deployerSigner); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - - address recipient_ = tokenContract.primarySaleRecipient(); - assertEq(recipient_, _primarySaleRecipient); - } - - function test_revert_setPrimarySaleRecipient_NotAuthorized() public { - address _primarySaleRecipient = address(0x123); - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - } - - function test_event_setPrimarySaleRecipient() public { - address _primarySaleRecipient = address(0x123); - - vm.expectEmit(true, true, true, true); - emit PrimarySaleRecipientUpdated(_primarySaleRecipient); - - vm.prank(deployerSigner); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: platform fee - //////////////////////////////////////////////////////////////*/ - - function test_state_setPlatformFeeInfo() public { - address _platformFeeRecipient = address(0x123); - uint256 _platformFeeBps = 1000; - - vm.prank(deployerSigner); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - - (address recipient_, uint16 bps) = tokenContract.getPlatformFeeInfo(); - assertEq(_platformFeeRecipient, recipient_); - assertEq(_platformFeeBps, bps); - } - - function test_revert_setPlatformFeeInfo_ExceedsMaxBps() public { - address _platformFeeRecipient = address(0x123); - uint256 _platformFeeBps = 10001; - - vm.expectRevert("exceeds MAX_BPS"); - vm.prank(deployerSigner); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - function test_revert_setPlatformFeeInfo_NotAuthorized() public { - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setPlatformFeeInfo(address(1), 1000); - } - - function test_event_platformFeeInfo() public { - address _platformFeeRecipient = address(0x123); - uint256 _platformFeeBps = 1000; - - vm.expectEmit(true, true, true, true); - emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps); - - vm.prank(deployerSigner); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: contract metadata - //////////////////////////////////////////////////////////////*/ - - function test_state_setContractURI() public { - string memory uri = "uri_string"; - - vm.prank(deployerSigner); - tokenContract.setContractURI(uri); - - string memory _contractURI = tokenContract.contractURI(); - - assertEq(_contractURI, uri); - } - - function test_revert_setContractURI() public { - bytes32 role = tokenContract.DEFAULT_ADMIN_ROLE(); - - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(address(0x1)), 20), - " is missing role ", - Strings.toHexString(uint256(role), 32) - ) - ); - vm.prank(address(0x1)); - tokenContract.setContractURI(""); - } - - /*/////////////////////////////////////////////////////////////// - Unit tests: setTokenURI - //////////////////////////////////////////////////////////////*/ - - function test_setTokenURI_state() public { - string memory uri = "uri_string"; - - vm.prank(deployerSigner); - tokenContract.setTokenURI(0, uri); - - string memory _tokenURI = tokenContract.tokenURI(0); - - assertEq(_tokenURI, uri); - } - - function test_setTokenURI_revert_NotAuthorized() public { - string memory uri = "uri_string"; - - vm.expectRevert(abi.encodeWithSelector(NFTMetadata.NFTMetadataUnauthorized.selector)); - vm.prank(address(0x1)); - tokenContract.setTokenURI(0, uri); - } - - function test_setTokenURI_revert_Frozen() public { - string memory uri = "uri_string"; - - vm.startPrank(deployerSigner); - tokenContract.freezeMetadata(); - - vm.expectRevert(abi.encodeWithSelector(NFTMetadata.NFTMetadataFrozen.selector, 0)); - tokenContract.setTokenURI(0, uri); - } -} diff --git a/src/test/tokenerc1155-BTT/burn-batch/burnBatch.t.sol b/src/test/tokenerc1155-BTT/burn-batch/burnBatch.t.sol deleted file mode 100644 index 273cb2db4..000000000 --- a/src/test/tokenerc1155-BTT/burn-batch/burnBatch.t.sol +++ /dev/null @@ -1,148 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract TokenERC1155Test_BurnBatch is BaseTest { - address public implementation; - address public proxy; - address public caller; - address public recipient; - string public uri; - uint256 public amount; - - MyTokenERC1155 internal tokenContract; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - caller = getActor(1); - recipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - uri = "uri"; - amount = 100; - - vm.prank(deployer); - tokenContract.grantRole(keccak256("MINTER_ROLE"), caller); - } - - function test_burn_whenNotOwnerNorApproved() public { - // mint two tokenIds - vm.startPrank(caller); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - vm.stopPrank(); - - uint256[] memory ids = new uint256[](2); - uint256[] memory amounts = new uint256[](2); - - ids[0] = 0; - ids[1] = 1; - amounts[0] = 10; - amounts[1] = 10; - - // burn - vm.expectRevert("ERC1155: caller is not owner nor approved."); - tokenContract.burnBatch(recipient, ids, amounts); - } - - function test_burn_whenOwner_invalidAmount() public { - // mint two tokenIds - vm.startPrank(caller); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - vm.stopPrank(); - - uint256[] memory ids = new uint256[](2); - uint256[] memory amounts = new uint256[](2); - - ids[0] = 0; - ids[1] = 1; - amounts[0] = 1000 ether; - amounts[1] = 10; - - // burn - vm.prank(recipient); - vm.expectRevert(); - tokenContract.burnBatch(recipient, ids, amounts); - } - - function test_burn_whenOwner() public { - // mint two tokenIds - vm.startPrank(caller); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - vm.stopPrank(); - - uint256[] memory ids = new uint256[](2); - uint256[] memory amounts = new uint256[](2); - - ids[0] = 0; - ids[1] = 1; - amounts[0] = 10; - amounts[1] = 10; - - // burn - vm.prank(recipient); - tokenContract.burnBatch(recipient, ids, amounts); - - assertEq(tokenContract.balanceOf(recipient, ids[0]), amount - amounts[0]); - assertEq(tokenContract.balanceOf(recipient, ids[1]), amount - amounts[1]); - } - - function test_burn_whenApproved() public { - // mint two tokenIds - vm.startPrank(caller); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - vm.stopPrank(); - - uint256[] memory ids = new uint256[](2); - uint256[] memory amounts = new uint256[](2); - - ids[0] = 0; - ids[1] = 1; - amounts[0] = 10; - amounts[1] = 10; - - vm.prank(recipient); - tokenContract.setApprovalForAll(caller, true); - - // burn - vm.prank(caller); - tokenContract.burnBatch(recipient, ids, amounts); - - assertEq(tokenContract.balanceOf(recipient, ids[0]), amount - amounts[0]); - assertEq(tokenContract.balanceOf(recipient, ids[1]), amount - amounts[1]); - } -} diff --git a/src/test/tokenerc1155-BTT/burn-batch/burnBatch.tree b/src/test/tokenerc1155-BTT/burn-batch/burnBatch.tree deleted file mode 100644 index dca6fa537..000000000 --- a/src/test/tokenerc1155-BTT/burn-batch/burnBatch.tree +++ /dev/null @@ -1,14 +0,0 @@ -burnBatch( - address account, - uint256[] memory ids, - uint256[] memory values -) -├── when the caller isn't `account` or `account` hasn't approved tokens to caller -│ └── it should revert ✅ -└── when the caller is `account` with balances less than `values` for corresponding `ids` -│ └── it should revert ✅ -└── when the caller is `account` with balances greater than or equal to `values` -│ └── it should burn `values` amounts of `ids` tokens from account ✅ -└── when the `account` has approved `values` amount of tokens to caller - └── it should burn the token ✅ - diff --git a/src/test/tokenerc1155-BTT/burn/burn.t.sol b/src/test/tokenerc1155-BTT/burn/burn.t.sol deleted file mode 100644 index 1bf2575b9..000000000 --- a/src/test/tokenerc1155-BTT/burn/burn.t.sol +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract TokenERC1155Test_Burn is BaseTest { - address public implementation; - address public proxy; - address public caller; - address public recipient; - string public uri; - uint256 public amount; - - MyTokenERC1155 internal tokenContract; - - event MetadataUpdate(uint256 _tokenId); - event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - caller = getActor(1); - recipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - uri = "uri"; - amount = 100; - - vm.prank(deployer); - tokenContract.grantRole(keccak256("MINTER_ROLE"), caller); - } - - function test_burn_whenNotOwnerNorApproved() public { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - - // burn - vm.expectRevert("ERC1155: caller is not owner nor approved."); - tokenContract.burn(recipient, _tokenIdToMint, amount); - } - - function test_burn_whenOwner_invalidAmount() public { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - - // burn - vm.prank(recipient); - vm.expectRevert(); - tokenContract.burn(recipient, _tokenIdToMint, amount + 1); - } - - function test_burn_whenOwner() public { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - - // burn - vm.prank(recipient); - tokenContract.burn(recipient, _tokenIdToMint, amount); - - assertEq(tokenContract.balanceOf(recipient, _tokenIdToMint), 0); - } - - function test_burn_whenApproved() public { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - - vm.prank(recipient); - tokenContract.setApprovalForAll(caller, true); - - // burn - vm.prank(caller); - tokenContract.burn(recipient, _tokenIdToMint, amount); - - assertEq(tokenContract.balanceOf(recipient, _tokenIdToMint), 0); - } -} diff --git a/src/test/tokenerc1155-BTT/burn/burn.tree b/src/test/tokenerc1155-BTT/burn/burn.tree deleted file mode 100644 index 8232a832d..000000000 --- a/src/test/tokenerc1155-BTT/burn/burn.tree +++ /dev/null @@ -1,14 +0,0 @@ -burn( - address account, - uint256 id, - uint256 value -) -├── when the caller isn't `account` or `account` hasn't approved tokens to caller -│ └── it should revert ✅ -└── when the caller is `account` with balance less than `value` -│ └── it should revert ✅ -└── when the caller is `account` with balance greater than or equal to `value` -│ └── it should burn `value` amount of `id` tokens from ✅ -└── when the `account` has approved `value` amount of tokens to caller - └── it should burn the token ✅ - diff --git a/src/test/tokenerc1155-BTT/initialize/initialize.t.sol b/src/test/tokenerc1155-BTT/initialize/initialize.t.sol deleted file mode 100644 index 5340ff595..000000000 --- a/src/test/tokenerc1155-BTT/initialize/initialize.t.sol +++ /dev/null @@ -1,313 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { IPlatformFee } from "contracts/extension/interface/IPlatformFee.sol"; - -contract MyTokenERC1155 is TokenERC1155 { - function eip712NameHash() external view returns (bytes32) { - return _EIP712NameHash(); - } - - function eip712VersionHash() external view returns (bytes32) { - return _EIP712VersionHash(); - } -} - -contract TokenERC1155Test_Initialize is BaseTest { - address public implementation; - address public proxy; - - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - } - - function test_initialize_initializingImplementation() public { - vm.expectRevert("Initializable: contract is already initialized"); - TokenERC1155(implementation).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - modifier whenNotImplementation() { - _; - } - - function test_initialize_proxyAlreadyInitialized() public whenNotImplementation { - vm.expectRevert("Initializable: contract is already initialized"); - MyTokenERC1155(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - modifier whenProxyNotInitialized() { - proxy = address(new TWProxy(implementation, "")); - _; - } - - function test_initialize_exceedsMaxBps() public whenNotImplementation whenProxyNotInitialized { - vm.expectRevert("exceeds MAX_BPS"); - MyTokenERC1155(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - uint128(MAX_BPS) + 1, // platformFeeBps greater than MAX_BPS - platformFeeRecipient - ); - } - - modifier whenPlatformFeeBpsWithinMaxBps() { - _; - } - - function test_initialize() public whenNotImplementation whenProxyNotInitialized whenPlatformFeeBpsWithinMaxBps { - MyTokenERC1155(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - - // check state - MyTokenERC1155 tokenContract = MyTokenERC1155(proxy); - - assertEq(tokenContract.eip712NameHash(), keccak256(bytes("TokenERC1155"))); - assertEq(tokenContract.eip712VersionHash(), keccak256(bytes("1"))); - - address[] memory _trustedForwarders = forwarders(); - for (uint256 i = 0; i < _trustedForwarders.length; i++) { - assertTrue(tokenContract.isTrustedForwarder(_trustedForwarders[i])); - } - - assertEq(tokenContract.name(), NAME); - assertEq(tokenContract.symbol(), SYMBOL); - assertEq(tokenContract.contractURI(), CONTRACT_URI); - - (address _platformFeeRecipient, uint16 _platformFeeBps) = tokenContract.getPlatformFeeInfo(); - assertEq(_platformFeeBps, platformFeeBps); - assertEq(_platformFeeRecipient, platformFeeRecipient); - assertEq(tokenContract.platformFeeRecipient(), platformFeeRecipient); - assertEq(uint8(tokenContract.getPlatformFeeType()), uint8(IPlatformFee.PlatformFeeType.Bps)); - - (address _royaltyRecipient, uint16 _royaltyBps) = tokenContract.getDefaultRoyaltyInfo(); - (address _royaltyRecipientForToken, uint16 _royaltyBpsForToken) = tokenContract.getRoyaltyInfoForToken(1); // random tokenId - assertEq(_royaltyBps, royaltyBps); - assertEq(_royaltyRecipient, royaltyRecipient); - assertEq(_royaltyRecipient, _royaltyRecipientForToken); - assertEq(_royaltyBps, _royaltyBpsForToken); - - assertEq(tokenContract.primarySaleRecipient(), saleRecipient); - - assertEq(tokenContract.owner(), deployer); - assertTrue(tokenContract.hasRole(bytes32(0x00), deployer)); - assertTrue(tokenContract.hasRole(keccak256("TRANSFER_ROLE"), deployer)); - assertTrue(tokenContract.hasRole(keccak256("TRANSFER_ROLE"), address(0))); - assertTrue(tokenContract.hasRole(keccak256("MINTER_ROLE"), deployer)); - assertTrue(tokenContract.hasRole(keccak256("METADATA_ROLE"), deployer)); - assertEq(tokenContract.getRoleAdmin(keccak256("METADATA_ROLE")), keccak256("METADATA_ROLE")); - } - - function test_initialize_event_RoleGranted_DefaultAdmin() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _defaultAdminRole = bytes32(0x00); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_defaultAdminRole, deployer, deployer); - MyTokenERC1155(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_MinterRole() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _minterRole = keccak256("MINTER_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_minterRole, deployer, deployer); - MyTokenERC1155(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_TransferRole() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _transferRole = keccak256("TRANSFER_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_transferRole, deployer, deployer); - MyTokenERC1155(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_TransferRole_AddressZero() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _transferRole = keccak256("TRANSFER_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_transferRole, address(0), deployer); - MyTokenERC1155(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_MetadataRole() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _metadataRole = keccak256("METADATA_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_metadataRole, deployer, deployer); - MyTokenERC1155(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleAdminChanged_MetadataRole() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _metadataRole = keccak256("METADATA_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleAdminChanged(_metadataRole, bytes32(0x00), _metadataRole); - MyTokenERC1155(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } -} diff --git a/src/test/tokenerc1155-BTT/initialize/initialize.tree b/src/test/tokenerc1155-BTT/initialize/initialize.tree deleted file mode 100644 index 15c2ba936..000000000 --- a/src/test/tokenerc1155-BTT/initialize/initialize.tree +++ /dev/null @@ -1,43 +0,0 @@ -initialize( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _primarySaleRecipient, - address _royaltyRecipient, - uint128 _royaltyBps, - uint128 _platformFeeBps, - address _platformFeeRecipient -) -├── when initializing the implementation contract (not proxy) -│ └── it should revert ✅ -└── when it is a proxy to the implementation - └── when it is already initialized - │ └── it should revert ✅ - └── when it is not initialized - └── when platformFeeBps is greater than MAX_BPS - │ └── it should revert ✅ - └── when platformFeeBps is less than or equal to MAX_BPS - └── it should correctly set EIP712 name hash and version hash ✅ - └── it should set trustedForwarder mapping to true for all addresses in `_trustedForwarders` ✅ - └── it should set name and symbol to `_name` and `_symbol` param values respectively ✅ - └── it should set contractURI to `_contractURI` param value ✅ - └── it should set platformFeeRecipient and platformFeeBps as `_platformFeeRecipient` and `_platformFeeBps` respectively ✅ - └── it should set platformFeeType to `Bps` ✅ - └── it should set royaltyRecipient and royaltyBps as `_royaltyRecipient` and `_royaltyBps` respectively ✅ - └── it should set primary sale recipient as `_primarySaleRecipient` param value ✅ - └── it should set _owner to `_defaultAdmin` param value ✅ - └── it should grant 0x00 (DEFAULT_ADMIN_ROLE) to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should grant MINTER_ROLE to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should grant TRANSFER_ROLE to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should grant TRANSFER_ROLE to address(0) ✅ - └── it should emit RoleGranted event ✅ - └── it should grant METADATA_ROLE to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should set METADATA_ROLE as role admin for METADATA_ROLE ✅ - └── it should emit RoleAdminChanged event ✅ - diff --git a/src/test/tokenerc1155-BTT/mint-to/mintTo.t.sol b/src/test/tokenerc1155-BTT/mint-to/mintTo.t.sol deleted file mode 100644 index b0a8703bb..000000000 --- a/src/test/tokenerc1155-BTT/mint-to/mintTo.t.sol +++ /dev/null @@ -1,307 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract ERC1155ReceiverCompliant is IERC1155Receiver { - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes calldata data - ) external view virtual override returns (bytes4) { - return this.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address operator, - address from, - uint256[] calldata ids, - uint256[] calldata values, - bytes calldata data - ) external returns (bytes4) { - return this.onERC1155BatchReceived.selector; - } - - function supportsInterface(bytes4 interfaceId) external view returns (bool) {} -} - -contract TokenERC1155Test_MintTo is BaseTest { - address public implementation; - address public proxy; - address public caller; - address public recipient; - string public uri; - uint256 public amount; - - MyTokenERC1155 internal tokenContract; - ERC1155ReceiverCompliant internal erc1155ReceiverContract; - - event MetadataUpdate(uint256 _tokenId); - event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri, uint256 quantityMinted); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - erc1155ReceiverContract = new ERC1155ReceiverCompliant(); - caller = getActor(1); - recipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - amount = 100; - uri = "ipfs://uri"; - } - - function test_mintTo_notMinterRole() public { - vm.prank(caller); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(keccak256("MINTER_ROLE")), 32) - ) - ); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - } - - modifier whenMinterRole() { - vm.prank(deployer); - tokenContract.grantRole(keccak256("MINTER_ROLE"), caller); - _; - } - - // ================== - // ======= Test branch: `tokenId` input param is type(uint256).max - // ================== - - function test_mintTo_maxTokenId_EOA() public whenMinterRole { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - - // check state after - assertEq(_tokenIdToMint, 0); - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.balanceOf(recipient, _tokenIdToMint), amount); - assertEq(tokenContract.uri(_tokenIdToMint), uri); - } - - function test_mintTo_maxTokenId_EOA_TokensMintedEvent() public whenMinterRole { - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(true, true, false, true); - emit TokensMinted(recipient, _tokenIdToMint, uri, amount); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - } - - function test_mintTo_maxTokenId_EOA_MetadataUpdateEvent() public whenMinterRole { - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(false, false, false, true); - emit MetadataUpdate(_tokenIdToMint); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - } - - function test_mintTo_maxTokenId_EOA_uriAlreadyPresent() public whenMinterRole { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(deployer); - tokenContract.setTokenURI(_tokenIdToMint, "ipfs://uriOld"); - - // mint - vm.prank(caller); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - - // check state after - assertEq(_tokenIdToMint, 0); - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.balanceOf(recipient, _tokenIdToMint), amount); - assertEq(tokenContract.uri(_tokenIdToMint), "ipfs://uriOld"); - } - - function test_mintTo_maxTokenId_nonERC1155ReceiverContract() public whenMinterRole { - recipient = address(this); - vm.prank(caller); - vm.expectRevert(); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - } - - modifier whenERC1155Receiver() { - recipient = address(erc1155ReceiverContract); - _; - } - - function test_mintTo_maxTokenId_contract() public whenMinterRole whenERC1155Receiver { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - - // check state after - assertEq(_tokenIdToMint, 0); - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.balanceOf(recipient, _tokenIdToMint), amount); - assertEq(tokenContract.uri(_tokenIdToMint), uri); - } - - function test_mintTo_maxTokenId_contract_TokensMintedEvent() public whenMinterRole whenERC1155Receiver { - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(true, true, false, true); - emit TokensMinted(recipient, _tokenIdToMint, uri, amount); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - } - - function test_mintTo_maxTokenId_contract_MetadataUpdateEvent() public whenMinterRole whenERC1155Receiver { - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(false, false, false, true); - emit MetadataUpdate(_tokenIdToMint); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - } - - function test_mintTo_maxTokenId_contract_uriAlreadyPresent() public whenMinterRole whenERC1155Receiver { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(deployer); - tokenContract.setTokenURI(_tokenIdToMint, "ipfs://uriOld"); - - // mint - vm.prank(caller); - tokenContract.mintTo(recipient, type(uint256).max, uri, amount); - - // check state after - assertEq(_tokenIdToMint, 0); - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.balanceOf(recipient, _tokenIdToMint), amount); - assertEq(tokenContract.uri(_tokenIdToMint), "ipfs://uriOld"); - } - - // ================== - // ======= Test branch: `tokenId` input param is not type(uint256).max - // ================== - - modifier whenNotMaxTokenId() { - // pre-mint the first token (i.e. id 0), so that nextTokenIdToMint is 1, for this code path - vm.prank(deployer); - tokenContract.mintTo(deployer, type(uint256).max, "uri1", amount); - _; - } - - function test_mintTo_EOA_invalidId() public whenMinterRole whenNotMaxTokenId { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - vm.expectRevert("invalid id"); - tokenContract.mintTo(recipient, _tokenIdToMint, uri, amount); - } - - modifier whenValidId() { - _; - } - - function test_mintTo_EOA() public whenMinterRole whenNotMaxTokenId whenValidId { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint() - 1; - - // mint - vm.prank(caller); - tokenContract.mintTo(recipient, _tokenIdToMint, uri, amount); - - // check state after - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.balanceOf(recipient, _tokenIdToMint), amount); - assertEq(tokenContract.uri(_tokenIdToMint), "uri1"); - } - - function test_mintTo_EOA_TokensMintedEvent() public whenMinterRole whenNotMaxTokenId whenValidId { - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint() - 1; - - vm.prank(caller); - vm.expectEmit(true, true, false, true); - emit TokensMinted(recipient, _tokenIdToMint, "uri1", amount); - tokenContract.mintTo(recipient, _tokenIdToMint, uri, amount); - } - - function test_mintTo_nonERC1155ReceiverContract() public whenMinterRole whenNotMaxTokenId whenValidId { - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint() - 1; - - recipient = address(this); - vm.prank(caller); - vm.expectRevert(); - tokenContract.mintTo(recipient, _tokenIdToMint, uri, amount); - } - - function test_mintTo_contract() public whenMinterRole whenNotMaxTokenId whenERC1155Receiver whenValidId { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint() - 1; - - // mint - vm.prank(caller); - tokenContract.mintTo(recipient, _tokenIdToMint, uri, amount); - - // check state after - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.balanceOf(recipient, _tokenIdToMint), amount); - assertEq(tokenContract.uri(_tokenIdToMint), "uri1"); - } - - function test_mintTo_contract_TokensMintedEvent() - public - whenMinterRole - whenNotMaxTokenId - whenERC1155Receiver - whenValidId - { - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint() - 1; - - vm.prank(caller); - vm.expectEmit(true, true, false, true); - emit TokensMinted(recipient, _tokenIdToMint, "uri1", amount); - tokenContract.mintTo(recipient, _tokenIdToMint, uri, amount); - } -} diff --git a/src/test/tokenerc1155-BTT/mint-to/mintTo.tree b/src/test/tokenerc1155-BTT/mint-to/mintTo.tree deleted file mode 100644 index facd1e8eb..000000000 --- a/src/test/tokenerc1155-BTT/mint-to/mintTo.tree +++ /dev/null @@ -1,48 +0,0 @@ -mintTo( - address _to, - uint256 _tokenId, - string calldata _uri, - uint256 _amount -) -├── when caller doesn't have MINTER_ROLE - │ └── it should revert ✅ - └── when caller has MINTER_ROLE - ├── when `_tokenId` is type(uint256).max - │ ├── when `_to` address is an EOA - │ │ └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - │ │ └── it should increment `nextTokenIdToMint` by 1 ✅ - │ │ └── it should mint the `_amount` number of tokens to the `_to` address ✅ - │ │ └── it should emit TokensMinted event ✅ - │ │ └── when there is no uri associated with the minted tokenId - │ │ └── it should set uri for minted tokenId equal to `_uri` ✅ - │ │ └── it should emit MetadataUpdate event ✅ - │ └── when `_to` address is a contract - │ ├── when `_to` address is non ERC1155Receiver implementer - │ │ └── it should revert ✅ - │ └── when `_to` address implements ERC1155Receiver - │ └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - │ └── it should increment `nextTokenIdToMint` by 1 ✅ - │ └── it should mint the `_amount` number of tokens to the `_to` address ✅ - │ └── it should emit TokensMinted event ✅ - │ └── when there is no uri associated with the minted tokenId - │ └── it should set uri for minted tokenId equal to `_uri` ✅ - │ └── it should emit MetadataUpdate event ✅ - │ - └── when `_tokenId` is not type(uint256).max - ├── when `_tokenId` is not less than nextTokenIdToMint - │ └── it should revert ✅ - └── when `_tokenId` is less than nextTokenIdToMint - ├── when `_to` address is an EOA - │ └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - │ └── it should increment `nextTokenIdToMint` by 1 ✅ - │ └── it should mint the `_amount` number of tokens to the `_to` address ✅ - │ └── it should emit TokensMinted event ✅ - └── when `_to` address is a contract - ├── when `_to` address is non ERC1155Receiver implementer - │ └── it should revert ✅ - └── when `_to` address implements ERC1155Receiver - └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - └── it should increment `nextTokenIdToMint` by 1 ✅ - └── it should mint the `_amount` number of tokens to the `_to` address ✅ - └── it should emit TokensMinted event ✅ - diff --git a/src/test/tokenerc1155-BTT/mint-with-signature/mintWithSignature.t.sol b/src/test/tokenerc1155-BTT/mint-with-signature/mintWithSignature.t.sol deleted file mode 100644 index 0452e427f..000000000 --- a/src/test/tokenerc1155-BTT/mint-with-signature/mintWithSignature.t.sol +++ /dev/null @@ -1,903 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { IPlatformFee } from "contracts/extension/interface/IPlatformFee.sol"; - -contract MyTokenERC1155 is TokenERC1155 { - function setMintedURI(MintRequest calldata _req, bytes calldata _signature) external { - verifyRequest(_req, _signature); - } -} - -contract ERC1155ReceiverCompliant is IERC1155Receiver { - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes calldata data - ) external view virtual override returns (bytes4) { - return this.onERC1155Received.selector; - } - - function onERC1155BatchReceived( - address operator, - address from, - uint256[] calldata ids, - uint256[] calldata values, - bytes calldata data - ) external returns (bytes4) { - return this.onERC1155BatchReceived.selector; - } - - function supportsInterface(bytes4 interfaceId) external view returns (bool) {} -} - -contract ReentrantContract { - fallback() external payable { - TokenERC1155.MintRequest memory _mintrequest; - bytes memory _signature; - MyTokenERC1155(msg.sender).mintWithSignature(_mintrequest, _signature); - } -} - -contract TokenERC1155Test_MintWithSignature is BaseTest { - address public implementation; - address public proxy; - address public caller; - address public recipient; - string public uri; - - address private defaultFeeRecipient; - - MyTokenERC1155 internal tokenContract; - ERC1155ReceiverCompliant internal erc1155ReceiverContract; - - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - TokenERC1155.MintRequest _mintrequest; - - event MetadataUpdate(uint256 _tokenId); - event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri); - event TokensMintedWithSignature( - address indexed signer, - address indexed mintedTo, - uint256 indexed tokenIdMinted, - TokenERC1155.MintRequest mintRequest - ); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - erc1155ReceiverContract = new ERC1155ReceiverCompliant(); - caller = getActor(1); - recipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - defaultFeeRecipient = tokenContract.DEFAULT_FEE_RECIPIENT(); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,uint256 tokenId,string uri,uint256 quantity,uint256 pricePerToken,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("TokenERC1155")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) - ); - - // construct default mintrequest - _mintrequest.to = address(0x1234); - _mintrequest.royaltyRecipient = royaltyRecipient; - _mintrequest.royaltyBps = royaltyBps; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.tokenId = type(uint256).max; - _mintrequest.uri = "ipfs://"; - _mintrequest.quantity = 100; - _mintrequest.pricePerToken = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 0; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - erc20.mint(caller, 1_000 ether); - vm.deal(caller, 1_000 ether); - - vm.startPrank(deployer); - erc20.approve(address(tokenContract), type(uint256).max); - vm.stopPrank(); - - vm.startPrank(caller); - erc20.approve(address(tokenContract), type(uint256).max); - vm.stopPrank(); - } - - function signMintRequest( - TokenERC1155.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = bytes.concat( - abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - _request.tokenId, - keccak256(bytes(_request.uri)) - ), - abi.encode( - _request.quantity, - _request.pricePerToken, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ) - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - // ================== - // ======= Assume _req.tokenId input is type(uint256).max and platform fee type is Bps - // ================== - - function test_mintWithSignature_notMinterRole() public { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - vm.expectRevert("invalid signature"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenMinterRole() { - vm.prank(deployer); - tokenContract.grantRole(keccak256("MINTER_ROLE"), signer); - _; - } - - function test_mintWithSignature_invalidUID() public whenMinterRole { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // set state with this mintrequest and signature, marking the UID as used - tokenContract.setMintedURI(_mintrequest, _signature); - - // pass the same UID mintrequest again - vm.prank(caller); - vm.expectRevert("invalid signature"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenUidNotUsed() { - _; - } - - function test_mintWithSignature_invalidStartTimestamp() public whenMinterRole whenUidNotUsed { - _mintrequest.validityStartTimestamp = uint128(block.timestamp + 1); - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("request expired"); - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenValidStartTimestamp() { - _; - } - - function test_mintWithSignature_invalidEndTimestamp() public whenMinterRole whenUidNotUsed whenValidStartTimestamp { - _mintrequest.validityEndTimestamp = uint128(block.timestamp - 1); - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("request expired"); - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenValidEndTimestamp() { - _; - } - - function test_mintWithSignature_recipientAddressZero() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - { - _mintrequest.to = address(0); - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("recipient undefined"); - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenRecipientAddressNotZero() { - _; - } - - function test_mintWithSignature_zeroQuantity() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - { - _mintrequest.quantity = 0; - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("zero quantity"); - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenNotZeroQuantity() { - _mintrequest.quantity = 100; - _; - } - - // ================== - // ======= Test branch: when mint price is zero - // ================== - - function test_mintWithSignature_zeroPrice_msgValueNonZero() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - { - _mintrequest.pricePerToken = 0; - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("!Value"); - vm.prank(caller); - tokenContract.mintWithSignature{ value: 1 }(_mintrequest, _signature); - } - - modifier whenMsgValueZero() { - _; - } - - function test_mintWithSignature_zeroPrice_EOA() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.pricePerToken = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.balanceOf(_mintrequest.to, _tokenIdToMint), _mintrequest.quantity); - assertEq(tokenContract.uri(_tokenIdToMint), _mintrequest.uri); - assertEq(tokenContract.totalSupply(_tokenIdToMint), _mintrequest.quantity); - } - - function test_mintWithSignature_zeroPrice_EOA_MetadataUpdateEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.pricePerToken = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(false, false, false, true); - emit MetadataUpdate(_tokenIdToMint); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_mintWithSignature_zeroPrice_EOA_TokensMintedWithSignatureEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.pricePerToken = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(true, true, false, true); - emit TokensMintedWithSignature(signer, _mintrequest.to, _tokenIdToMint, _mintrequest); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_mintWithSignature_zeroPrice_nonERC1155ReceiverContract() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.pricePerToken = 0; - _mintrequest.to = address(this); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // mint - vm.prank(caller); - vm.expectRevert(); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenERC1155Receiver() { - _mintrequest.to = address(erc1155ReceiverContract); - _; - } - - function test_mintWithSignature_zeroPrice_contract() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - whenERC1155Receiver - { - _mintrequest.pricePerToken = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.balanceOf(_mintrequest.to, _tokenIdToMint), _mintrequest.quantity); - assertEq(tokenContract.uri(_tokenIdToMint), _mintrequest.uri); - assertEq(tokenContract.totalSupply(_tokenIdToMint), _mintrequest.quantity); - } - - function test_mintWithSignature_zeroPrice_contract_MetadataUpdateEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - whenERC1155Receiver - { - _mintrequest.pricePerToken = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(false, false, false, true); - emit MetadataUpdate(_tokenIdToMint); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_mintWithSignature_zeroPrice_contract_TokensMintedWithSignatureEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - whenERC1155Receiver - { - _mintrequest.pricePerToken = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(true, true, true, true); - emit TokensMintedWithSignature(signer, _mintrequest.to, _tokenIdToMint, _mintrequest); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - // ================== - // ======= Test branch: when mint price is not zero - // ================== - - function test_mintWithSignature_nonZeroPrice_nativeToken_incorrectMsgValue() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - { - _mintrequest.pricePerToken = 10; - _mintrequest.currency = NATIVE_TOKEN; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 incorrectTotalPrice = (_mintrequest.pricePerToken * _mintrequest.quantity) + 1; - - vm.expectRevert("must send total price."); - vm.prank(caller); - tokenContract.mintWithSignature{ value: incorrectTotalPrice }(_mintrequest, _signature); - } - - modifier whenCorrectMsgValue() { - _; - } - - function test_mintWithSignature_nonZeroPrice_nativeToken() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenCorrectMsgValue - { - _mintrequest.pricePerToken = 10; - _mintrequest.currency = NATIVE_TOKEN; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - uint256 totalPrice = (_mintrequest.pricePerToken * _mintrequest.quantity); - - // mint - vm.prank(caller); - tokenContract.mintWithSignature{ value: totalPrice }(_mintrequest, _signature); - - // check state after - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.balanceOf(_mintrequest.to, _tokenIdToMint), _mintrequest.quantity); - assertEq(tokenContract.uri(_tokenIdToMint), _mintrequest.uri); - assertEq(tokenContract.totalSupply(_tokenIdToMint), _mintrequest.quantity); - - uint256 _platformFee = (totalPrice * platformFeeBps) / 10_000; - uint256 defaultFee = (totalPrice * 100) / 10_000; - uint256 _saleProceeds = totalPrice - _platformFee - defaultFee; - assertEq(caller.balance, 1000 ether - totalPrice); - assertEq(tokenContract.platformFeeRecipient().balance, _platformFee); - assertEq(tokenContract.primarySaleRecipient().balance, _saleProceeds); - assertEq(defaultFeeRecipient.balance, defaultFee); - } - - function test_mintWithSignature_nonZeroPrice_nativeToken_MetadataUpdateEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenCorrectMsgValue - { - _mintrequest.pricePerToken = 10; - _mintrequest.currency = NATIVE_TOKEN; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(false, false, false, true); - emit MetadataUpdate(_tokenIdToMint); - tokenContract.mintWithSignature{ value: _mintrequest.pricePerToken * _mintrequest.quantity }( - _mintrequest, - _signature - ); - } - - function test_mintWithSignature_nonZeroPrice_nativeToken_TokensMintedWithSignatureEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenCorrectMsgValue - { - _mintrequest.pricePerToken = 10; - _mintrequest.currency = NATIVE_TOKEN; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(true, true, true, true); - emit TokensMintedWithSignature(signer, _mintrequest.to, _tokenIdToMint, _mintrequest); - tokenContract.mintWithSignature{ value: _mintrequest.pricePerToken * _mintrequest.quantity }( - _mintrequest, - _signature - ); - } - - function test_mintWithSignature_nonZeroPrice_ERC20_nonZeroMsgValue() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - { - _mintrequest.pricePerToken = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("msg value not zero"); - vm.prank(caller); - tokenContract.mintWithSignature{ value: 1 }(_mintrequest, _signature); - } - - function test_mintWithSignature_nonZeroPrice_ERC20() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.pricePerToken = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - uint256 totalPrice = (_mintrequest.pricePerToken * _mintrequest.quantity); - - // mint - vm.prank(caller); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - - // check state after - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.balanceOf(_mintrequest.to, _tokenIdToMint), _mintrequest.quantity); - assertEq(tokenContract.uri(_tokenIdToMint), _mintrequest.uri); - assertEq(tokenContract.totalSupply(_tokenIdToMint), _mintrequest.quantity); - - uint256 _platformFee = (totalPrice * platformFeeBps) / 10_000; - uint256 defaultFee = (totalPrice * 100) / 10_000; - uint256 _saleProceeds = totalPrice - _platformFee - defaultFee; - assertEq(erc20.balanceOf(caller), 1000 ether - totalPrice); - assertEq(erc20.balanceOf(tokenContract.platformFeeRecipient()), _platformFee); - assertEq(erc20.balanceOf(tokenContract.primarySaleRecipient()), _saleProceeds); - assertEq(erc20.balanceOf(defaultFeeRecipient), defaultFee); - } - - function test_mintWithSignature_nonZeroPrice_ERC20_MetadataUpdateEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.pricePerToken = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(false, false, false, true); - emit MetadataUpdate(_tokenIdToMint); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - function test_mintWithSignature_nonZeroPrice_ERC20_TokensMintedWithSignatureEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.pricePerToken = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(true, true, true, true); - emit TokensMintedWithSignature(signer, _mintrequest.to, _tokenIdToMint, _mintrequest); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - // ================== - // ======= Test branch: other cases - // ================== - - function test_mintWithSignature_nonZeroRoyaltyRecipient() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.pricePerToken = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - - (address _royaltyRecipient, uint16 _royaltyBps) = tokenContract.getRoyaltyInfoForToken(0); - assertEq(_royaltyRecipient, royaltyRecipient); - assertEq(_royaltyBps, royaltyBps); - } - - function test_mintWithSignature_royaltyRecipientZeroAddress() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.pricePerToken = 0; - _mintrequest.royaltyRecipient = address(0); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - - (address _royaltyRecipient, uint16 _royaltyBps) = tokenContract.getRoyaltyInfoForToken(0); - (address _defaultRoyaltyRecipient, uint16 _defaultRoyaltyBps) = tokenContract.getDefaultRoyaltyInfo(); - assertEq(_royaltyRecipient, _defaultRoyaltyRecipient); - assertEq(_royaltyBps, _defaultRoyaltyBps); - } - - function test_mintWithSignature_reentrantRecipientContract() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.pricePerToken = 0; - _mintrequest.to = address(new ReentrantContract()); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - vm.expectRevert("ReentrancyGuard: reentrant call"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_mintWithSignature_nonZeroPrice_flatFee_exceedsTotalPrice() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - vm.startPrank(deployer); - tokenContract.setPlatformFeeType(IPlatformFee.PlatformFeeType.Flat); - tokenContract.setFlatPlatformFeeInfo(platformFeeRecipient, 100 ether); - vm.stopPrank(); - - _mintrequest.pricePerToken = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - uint256 totalPrice = (_mintrequest.pricePerToken * _mintrequest.quantity); - - // mint - vm.prank(caller); - vm.expectRevert("price less than platform fee"); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - function test_mintWithSignature_nonZeroPrice_flatFee() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - vm.prank(deployer); - tokenContract.setPlatformFeeType(IPlatformFee.PlatformFeeType.Flat); - - _mintrequest.pricePerToken = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - uint256 totalPrice = (_mintrequest.pricePerToken * _mintrequest.quantity); - - // mint - vm.prank(caller); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - - // check state after - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.balanceOf(_mintrequest.to, _tokenIdToMint), _mintrequest.quantity); - assertEq(tokenContract.uri(_tokenIdToMint), _mintrequest.uri); - assertEq(tokenContract.totalSupply(_tokenIdToMint), _mintrequest.quantity); - - (, uint256 _platformFee) = tokenContract.getFlatPlatformFeeInfo(); - uint256 defaultFee = (totalPrice * 100) / 10_000; - uint256 _saleProceeds = totalPrice - _platformFee - defaultFee; - assertEq(erc20.balanceOf(caller), 1000 ether - totalPrice); - assertEq(erc20.balanceOf(tokenContract.platformFeeRecipient()), _platformFee); - assertEq(erc20.balanceOf(tokenContract.primarySaleRecipient()), _saleProceeds); - assertEq(erc20.balanceOf(defaultFeeRecipient), defaultFee); - } - - modifier whenNotMaxTokenId() { - // pre-mint the first token (i.e. id 0), so that nextTokenIdToMint is 1, for this code path - vm.prank(deployer); - tokenContract.mintTo(deployer, type(uint256).max, "uri1", 10); - _; - } - - function test_mintWithSignature_nonZeroPrice_notMaxTokenId_invalidId() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - whenNotMaxTokenId - { - vm.prank(deployer); - tokenContract.setPlatformFeeType(IPlatformFee.PlatformFeeType.Flat); - - _mintrequest.pricePerToken = 10; - _mintrequest.currency = address(erc20); - _mintrequest.tokenId = 1; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - vm.expectRevert("invalid id"); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - modifier whenValidId() { - _; - } - - function test_mintWithSignature_nonZeroPrice_notMaxTokenId() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - whenNotMaxTokenId - whenValidId - { - vm.prank(deployer); - tokenContract.setPlatformFeeType(IPlatformFee.PlatformFeeType.Flat); - - _mintrequest.pricePerToken = 10; - _mintrequest.currency = address(erc20); - _mintrequest.tokenId = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint() - 1; - - uint256 totalPrice = (_mintrequest.pricePerToken * _mintrequest.quantity); - - // mint - vm.prank(caller); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - - // check state after - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.balanceOf(_mintrequest.to, _tokenIdToMint), _mintrequest.quantity); - assertEq(tokenContract.uri(_tokenIdToMint), "uri1"); - assertEq(tokenContract.totalSupply(_tokenIdToMint), _mintrequest.quantity + 10); - } -} diff --git a/src/test/tokenerc1155-BTT/mint-with-signature/mintWithSignature.tree b/src/test/tokenerc1155-BTT/mint-with-signature/mintWithSignature.tree deleted file mode 100644 index 115264aec..000000000 --- a/src/test/tokenerc1155-BTT/mint-with-signature/mintWithSignature.tree +++ /dev/null @@ -1,102 +0,0 @@ -mintWithSignature(MintRequest calldata _req, bytes calldata _signature) -// assuming _req.tokenId input is type(uint256).max and platform fee type is Bps -├── when signer doesn't have MINTER_ROLE -│ └── it should revert ✅ -└── when signer has MINTER_ROLE - └── when `_req.uid` has already been used - │ └── it should revert ✅ - └── when `_req.uid` has not been used - └── when `_req.validityStartTimestamp` is greater than block timestamp - │ └── it should revert ✅ - └── when `_req.validityStartTimestamp` is less than or equal to block timestamp - └── when `_req.validityEndTimestamp` is less than block timestamp - │ └── it should revert ✅ - └── when `_req.validityEndTimestamp` is greater than or equal to block timestamp - └── when `_req.to` is address(0) - │ └── it should revert ✅ - └── when `_req.to` is not address(0) - ├── when `_req.quantity` is zero - │ └── it should revert ✅ - └── when `_req.quantity` is not zero - │ - │ // case: price is zero - └── when `_req.pricePerToken` is zero - │ └── when msg.value is not zero - │ │ └── it should revert ✅ - │ └── when msg.value is zero - │ ├── when `_req.to` address is an EOA - │ │ └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - │ │ └── it should increment `nextTokenIdToMint` by 1 ✅ - │ │ └── it should mint the `_req.quantity` number of tokens to the `_req.to` address ✅ - │ │ └── it should increment totalSupply of tokenId by `_req.quantity` ✅ - │ │ └── it should set `_req.uid` as minted ✅ - │ │ └── it should set uri for minted tokenId equal to `_req.uri` ✅ - │ │ └── it should emit MetadataUpdate event ✅ - │ │ └── it should emit TokensMintedWithSignature event ✅ - │ └── when `_to` address is a contract - │ ├── when `_to` address is non ERC1155Receiver implementer - │ │ └── it should revert ✅ - │ └── when `_to` address implements ERC1155Receiver - │ └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - │ └── it should increment `nextTokenIdToMint` by 1 ✅ - │ └── it should mint the `_req.quantity` number of tokens to the `_req.to` address ✅ - │ └── it should increment totalSupply of tokenId by `_req.quantity` ✅ - │ └── it should set `_req.uid` as minted ✅ - │ └── it should set uri for minted tokenId equal to `_uri` ✅ - │ └── it should emit MetadataUpdate event ✅ - │ └── it should emit TokensMintedWithSignature event ✅ - │ - │ // case: price is not zero - └── when `_req.pricePerToken` is not zero - └── when currency is native token - │ └── when msg.value is not equal to total price - │ │ └── it should revert ✅ - │ └── when msg.value is equal to total price - │ └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - │ └── it should increment `nextTokenIdToMint` by 1 ✅ - │ └── it should mint the `_req.quantity` number of tokens to the `_req.to` address ✅ - │ └── it should increment totalSupply of tokenId by `_req.quantity` ✅ - │ └── it should set `_req.uid` as minted ✅ - │ └── it should set uri for minted tokenId equal to `_uri` ✅ - │ └── (transfer to sale recipient) ✅ - │ └── (transfer to fee recipient) ✅ - │ └── it should emit MetadataUpdate event ✅ - │ └── it should emit TokensMintedWithSignature event ✅ - └── when currency is some ERC20 token - └── when msg.value is not zero - │ └── it should revert ✅ - └── when msg.value is zero - └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - └── it should increment `nextTokenIdToMint` by 1 ✅ - └── it should mint the `_req.quantity` number of tokens to the `_req.to` address ✅ - └── it should increment totalSupply of tokenId by `_req.quantity` ✅ - └── it should set `_req.uid` as minted ✅ - └── it should set uri for minted tokenId equal to `_uri` ✅ - └── (transfer to sale recipient) ✅ - └── (transfer to fee recipient) ✅ - └── it should emit MetadataUpdate event ✅ - └── it should emit TokensMintedWithSignature event ✅ - -// other cases - -├── when `_req.royaltyRecipient` is not address(0) - │ └── it should set royaltyInfoForToken ✅ - └── when `_req.royaltyRecipient` is address(0) - └── it should use default royalty info ✅ - -├── when reentrant call - └── it should revert ✅ - -├── when platformFeeType is flat - └── when total price is less than platform fee - │ └── it should revert ✅ - └── when total price is greater than or equal to platform fee - └── (transfer to sale recipient) ✅ - └── (transfer to fee recipient) ✅ - -├── when tokenId input is greater than or equal to nextTokenIdToMint - └── it should revert ✅ -├── when tokenId input is less than nextTokenIdToMint - └── it should mint ✅ - - diff --git a/src/test/tokenerc1155-BTT/other-functions/other.t.sol b/src/test/tokenerc1155-BTT/other-functions/other.t.sol deleted file mode 100644 index c95c15dab..000000000 --- a/src/test/tokenerc1155-BTT/other-functions/other.t.sol +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; -import { IStaking1155 } from "contracts/extension/interface/IStaking1155.sol"; -import { IERC2981 } from "contracts/eip/interface/IERC2981.sol"; - -import "@openzeppelin/contracts-upgradeable/access/IAccessControlEnumerableUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; -import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 { - function canSetMetadata() public view returns (bool) { - return _canSetMetadata(); - } - - function canFreezeMetadata() public view returns (bool) { - return _canFreezeMetadata(); - } - - function beforeTokenTransfer( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) external { - _beforeTokenTransfer(operator, from, to, ids, amounts, data); - } - - function setTotalSupply(uint256 _tokenId, uint256 _totalSupply) external { - totalSupply[_tokenId] = _totalSupply; - } -} - -contract TokenERC1155Test_OtherFunctions is BaseTest { - address public implementation; - address public proxy; - - MyTokenERC1155 public tokenContract; - address internal caller; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - caller = getActor(3); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - } - - function test_contractType() public { - assertEq(tokenContract.contractType(), bytes32("TokenERC1155")); - } - - function test_contractVersion() public { - assertEq(tokenContract.contractVersion(), uint8(1)); - } - - function test_beforeTokenTransfer_restricted_notTransferRole() public { - uint256[] memory ids; - uint256[] memory amounts; - - vm.prank(deployer); - tokenContract.revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.expectRevert("restricted to TRANSFER_ROLE holders."); - tokenContract.beforeTokenTransfer(caller, caller, address(0x123), ids, amounts, ""); - } - - modifier whenTransferRole() { - vm.prank(deployer); - tokenContract.grantRole(keccak256("TRANSFER_ROLE"), caller); - _; - } - - function test_beforeTokenTransfer_restricted() public whenTransferRole { - uint256[] memory ids; - uint256[] memory amounts; - tokenContract.beforeTokenTransfer(caller, caller, address(0x123), ids, amounts, ""); - } - - function test_beforeTokenTransfer_restricted_fromZero() public whenTransferRole { - uint256[] memory ids = new uint256[](1); - uint256[] memory amounts = new uint256[](1); - uint256 _initialSupply = 100; - - ids[0] = 1; - amounts[0] = 10; - tokenContract.setTotalSupply(ids[0], _initialSupply); // mock set supply - - tokenContract.beforeTokenTransfer(caller, address(0), address(0x123), ids, amounts, ""); - - assertEq(tokenContract.totalSupply(ids[0]), amounts[0] + _initialSupply); - } - - function test_beforeTokenTransfer_restricted_toZero() public whenTransferRole { - uint256[] memory ids = new uint256[](1); - uint256[] memory amounts = new uint256[](1); - uint256 _initialSupply = 100; - - ids[0] = 1; - amounts[0] = 10; - tokenContract.setTotalSupply(ids[0], _initialSupply); // mock set supply - - tokenContract.beforeTokenTransfer(caller, caller, address(0), ids, amounts, ""); - - assertEq(tokenContract.totalSupply(ids[0]), _initialSupply - amounts[0]); - } - - function test_canSetMetadata_notMetadataRole() public { - assertFalse(tokenContract.canSetMetadata()); - } - - modifier whenMetadataRoleRole() { - _; - } - - function test_canSetMetadata() public whenMetadataRoleRole { - vm.prank(deployer); - assertTrue(tokenContract.canSetMetadata()); - } - - function test_canFreezeMetadata_notMetadataRole() public { - assertFalse(tokenContract.canFreezeMetadata()); - } - - function test_canFreezeMetadata() public whenMetadataRoleRole { - vm.prank(deployer); - assertTrue(tokenContract.canFreezeMetadata()); - } - - function test_supportsInterface() public { - assertTrue(tokenContract.supportsInterface(type(IERC2981).interfaceId)); - assertTrue(tokenContract.supportsInterface(type(IERC165).interfaceId)); - assertTrue(tokenContract.supportsInterface(type(IERC165Upgradeable).interfaceId)); - assertTrue(tokenContract.supportsInterface(type(IAccessControlEnumerableUpgradeable).interfaceId)); - assertTrue(tokenContract.supportsInterface(type(IAccessControlUpgradeable).interfaceId)); - assertTrue(tokenContract.supportsInterface(type(IERC1155Upgradeable).interfaceId)); - - // false for other not supported interfaces - assertFalse(tokenContract.supportsInterface(type(IStaking1155).interfaceId)); - } -} diff --git a/src/test/tokenerc1155-BTT/other-functions/other.tree b/src/test/tokenerc1155-BTT/other-functions/other.tree deleted file mode 100644 index 6af7d78cf..000000000 --- a/src/test/tokenerc1155-BTT/other-functions/other.tree +++ /dev/null @@ -1,37 +0,0 @@ -contractType() -├── it should return bytes32("TokenERC1155") ✅ - -contractVersion() -├── it should return uint8(1) ✅ - -_beforeTokenTransfers( - address operator, - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data -) -├── when transfers are restricted (i.e. address(0) doesn't have transfer role, or from-to addresses are not address(0) - └── when from and to don't have transfer role - │ └── it should revert ✅ - └── when from is address(0) - │ └── it should increase totalSupply of `ids` by `amounts` ✅ - └── when to is address(0) - └── it should decrease totalSupply of `ids` by `amounts` ✅ - -_canSetMetadata() -├── when the caller doesn't have METADATA_ROLE -│ └── it should revert ✅ -└── when the caller has METADATA_ROLE - └── it should return true ✅ - -_canFreezeMetadata() -├── when the caller doesn't have METADATA_ROLE -│ └── it should revert ✅ -└── when the caller has METADATA_ROLE - └── it should return true ✅ - -supportsInterface(bytes4 interfaceId) -├── it should return true for supported interface ✅ -├── it should return false for not supported interface ✅ diff --git a/src/test/tokenerc1155-BTT/owner/owner.t.sol b/src/test/tokenerc1155-BTT/owner/owner.t.sol deleted file mode 100644 index 0615f32c4..000000000 --- a/src/test/tokenerc1155-BTT/owner/owner.t.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract TokenERC1155Test_Owner is BaseTest { - address public implementation; - address public proxy; - - MyTokenERC1155 internal tokenContract; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - } - - function test_owner() public { - assertEq(tokenContract.owner(), deployer); - } - - function test_owner_notDefaultAdmin() public { - vm.prank(deployer); - tokenContract.renounceRole(bytes32(0x00), deployer); - - assertEq(tokenContract.owner(), address(0)); - } -} diff --git a/src/test/tokenerc1155-BTT/owner/owner.tree b/src/test/tokenerc1155-BTT/owner/owner.tree deleted file mode 100644 index 576cfcb91..000000000 --- a/src/test/tokenerc1155-BTT/owner/owner.tree +++ /dev/null @@ -1,6 +0,0 @@ -owner() -├── when private variable `_owner` DEFAULT_ADMIN_ROLE -│ └── it should return `_owner` ✅ -└── when private variable `_owner` doesn't have DEFAULT_ADMIN_ROLE - └── it should return address(0) ✅ - diff --git a/src/test/tokenerc1155-BTT/set-contract-uri/setContractURI.t.sol b/src/test/tokenerc1155-BTT/set-contract-uri/setContractURI.t.sol deleted file mode 100644 index 4f3739103..000000000 --- a/src/test/tokenerc1155-BTT/set-contract-uri/setContractURI.t.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract TokenERC1155Test_SetContractURI is BaseTest { - address public implementation; - address public proxy; - address internal caller; - string internal _contractURI; - - MyTokenERC1155 internal tokenContract; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - - caller = getActor(1); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - _contractURI = "ipfs://contracturi"; - } - - function test_setContractURI_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setContractURI(_contractURI); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setContractURI_empty() public whenCallerAuthorized { - vm.prank(address(caller)); - tokenContract.setContractURI(""); - - // get contract uri - assertEq(tokenContract.contractURI(), ""); - } - - function test_setContractURI_notEmpty() public whenCallerAuthorized { - vm.prank(address(caller)); - tokenContract.setContractURI(_contractURI); - - // get contract uri - assertEq(tokenContract.contractURI(), _contractURI); - } -} diff --git a/src/test/tokenerc1155-BTT/set-contract-uri/setContractURI.tree b/src/test/tokenerc1155-BTT/set-contract-uri/setContractURI.tree deleted file mode 100644 index 8fc480b19..000000000 --- a/src/test/tokenerc1155-BTT/set-contract-uri/setContractURI.tree +++ /dev/null @@ -1,8 +0,0 @@ -setContractURI(string calldata _uri) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── when `uri` is empty - │ └── it should update contract URI to empty string ✅ - └── when `uri` is not empty - └── it should update contract URI to `_uri` ✅ \ No newline at end of file diff --git a/src/test/tokenerc1155-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol b/src/test/tokenerc1155-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol deleted file mode 100644 index 552d9d75b..000000000 --- a/src/test/tokenerc1155-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract TokenERC1155Test_SetDefaultRoyaltyInfo is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal defaultRoyaltyRecipient; - uint256 internal defaultRoyaltyBps; - - MyTokenERC1155 internal tokenContract; - - event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - - caller = getActor(1); - defaultRoyaltyRecipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - } - - function test_setDefaultRoyaltyInfo_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setDefaultRoyaltyInfo_exceedMaxBps() public whenCallerAuthorized { - defaultRoyaltyBps = 10_001; - vm.prank(address(caller)); - vm.expectRevert("exceed royalty bps"); - tokenContract.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } - - modifier whenNotExceedMaxBps() { - defaultRoyaltyBps = 500; - _; - } - - function test_setDefaultRoyaltyInfo() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - tokenContract.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - - // get default royalty info - (address _recipient, uint16 _royaltyBps) = tokenContract.getDefaultRoyaltyInfo(); - assertEq(_recipient, defaultRoyaltyRecipient); - assertEq(_royaltyBps, uint16(defaultRoyaltyBps)); - - // get royalty info for token - uint256 tokenId = 0; - (_recipient, _royaltyBps) = tokenContract.getRoyaltyInfoForToken(tokenId); - assertEq(_recipient, defaultRoyaltyRecipient); - assertEq(_royaltyBps, uint16(defaultRoyaltyBps)); - - // royaltyInfo - ERC2981 - uint256 salePrice = 1000; - (address _royaltyRecipient, uint256 _royaltyAmount) = tokenContract.royaltyInfo(tokenId, salePrice); - assertEq(_royaltyRecipient, defaultRoyaltyRecipient); - assertEq(_royaltyAmount, (salePrice * defaultRoyaltyBps) / 10_000); - } - - function test_setDefaultRoyaltyInfo_event() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - vm.expectEmit(true, false, false, true); - emit DefaultRoyalty(defaultRoyaltyRecipient, defaultRoyaltyBps); - tokenContract.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } -} diff --git a/src/test/tokenerc1155-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.tree b/src/test/tokenerc1155-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.tree deleted file mode 100644 index 78a4312de..000000000 --- a/src/test/tokenerc1155-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.tree +++ /dev/null @@ -1,11 +0,0 @@ -setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when royalty bps input is greater than MAX_BPS - │ └── it should revert ✅ - └── when royalty bps input is less than or equal to MAX_BPS - └── it should update default royalty recipient ✅ - └── it should update default royalty bps ✅ - └── it should calculate royalty amount for a token-id based on default royalty info ✅ - └── it should emit DefaultRoyalty event ✅ \ No newline at end of file diff --git a/src/test/tokenerc1155-BTT/set-flat-platform-fee-info/setFlatPlatformFeeInfo.t.sol b/src/test/tokenerc1155-BTT/set-flat-platform-fee-info/setFlatPlatformFeeInfo.t.sol deleted file mode 100644 index 380d65921..000000000 --- a/src/test/tokenerc1155-BTT/set-flat-platform-fee-info/setFlatPlatformFeeInfo.t.sol +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract TokenERC1155Test_SetFlatPlatformFeeInfo is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal _platformFeeRecipient; - uint256 internal _flatFee; - - MyTokenERC1155 internal tokenContract; - - event FlatPlatformFeeUpdated(address platformFeeRecipient, uint256 flatFee); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - - caller = getActor(1); - _platformFeeRecipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - _flatFee = 25; - } - - function test_setFlatPlatformFeeInfo_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setFlatPlatformFeeInfo() public whenCallerAuthorized { - vm.prank(address(caller)); - tokenContract.setFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee); - - // get platform fee info - (address _recipient, uint256 _fee) = tokenContract.getFlatPlatformFeeInfo(); - assertEq(_recipient, _platformFeeRecipient); - assertEq(_fee, _flatFee); - assertEq(tokenContract.platformFeeRecipient(), _platformFeeRecipient); - } - - function test_setFlatPlatformFeeInfo_event() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectEmit(false, false, false, true); - emit FlatPlatformFeeUpdated(_platformFeeRecipient, _flatFee); - tokenContract.setFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee); - } -} diff --git a/src/test/tokenerc1155-BTT/set-flat-platform-fee-info/setFlatPlatformFeeInfo.tree b/src/test/tokenerc1155-BTT/set-flat-platform-fee-info/setFlatPlatformFeeInfo.tree deleted file mode 100644 index 95bfe1f2d..000000000 --- a/src/test/tokenerc1155-BTT/set-flat-platform-fee-info/setFlatPlatformFeeInfo.tree +++ /dev/null @@ -1,8 +0,0 @@ -setFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── when `_platformFeeBps` is less than or equal to MAX_BPS - └── it should update platform fee recipient ✅ - └── it should update flatPlatformFee ✅ - └── it should emit FlatPlatformFeeUpdated event ✅ \ No newline at end of file diff --git a/src/test/tokenerc1155-BTT/set-owner/setOwner.t.sol b/src/test/tokenerc1155-BTT/set-owner/setOwner.t.sol deleted file mode 100644 index 00dd0f2ef..000000000 --- a/src/test/tokenerc1155-BTT/set-owner/setOwner.t.sol +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract TokenERC1155Test_SetOwner is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal _newOwner; - - MyTokenERC1155 internal tokenContract; - - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - - caller = getActor(1); - _newOwner = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - } - - function test_setOwner_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setOwner(_newOwner); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setOwner_newOwnerNotAdmin() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectRevert("new owner not module admin."); - tokenContract.setOwner(_newOwner); - } - - modifier whenNewOwnerIsAnAdmin() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), _newOwner); - _; - } - - function test_setOwner() public whenCallerAuthorized whenNewOwnerIsAnAdmin { - vm.prank(address(caller)); - tokenContract.setOwner(_newOwner); - - assertEq(tokenContract.owner(), _newOwner); - } - - function test_setOwner_event() public whenCallerAuthorized whenNewOwnerIsAnAdmin { - vm.prank(address(caller)); - vm.expectEmit(true, true, false, false); - emit OwnerUpdated(deployer, _newOwner); - tokenContract.setOwner(_newOwner); - } -} diff --git a/src/test/tokenerc1155-BTT/set-owner/setOwner.tree b/src/test/tokenerc1155-BTT/set-owner/setOwner.tree deleted file mode 100644 index 964e97cac..000000000 --- a/src/test/tokenerc1155-BTT/set-owner/setOwner.tree +++ /dev/null @@ -1,9 +0,0 @@ -setOwner(address _newOwner) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── when incoming `_owner` doesn't have DEFAULT_ADMIN_ROLE - │ └── it should revert ✅ - └── when incoming `_owner` has DEFAULT_ADMIN_ROLE - └── it should update owner ✅ - └── it should emit OwnerUpdated event ✅ \ No newline at end of file diff --git a/src/test/tokenerc1155-BTT/set-platform-fee-info/setPlatformFeeInfo.t.sol b/src/test/tokenerc1155-BTT/set-platform-fee-info/setPlatformFeeInfo.t.sol deleted file mode 100644 index e52402a03..000000000 --- a/src/test/tokenerc1155-BTT/set-platform-fee-info/setPlatformFeeInfo.t.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract TokenERC1155Test_SetPlatformFeeInfo is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal _platformFeeRecipient; - uint256 internal _platformFeeBps; - - MyTokenERC1155 internal tokenContract; - - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - - caller = getActor(1); - _platformFeeRecipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - } - - function test_setPlatformFeeInfo_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setPlatformFeeInfo_exceedMaxBps() public whenCallerAuthorized { - _platformFeeBps = 10_001; - vm.prank(address(caller)); - vm.expectRevert("exceeds MAX_BPS"); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - modifier whenNotExceedMaxBps() { - _platformFeeBps = 500; - _; - } - - function test_setPlatformFeeInfo() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - - // get platform fee info - (address _recipient, uint16 _bps) = tokenContract.getPlatformFeeInfo(); - assertEq(_recipient, _platformFeeRecipient); - assertEq(_bps, uint16(_platformFeeBps)); - assertEq(tokenContract.platformFeeRecipient(), _platformFeeRecipient); - } - - function test_setPlatformFeeInfo_event() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - vm.expectEmit(true, false, false, true); - emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } -} diff --git a/src/test/tokenerc1155-BTT/set-platform-fee-info/setPlatformFeeInfo.tree b/src/test/tokenerc1155-BTT/set-platform-fee-info/setPlatformFeeInfo.tree deleted file mode 100644 index dcef9965e..000000000 --- a/src/test/tokenerc1155-BTT/set-platform-fee-info/setPlatformFeeInfo.tree +++ /dev/null @@ -1,10 +0,0 @@ -setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when `_platformFeeBps` is greater than MAX_BPS - │ └── it should revert ✅ - └── when `_platformFeeBps` is less than or equal to MAX_BPS - └── it should update platform fee recipient ✅ - └── it should update platform fee bps ✅ - └── it should emit PlatformFeeInfoUpdated event ✅ \ No newline at end of file diff --git a/src/test/tokenerc1155-BTT/set-platform-fee-type/setPlatformFeeType.t.sol b/src/test/tokenerc1155-BTT/set-platform-fee-type/setPlatformFeeType.t.sol deleted file mode 100644 index db96dd310..000000000 --- a/src/test/tokenerc1155-BTT/set-platform-fee-type/setPlatformFeeType.t.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { IPlatformFee } from "contracts/extension/interface/IPlatformFee.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract TokenERC1155Test_SetPlatformFeeType is BaseTest { - address public implementation; - address public proxy; - address internal caller; - IPlatformFee.PlatformFeeType internal _newFeeType; - - MyTokenERC1155 internal tokenContract; - - event PlatformFeeTypeUpdated(IPlatformFee.PlatformFeeType feeType); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - - caller = getActor(1); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - _newFeeType = IPlatformFee.PlatformFeeType.Flat; - } - - function test_setPlatformFeeType_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setPlatformFeeType(_newFeeType); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setPlatformFeeType() public whenCallerAuthorized { - vm.prank(address(caller)); - tokenContract.setPlatformFeeType(_newFeeType); - - assertEq(uint8(tokenContract.getPlatformFeeType()), uint8(_newFeeType)); - } - - function test_setPlatformFeeType_event() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectEmit(false, false, false, true); - emit PlatformFeeTypeUpdated(_newFeeType); - tokenContract.setPlatformFeeType(_newFeeType); - } -} diff --git a/src/test/tokenerc1155-BTT/set-platform-fee-type/setPlatformFeeType.tree b/src/test/tokenerc1155-BTT/set-platform-fee-type/setPlatformFeeType.tree deleted file mode 100644 index e25a6bd4c..000000000 --- a/src/test/tokenerc1155-BTT/set-platform-fee-type/setPlatformFeeType.tree +++ /dev/null @@ -1,6 +0,0 @@ -setPlatformFeeType(PlatformFeeType _feeType) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── it should update platformFeeType ✅ - └── it should emit PlatformFeeTypeUpdated event ✅ \ No newline at end of file diff --git a/src/test/tokenerc1155-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.t.sol b/src/test/tokenerc1155-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.t.sol deleted file mode 100644 index 2f838241e..000000000 --- a/src/test/tokenerc1155-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.t.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract TokenERC1155Test_SetPrimarySaleRecipient is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal _primarySaleRecipient; - - MyTokenERC1155 internal tokenContract; - - event PrimarySaleRecipientUpdated(address indexed recipient); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - - caller = getActor(1); - _primarySaleRecipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - } - - function test_setPrimarySaleRecipient_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setPrimarySaleRecipient() public whenCallerAuthorized { - vm.prank(address(caller)); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - - // get primary sale recipient info - assertEq(tokenContract.primarySaleRecipient(), _primarySaleRecipient); - } - - function test_setPrimarySaleRecipient_event() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectEmit(true, false, false, false); - emit PrimarySaleRecipientUpdated(_primarySaleRecipient); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - } -} diff --git a/src/test/tokenerc1155-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.tree b/src/test/tokenerc1155-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.tree deleted file mode 100644 index 230035a07..000000000 --- a/src/test/tokenerc1155-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.tree +++ /dev/null @@ -1,6 +0,0 @@ -setPrimarySaleRecipient(address _saleRecipient) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── it should update primary sale recipient ✅ - └── it should emit PrimarySaleRecipientUpdated event ✅ \ No newline at end of file diff --git a/src/test/tokenerc1155-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol b/src/test/tokenerc1155-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol deleted file mode 100644 index 051dd7918..000000000 --- a/src/test/tokenerc1155-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract TokenERC1155Test_SetRoyaltyInfoForToken is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal defaultRoyaltyRecipient; - uint256 internal defaultRoyaltyBps; - - MyTokenERC1155 internal tokenContract; - - address internal royaltyRecipientForToken; - uint256 internal royaltyBpsForToken; - uint256 internal tokenId; - - event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - - caller = getActor(1); - defaultRoyaltyRecipient = getActor(2); - royaltyRecipientForToken = getActor(3); - defaultRoyaltyBps = 500; - tokenId = 1; - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - - vm.prank(deployer); - tokenContract.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } - - function test_setRoyaltyInfoForToken_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setRoyaltyInfoForToken_exceedMaxBps() public whenCallerAuthorized { - royaltyBpsForToken = 10_001; - vm.prank(address(caller)); - vm.expectRevert("exceed royalty bps"); - tokenContract.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - } - - modifier whenNotExceedMaxBps() { - royaltyBpsForToken = 1000; - _; - } - - function test_setRoyaltyInfoForToken() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - tokenContract.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - - // get default royalty info - (address _defaultRecipient, uint16 _defaultRoyaltyBps) = tokenContract.getDefaultRoyaltyInfo(); - assertEq(_defaultRecipient, defaultRoyaltyRecipient); - assertEq(_defaultRoyaltyBps, uint16(defaultRoyaltyBps)); - - // get royalty info for token - (address _royaltyRecipientForToken, uint16 _royaltyBpsForToken) = tokenContract.getRoyaltyInfoForToken(tokenId); - assertEq(_royaltyRecipientForToken, royaltyRecipientForToken); - assertEq(_royaltyBpsForToken, uint16(royaltyBpsForToken)); - - // royaltyInfo - ERC2981: calculate for default - uint256 salePrice = 1000; - (address _royaltyRecipient, uint256 _royaltyAmount) = tokenContract.royaltyInfo(0, salePrice); - assertEq(_royaltyRecipient, defaultRoyaltyRecipient); - assertEq(_royaltyAmount, (salePrice * defaultRoyaltyBps) / 10_000); - - // royaltyInfo - ERC2981: calculate for specific tokenId we set the royalty info for - (_royaltyRecipient, _royaltyAmount) = tokenContract.royaltyInfo(tokenId, salePrice); - assertEq(_royaltyRecipient, royaltyRecipientForToken); - assertEq(_royaltyAmount, (salePrice * royaltyBpsForToken) / 10_000); - } - - function test_setRoyaltyInfoForToken_event() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - vm.expectEmit(true, true, false, true); - emit RoyaltyForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - tokenContract.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - } -} diff --git a/src/test/tokenerc1155-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.tree b/src/test/tokenerc1155-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.tree deleted file mode 100644 index cada076de..000000000 --- a/src/test/tokenerc1155-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.tree +++ /dev/null @@ -1,15 +0,0 @@ -function setRoyaltyInfoForToken( - uint256 _tokenId, - address _recipient, - uint256 _bps -) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when royalty bps input is greater than MAX_BPS - │ └── it should revert ✅ - └── when royalty bps input is less than or equal to MAX_BPS - └── it should update default royalty recipient ✅ - └── it should update default royalty bps ✅ - └── it should calculate royalty amount for a token-id based on default royalty info ✅ - └── it should emit RoyaltyForToken event ✅ \ No newline at end of file diff --git a/src/test/tokenerc1155-BTT/uri/tokenURI.t.sol b/src/test/tokenerc1155-BTT/uri/tokenURI.t.sol deleted file mode 100644 index 1a2feb0a2..000000000 --- a/src/test/tokenerc1155-BTT/uri/tokenURI.t.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 {} - -contract TokenERC1155Test_Uri is BaseTest { - address public implementation; - address public proxy; - - MyTokenERC1155 internal tokenContract; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - } - - function test_uri() public { - uint256 _tokenId = 1; - string memory _uri = "ipfs://uri/1"; - - vm.prank(deployer); - tokenContract.setTokenURI(_tokenId, _uri); - - assertEq(tokenContract.uri(_tokenId), _uri); - } -} diff --git a/src/test/tokenerc1155-BTT/uri/tokenURI.tree b/src/test/tokenerc1155-BTT/uri/tokenURI.tree deleted file mode 100644 index 2df0b55ed..000000000 --- a/src/test/tokenerc1155-BTT/uri/tokenURI.tree +++ /dev/null @@ -1,3 +0,0 @@ -uri(uint256 _tokenId) -├── it should return uri associated with the given `_tokenId` ✅ - diff --git a/src/test/tokenerc1155-BTT/verify/verify.t.sol b/src/test/tokenerc1155-BTT/verify/verify.t.sol deleted file mode 100644 index f584f57a8..000000000 --- a/src/test/tokenerc1155-BTT/verify/verify.t.sol +++ /dev/null @@ -1,157 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC1155 is TokenERC1155 { - function setMintedURI(MintRequest calldata _req, bytes calldata _signature) external { - verifyRequest(_req, _signature); - } -} - -contract TokenERC1155Test_Verify is BaseTest { - address public implementation; - address public proxy; - - MyTokenERC1155 internal tokenContract; - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - TokenERC1155.MintRequest _mintrequest; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC1155()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC1155(proxy); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,uint256 tokenId,string uri,uint256 quantity,uint256 pricePerToken,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("TokenERC1155")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) - ); - - // construct default mintrequest - _mintrequest.to = address(0x1234); - _mintrequest.royaltyRecipient = royaltyRecipient; - _mintrequest.royaltyBps = royaltyBps; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.tokenId = type(uint256).max; - _mintrequest.uri = "ipfs://"; - _mintrequest.quantity = 100; - _mintrequest.pricePerToken = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 0; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - } - - function signMintRequest( - TokenERC1155.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = bytes.concat( - abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - _request.tokenId, - keccak256(bytes(_request.uri)) - ), - abi.encode( - _request.quantity, - _request.pricePerToken, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ) - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - function test_verify_notMinterRole() public { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - (bool _isValid, address _recoveredSigner) = tokenContract.verify(_mintrequest, _signature); - - assertFalse(_isValid); - assertEq(_recoveredSigner, signer); - } - - modifier whenMinterRole() { - vm.prank(deployer); - tokenContract.grantRole(keccak256("MINTER_ROLE"), signer); - _; - } - - function test_verify_invalidUID() public whenMinterRole { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // set state with this mintrequest and signature, marking the UID as used - tokenContract.setMintedURI(_mintrequest, _signature); - - // pass the same UID mintrequest again - (bool _isValid, address _recoveredSigner) = tokenContract.verify(_mintrequest, _signature); - - assertFalse(_isValid); - assertEq(_recoveredSigner, signer); - } - - modifier whenUidNotUsed() { - _; - } - - function test_verify() public whenMinterRole whenUidNotUsed { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - (bool _isValid, address _recoveredSigner) = tokenContract.verify(_mintrequest, _signature); - - assertTrue(_isValid); - assertEq(_recoveredSigner, signer); - } -} diff --git a/src/test/tokenerc1155-BTT/verify/verify.tree b/src/test/tokenerc1155-BTT/verify/verify.tree deleted file mode 100644 index c160faa0f..000000000 --- a/src/test/tokenerc1155-BTT/verify/verify.tree +++ /dev/null @@ -1,12 +0,0 @@ -verify(MintRequest calldata _req, bytes calldata _signature) -├── when signer doesn't have MINTER_ROLE -│ └── it should return false ✅ -│ └── it should return recovered signer equal to the actual signer of the request ✅ -└── when signer has MINTER_ROLE - └── when `_req.uid` has already been used - │ └── it should return false ✅ - │ └── it should return recovered signer equal to the actual signer of the request ✅ - └── when `_req.uid` has not been used - └── it should return true ✅ - └── it should return recovered signer equal to the actual signer of the request ✅ - diff --git a/src/test/tokenerc20-BTT/initialize/initialize.t.sol b/src/test/tokenerc20-BTT/initialize/initialize.t.sol deleted file mode 100644 index 6dae4df15..000000000 --- a/src/test/tokenerc20-BTT/initialize/initialize.t.sol +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC20 is TokenERC20 { - function eip712NameHash() external view returns (bytes32) { - return _EIP712NameHash(); - } - - function eip712VersionHash() external view returns (bytes32) { - return _EIP712VersionHash(); - } -} - -contract TokenERC20Test_Initialize is BaseTest { - address public implementation; - address public proxy; - - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC20()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ) - ); - } - - function test_initialize_initializingImplementation() public { - vm.expectRevert("Initializable: contract is already initialized"); - TokenERC20(implementation).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ); - } - - modifier whenNotImplementation() { - _; - } - - function test_initialize_proxyAlreadyInitialized() public whenNotImplementation { - vm.expectRevert("Initializable: contract is already initialized"); - MyTokenERC20(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ); - } - - modifier whenProxyNotInitialized() { - proxy = address(new TWProxy(implementation, "")); - _; - } - - function test_initialize_exceedsMaxBps() public whenNotImplementation whenProxyNotInitialized { - vm.expectRevert("exceeds MAX_BPS"); - MyTokenERC20(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - uint128(MAX_BPS) + 1 // platformFeeBps greater than MAX_BPS - ); - } - - modifier whenPlatformFeeBpsWithinMaxBps() { - _; - } - - function test_initialize() public whenNotImplementation whenProxyNotInitialized whenPlatformFeeBpsWithinMaxBps { - MyTokenERC20(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ); - - // check state - MyTokenERC20 tokenContract = MyTokenERC20(proxy); - - assertEq(tokenContract.eip712NameHash(), keccak256(bytes(NAME))); - assertEq(tokenContract.eip712VersionHash(), keccak256(bytes("1"))); - - address[] memory _trustedForwarders = forwarders(); - for (uint256 i = 0; i < _trustedForwarders.length; i++) { - assertTrue(tokenContract.isTrustedForwarder(_trustedForwarders[i])); - } - - assertEq(tokenContract.name(), NAME); - assertEq(tokenContract.symbol(), SYMBOL); - assertEq(tokenContract.contractURI(), CONTRACT_URI); - - (address _platformFeeRecipient, uint16 _platformFeeBps) = tokenContract.getPlatformFeeInfo(); - assertEq(_platformFeeBps, platformFeeBps); - assertEq(_platformFeeRecipient, platformFeeRecipient); - - assertEq(tokenContract.primarySaleRecipient(), saleRecipient); - - assertTrue(tokenContract.hasRole(bytes32(0x00), deployer)); - assertTrue(tokenContract.hasRole(keccak256("TRANSFER_ROLE"), deployer)); - assertTrue(tokenContract.hasRole(keccak256("TRANSFER_ROLE"), address(0))); - assertTrue(tokenContract.hasRole(keccak256("MINTER_ROLE"), deployer)); - } - - function test_initialize_event_RoleGranted_DefaultAdmin() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _defaultAdminRole = bytes32(0x00); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_defaultAdminRole, deployer, deployer); - MyTokenERC20(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ); - } - - function test_initialize_event_RoleGranted_MinterRole() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _minterRole = keccak256("MINTER_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_minterRole, deployer, deployer); - MyTokenERC20(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ); - } - - function test_initialize_event_RoleGranted_TransferRole() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _transferRole = keccak256("TRANSFER_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_transferRole, deployer, deployer); - MyTokenERC20(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ); - } - - function test_initialize_event_RoleGranted_TransferRole_AddressZero() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _transferRole = keccak256("TRANSFER_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_transferRole, address(0), deployer); - MyTokenERC20(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ); - } -} diff --git a/src/test/tokenerc20-BTT/initialize/initialize.tree b/src/test/tokenerc20-BTT/initialize/initialize.tree deleted file mode 100644 index a3ead7790..000000000 --- a/src/test/tokenerc20-BTT/initialize/initialize.tree +++ /dev/null @@ -1,34 +0,0 @@ -initialize( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _primarySaleRecipient, - address _platformFeeRecipient - uint256 _platformFeeBps, -) -├── when initializing the implementation contract (not proxy) -│ └── it should revert ✅ -└── when it is a proxy to the implementation - └── when it is already initialized - │ └── it should revert ✅ - └── when it is not initialized - └── when platformFeeBps is greater than MAX_BPS - │ └── it should revert ✅ - └── when platformFeeBps is less than or equal to MAX_BPS - └── it should correctly set EIP712 name hash and version hash ✅ - └── it should set trustedForwarder mapping to true for all addresses in `_trustedForwarders` ✅ - └── it should set _name and _symbol to `_name` and `_symbol` param values respectively ✅ - └── it should set contractURI to `_contractURI` param value ✅ - └── it should set platformFeeRecipient and platformFeeBps as `_platformFeeRecipient` and `_platformFeeBps` respectively ✅ - └── it should set primary sale recipient as `_saleRecipient` param value ✅ - └── it should grant 0x00 (DEFAULT_ADMIN_ROLE) to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should grant MINTER_ROLE to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should grant TRANSFER_ROLE to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should grant TRANSFER_ROLE to address(0) ✅ - └── it should emit RoleGranted event ✅ - diff --git a/src/test/tokenerc20-BTT/mint-to/mintTo.t.sol b/src/test/tokenerc20-BTT/mint-to/mintTo.t.sol deleted file mode 100644 index ad5095640..000000000 --- a/src/test/tokenerc20-BTT/mint-to/mintTo.t.sol +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC20 is TokenERC20 {} - -contract TokenERC20Test_MintTo is BaseTest { - address public implementation; - address public proxy; - address public caller; - address public recipient; - uint256 public amount; - - MyTokenERC20 internal tokenContract; - - event TokensMinted(address indexed mintedTo, uint256 quantityMinted); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC20()); - caller = getActor(1); - recipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ) - ); - - tokenContract = MyTokenERC20(proxy); - amount = 100; - } - - function test_mintTo_notMinterRole() public { - vm.prank(caller); - vm.expectRevert("not minter."); - tokenContract.mintTo(recipient, amount); - } - - modifier whenMinterRole() { - vm.prank(deployer); - tokenContract.grantRole(keccak256("MINTER_ROLE"), caller); - _; - } - - function test_mintTo() public whenMinterRole { - // mint - vm.prank(caller); - tokenContract.mintTo(recipient, amount); - - // check state after - assertEq(tokenContract.balanceOf(recipient), amount); - } - - function test_mintTo_TokensMintedEvent() public whenMinterRole { - vm.prank(caller); - vm.expectEmit(true, false, false, true); - emit TokensMinted(recipient, amount); - tokenContract.mintTo(recipient, amount); - } -} diff --git a/src/test/tokenerc20-BTT/mint-to/mintTo.tree b/src/test/tokenerc20-BTT/mint-to/mintTo.tree deleted file mode 100644 index 33bb14c7e..000000000 --- a/src/test/tokenerc20-BTT/mint-to/mintTo.tree +++ /dev/null @@ -1,7 +0,0 @@ -mintTo(address to, uint256 amount) -├── when caller doesn't have MINTER_ROLE - │ └── it should revert ✅ - └── when caller has MINTER_ROLE - └── it should mint `amount` to `to` ✅ - └── it should emit TokensMinted event ✅ - diff --git a/src/test/tokenerc20-BTT/mint-with-signature/mintWithSignature.t.sol b/src/test/tokenerc20-BTT/mint-with-signature/mintWithSignature.t.sol deleted file mode 100644 index bbe988eb8..000000000 --- a/src/test/tokenerc20-BTT/mint-with-signature/mintWithSignature.t.sol +++ /dev/null @@ -1,449 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC20 is TokenERC20 { - function setMintedUID(MintRequest calldata _req, bytes calldata _signature) external { - verifyRequest(_req, _signature); - } -} - -contract ReentrantContract { - fallback() external payable { - TokenERC20.MintRequest memory _mintrequest; - bytes memory _signature; - MyTokenERC20(msg.sender).mintWithSignature(_mintrequest, _signature); - } -} - -contract TokenERC20Test_MintWithSignature is BaseTest { - address public implementation; - address public proxy; - address public caller; - address public recipient; - - MyTokenERC20 internal tokenContract; - - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - TokenERC20.MintRequest _mintrequest; - - event TokensMintedWithSignature( - address indexed signer, - address indexed mintedTo, - TokenERC20.MintRequest mintRequest - ); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC20()); - caller = getActor(1); - recipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ) - ); - - tokenContract = MyTokenERC20(proxy); - - typehashMintRequest = keccak256( - "MintRequest(address to,address primarySaleRecipient,uint256 quantity,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes(NAME)); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) - ); - - // construct default mintrequest - _mintrequest.to = recipient; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.quantity = 100; - _mintrequest.price = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 0; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - erc20.mint(caller, 1_000 ether); - vm.deal(caller, 1_000 ether); - - vm.startPrank(deployer); - erc20.approve(address(tokenContract), type(uint256).max); - vm.stopPrank(); - - vm.startPrank(caller); - erc20.approve(address(tokenContract), type(uint256).max); - vm.stopPrank(); - } - - function signMintRequest( - TokenERC20.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.primarySaleRecipient, - _request.quantity, - _request.price, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - function test_mintWithSignature_notMinterRole() public { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - vm.expectRevert("invalid signature"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenMinterRole() { - vm.prank(deployer); - tokenContract.grantRole(keccak256("MINTER_ROLE"), signer); - _; - } - - function test_mintWithSignature_invalidUID() public whenMinterRole { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // set state with this mintrequest and signature, marking the UID as used - tokenContract.setMintedUID(_mintrequest, _signature); - - // pass the same UID mintrequest again - vm.prank(caller); - vm.expectRevert("invalid signature"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenUidNotUsed() { - _; - } - - function test_mintWithSignature_invalidStartTimestamp() public whenMinterRole whenUidNotUsed { - _mintrequest.validityStartTimestamp = uint128(block.timestamp + 1); - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("request expired"); - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenValidStartTimestamp() { - _; - } - - function test_mintWithSignature_invalidEndTimestamp() public whenMinterRole whenUidNotUsed whenValidStartTimestamp { - _mintrequest.validityEndTimestamp = uint128(block.timestamp - 1); - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("request expired"); - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenValidEndTimestamp() { - _; - } - - function test_mintWithSignature_recipientAddressZero() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - { - _mintrequest.to = address(0); - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("recipient undefined"); - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenRecipientAddressNotZero() { - _; - } - - function test_mintWithSignature_zeroQuantity() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - { - _mintrequest.quantity = 0; - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("zero quantity"); - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenNotZeroQuantity() { - _mintrequest.quantity = 100; - _; - } - - // ================== - // ======= Test branch: when mint price is zero - // ================== - - function test_mintWithSignature_zeroPrice_msgValueNonZero() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - { - _mintrequest.price = 0; - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("!Value"); - vm.prank(caller); - tokenContract.mintWithSignature{ value: 1 }(_mintrequest, _signature); - } - - modifier whenMsgValueZero() { - _; - } - - function test_mintWithSignature_zeroPrice() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.price = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // mint - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after - assertEq(tokenContract.balanceOf(recipient), _mintrequest.quantity); - } - - function test_mintWithSignature_zeroPrice_TokensMintedWithSignatureEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.price = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - vm.expectEmit(true, true, false, true); - emit TokensMintedWithSignature(signer, _mintrequest.to, _mintrequest); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - // ================== - // ======= Test branch: when mint price is not zero - // ================== - - function test_mintWithSignature_nonZeroPrice_nativeToken_incorrectMsgValue() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - { - _mintrequest.price = 10; - _mintrequest.currency = NATIVE_TOKEN; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 incorrectTotalPrice = (_mintrequest.price) + 1; - - vm.expectRevert("must send total price."); - vm.prank(caller); - tokenContract.mintWithSignature{ value: incorrectTotalPrice }(_mintrequest, _signature); - } - - modifier whenCorrectMsgValue() { - _; - } - - function test_mintWithSignature_nonZeroPrice_nativeToken() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenCorrectMsgValue - { - _mintrequest.price = 10; - _mintrequest.currency = NATIVE_TOKEN; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // mint - vm.prank(caller); - tokenContract.mintWithSignature{ value: _mintrequest.price }(_mintrequest, _signature); - - // check state after - assertEq(tokenContract.balanceOf(recipient), _mintrequest.quantity); - - uint256 _platformFee = (_mintrequest.price * platformFeeBps) / 10_000; - uint256 _saleProceeds = _mintrequest.price - _platformFee; - assertEq(caller.balance, 1000 ether - _mintrequest.price); - - (address _platformFeeRecipient, ) = tokenContract.getPlatformFeeInfo(); - assertEq(_platformFeeRecipient.balance, _platformFee); - assertEq(tokenContract.primarySaleRecipient().balance, _saleProceeds); - } - - function test_mintWithSignature_nonZeroPrice_nativeToken_TokensMintedWithSignatureEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenCorrectMsgValue - { - _mintrequest.price = 10; - _mintrequest.currency = NATIVE_TOKEN; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - vm.expectEmit(true, true, false, true); - emit TokensMintedWithSignature(signer, _mintrequest.to, _mintrequest); - tokenContract.mintWithSignature{ value: _mintrequest.price }(_mintrequest, _signature); - } - - function test_mintWithSignature_nonZeroPrice_ERC20_nonZeroMsgValue() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - { - _mintrequest.price = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("msg value not zero"); - vm.prank(caller); - tokenContract.mintWithSignature{ value: 1 }(_mintrequest, _signature); - } - - function test_mintWithSignature_nonZeroPrice_ERC20() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.price = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // mint - vm.prank(caller); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - - // check state after - assertEq(tokenContract.balanceOf(recipient), _mintrequest.quantity); - - uint256 _platformFee = (_mintrequest.price * platformFeeBps) / 10_000; - uint256 _saleProceeds = _mintrequest.price - _platformFee; - assertEq(erc20.balanceOf(caller), 1000 ether - _mintrequest.price); - (address _platformFeeRecipient, ) = tokenContract.getPlatformFeeInfo(); - assertEq(erc20.balanceOf(_platformFeeRecipient), _platformFee); - assertEq(erc20.balanceOf(tokenContract.primarySaleRecipient()), _saleProceeds); - } - - function test_mintWithSignature_nonZeroPrice_ERC20_TokensMintedWithSignatureEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotZeroQuantity - whenMsgValueZero - { - _mintrequest.price = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - vm.expectEmit(true, true, false, true); - emit TokensMintedWithSignature(signer, _mintrequest.to, _mintrequest); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - // ================== - // ======= Test branch: other cases - // ================== -} diff --git a/src/test/tokenerc20-BTT/mint-with-signature/mintWithSignature.tree b/src/test/tokenerc20-BTT/mint-with-signature/mintWithSignature.tree deleted file mode 100644 index d7cfcdcab..000000000 --- a/src/test/tokenerc20-BTT/mint-with-signature/mintWithSignature.tree +++ /dev/null @@ -1,51 +0,0 @@ -mintWithSignature(MintRequest calldata _req, bytes calldata _signature) -├── when signer doesn't have MINTER_ROLE -│ └── it should revert ✅ -└── when signer has MINTER_ROLE - └── when `_req.uid` has already been used - │ └── it should revert ✅ - └── when `_req.uid` has not been used - └── when `_req.validityStartTimestamp` is greater than block timestamp - │ └── it should revert ✅ - └── when `_req.validityStartTimestamp` is less than or equal to block timestamp - └── when `_req.validityEndTimestamp` is less than block timestamp - │ └── it should revert ✅ - └── when `_req.validityEndTimestamp` is greater than or equal to block timestamp - └── when `_req.to` is address(0) - │ └── it should revert ✅ - └── when `_req.to` is not address(0) - ├── when `_req.quantity` is zero - │ └── it should revert ✅ - └── when `_req.quantity` is not zero - │ - │ // case: price is zero - └── when `_req.price` is zero - │ └── when msg.value is not zero - │ │ └── it should revert ✅ - │ └── when msg.value is zero - │ └── it should mint `amount` to `to` ✅ - │ └── it should emit TokensMintedWithSignature event ✅ - │ - │ // case: price is not zero - └── when `_req.price` is not zero - └── when currency is native token - │ └── when msg.value is not equal to total price - │ │ └── it should revert ✅ - │ └── when msg.value is equal to total price - │ └── it should mint `amount` to `to` ✅ - │ └── (transfer to sale recipient) ✅ - │ └── (transfer to fee recipient) ✅ - │ └── it should emit TokensMintedWithSignature event ✅ - └── when currency is some ERC20 token - └── when msg.value is not zero - │ └── it should revert ✅ - └── when msg.value is zero - └── it should mint `amount` to `to` ✅ - └── (transfer to sale recipient) ✅ - └── (transfer to fee recipient) ✅ - └── it should emit TokensMintedWithSignature event ✅ - -// other cases - - - diff --git a/src/test/tokenerc20-BTT/other-functions/other.t.sol b/src/test/tokenerc20-BTT/other-functions/other.t.sol deleted file mode 100644 index 70b62669e..000000000 --- a/src/test/tokenerc20-BTT/other-functions/other.t.sol +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; -import { IStaking20 } from "contracts/extension/interface/IStaking20.sol"; - -import "@openzeppelin/contracts-upgradeable/access/IAccessControlEnumerableUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC20 is TokenERC20 { - function beforeTokenTransfer(address from, address to, uint256 amount) external { - _beforeTokenTransfer(from, to, amount); - } - - function mint(address account, uint256 amount) external { - _mint(account, amount); - } - - function burn(address account, uint256 amount) external { - _burn(account, amount); - } -} - -contract TokenERC20Test_OtherFunctions is BaseTest { - address public implementation; - address public proxy; - - MyTokenERC20 public tokenContract; - address internal caller; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC20()); - caller = getActor(3); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ) - ); - - tokenContract = MyTokenERC20(proxy); - } - - function test_contractType() public { - assertEq(tokenContract.contractType(), bytes32("TokenERC20")); - } - - function test_contractVersion() public { - assertEq(tokenContract.contractVersion(), uint8(1)); - } - - function test_beforeTokenTransfer_restricted_notTransferRole() public { - vm.prank(deployer); - tokenContract.revokeRole(keccak256("TRANSFER_ROLE"), address(0)); - vm.expectRevert("transfers restricted."); - tokenContract.beforeTokenTransfer(caller, address(0x123), 100); - } - - modifier whenTransferRole() { - vm.prank(deployer); - tokenContract.grantRole(keccak256("TRANSFER_ROLE"), caller); - _; - } - - function test_beforeTokenTransfer_restricted() public whenTransferRole { - tokenContract.beforeTokenTransfer(caller, address(0x123), 100); - } - - function test_mint() public { - tokenContract.mint(caller, 100); - assertEq(tokenContract.balanceOf(caller), 100); - } - - function test_burn() public { - tokenContract.mint(caller, 100); - assertEq(tokenContract.balanceOf(caller), 100); - - tokenContract.burn(caller, 60); - assertEq(tokenContract.balanceOf(caller), 40); - } -} diff --git a/src/test/tokenerc20-BTT/other-functions/other.tree b/src/test/tokenerc20-BTT/other-functions/other.tree deleted file mode 100644 index 57a1466a8..000000000 --- a/src/test/tokenerc20-BTT/other-functions/other.tree +++ /dev/null @@ -1,20 +0,0 @@ -contractType() -├── it should return bytes32("TokenERC20") ✅ - -contractVersion() -├── it should return uint8(1) ✅ - -_beforeTokenTransfers( - address from, - address to, - uint256 amount -) -├── when transfers are restricted (i.e. address(0) doesn't have transfer role, or from-to addresses are not address(0) - └── when from and to don't have transfer role - │ └── it should revert ✅ - -_mint(address account, uint256 amount) -├── it should mint amount to account ✅ - -_burn(address account, uint256 amount) -├── it should mint amount from account ✅ diff --git a/src/test/tokenerc20-BTT/set-contract-uri/setContractURI.t.sol b/src/test/tokenerc20-BTT/set-contract-uri/setContractURI.t.sol deleted file mode 100644 index faccf3f9b..000000000 --- a/src/test/tokenerc20-BTT/set-contract-uri/setContractURI.t.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC20 is TokenERC20 {} - -contract TokenERC20Test_SetContractURI is BaseTest { - address public implementation; - address public proxy; - address internal caller; - string internal _contractURI; - - MyTokenERC20 internal tokenContract; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC20()); - - caller = getActor(1); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ) - ); - - tokenContract = MyTokenERC20(proxy); - _contractURI = "ipfs://contracturi"; - } - - function test_setContractURI_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setContractURI(_contractURI); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setContractURI_empty() public whenCallerAuthorized { - vm.prank(address(caller)); - tokenContract.setContractURI(""); - - // get contract uri - assertEq(tokenContract.contractURI(), ""); - } - - function test_setContractURI_notEmpty() public whenCallerAuthorized { - vm.prank(address(caller)); - tokenContract.setContractURI(_contractURI); - - // get contract uri - assertEq(tokenContract.contractURI(), _contractURI); - } -} diff --git a/src/test/tokenerc20-BTT/set-contract-uri/setContractURI.tree b/src/test/tokenerc20-BTT/set-contract-uri/setContractURI.tree deleted file mode 100644 index 8fc480b19..000000000 --- a/src/test/tokenerc20-BTT/set-contract-uri/setContractURI.tree +++ /dev/null @@ -1,8 +0,0 @@ -setContractURI(string calldata _uri) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── when `uri` is empty - │ └── it should update contract URI to empty string ✅ - └── when `uri` is not empty - └── it should update contract URI to `_uri` ✅ \ No newline at end of file diff --git a/src/test/tokenerc20-BTT/set-platform-fee-info/setPlatformFeeInfo.t.sol b/src/test/tokenerc20-BTT/set-platform-fee-info/setPlatformFeeInfo.t.sol deleted file mode 100644 index d2a14a7f1..000000000 --- a/src/test/tokenerc20-BTT/set-platform-fee-info/setPlatformFeeInfo.t.sol +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC20 is TokenERC20 {} - -contract TokenERC20Test_SetPlatformFeeInfo is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal _platformFeeRecipient; - uint256 internal _platformFeeBps; - - MyTokenERC20 internal tokenContract; - - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC20()); - - caller = getActor(1); - _platformFeeRecipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ) - ); - - tokenContract = MyTokenERC20(proxy); - } - - function test_setPlatformFeeInfo_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setPlatformFeeInfo_exceedMaxBps() public whenCallerAuthorized { - _platformFeeBps = 10_001; - vm.prank(address(caller)); - vm.expectRevert("exceeds MAX_BPS"); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - modifier whenNotExceedMaxBps() { - _platformFeeBps = 500; - _; - } - - function test_setPlatformFeeInfo() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - - // get platform fee info - (address _recipient, uint16 _bps) = tokenContract.getPlatformFeeInfo(); - assertEq(_recipient, _platformFeeRecipient); - assertEq(_bps, uint16(_platformFeeBps)); - } - - function test_setPlatformFeeInfo_event() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - vm.expectEmit(true, false, false, true); - emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } -} diff --git a/src/test/tokenerc20-BTT/set-platform-fee-info/setPlatformFeeInfo.tree b/src/test/tokenerc20-BTT/set-platform-fee-info/setPlatformFeeInfo.tree deleted file mode 100644 index dcef9965e..000000000 --- a/src/test/tokenerc20-BTT/set-platform-fee-info/setPlatformFeeInfo.tree +++ /dev/null @@ -1,10 +0,0 @@ -setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when `_platformFeeBps` is greater than MAX_BPS - │ └── it should revert ✅ - └── when `_platformFeeBps` is less than or equal to MAX_BPS - └── it should update platform fee recipient ✅ - └── it should update platform fee bps ✅ - └── it should emit PlatformFeeInfoUpdated event ✅ \ No newline at end of file diff --git a/src/test/tokenerc20-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.t.sol b/src/test/tokenerc20-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.t.sol deleted file mode 100644 index 070f9bfaf..000000000 --- a/src/test/tokenerc20-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.t.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC20 is TokenERC20 {} - -contract TokenERC20Test_SetPrimarySaleRecipient is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal _primarySaleRecipient; - - MyTokenERC20 internal tokenContract; - - event PrimarySaleRecipientUpdated(address indexed recipient); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC20()); - - caller = getActor(1); - _primarySaleRecipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ) - ); - - tokenContract = MyTokenERC20(proxy); - } - - function test_setPrimarySaleRecipient_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setPrimarySaleRecipient() public whenCallerAuthorized { - vm.prank(address(caller)); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - - // get primary sale recipient info - assertEq(tokenContract.primarySaleRecipient(), _primarySaleRecipient); - } - - function test_setPrimarySaleRecipient_event() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectEmit(true, false, false, false); - emit PrimarySaleRecipientUpdated(_primarySaleRecipient); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - } -} diff --git a/src/test/tokenerc20-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.tree b/src/test/tokenerc20-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.tree deleted file mode 100644 index 230035a07..000000000 --- a/src/test/tokenerc20-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.tree +++ /dev/null @@ -1,6 +0,0 @@ -setPrimarySaleRecipient(address _saleRecipient) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── it should update primary sale recipient ✅ - └── it should emit PrimarySaleRecipientUpdated event ✅ \ No newline at end of file diff --git a/src/test/tokenerc20-BTT/verify/verify.t.sol b/src/test/tokenerc20-BTT/verify/verify.t.sol deleted file mode 100644 index fb54c7868..000000000 --- a/src/test/tokenerc20-BTT/verify/verify.t.sol +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC20 is TokenERC20 { - function setMintedUID(MintRequest calldata _req, bytes calldata _signature) external { - verifyRequest(_req, _signature); - } -} - -contract TokenERC20Test_Verify is BaseTest { - address public implementation; - address public proxy; - - MyTokenERC20 internal tokenContract; - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - TokenERC20.MintRequest _mintrequest; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC20()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ) - ); - - tokenContract = MyTokenERC20(proxy); - - typehashMintRequest = keccak256( - "MintRequest(address to,address primarySaleRecipient,uint256 quantity,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes(NAME)); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) - ); - - // construct default mintrequest - _mintrequest.to = address(123); - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.quantity = 100; - _mintrequest.price = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 0; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - } - - function signMintRequest( - TokenERC20.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.primarySaleRecipient, - _request.quantity, - _request.price, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - function test_verify_notMinterRole() public { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - (bool _isValid, address _recoveredSigner) = tokenContract.verify(_mintrequest, _signature); - - assertFalse(_isValid); - assertEq(_recoveredSigner, signer); - } - - modifier whenMinterRole() { - vm.prank(deployer); - tokenContract.grantRole(keccak256("MINTER_ROLE"), signer); - _; - } - - function test_verify_invalidUID() public whenMinterRole { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // set state with this mintrequest and signature, marking the UID as used - tokenContract.setMintedUID(_mintrequest, _signature); - - // pass the same UID mintrequest again - (bool _isValid, address _recoveredSigner) = tokenContract.verify(_mintrequest, _signature); - - assertFalse(_isValid); - assertEq(_recoveredSigner, signer); - } - - modifier whenUidNotUsed() { - _; - } - - function test_verify() public whenMinterRole whenUidNotUsed { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - (bool _isValid, address _recoveredSigner) = tokenContract.verify(_mintrequest, _signature); - - assertTrue(_isValid); - assertEq(_recoveredSigner, signer); - } -} diff --git a/src/test/tokenerc20-BTT/verify/verify.tree b/src/test/tokenerc20-BTT/verify/verify.tree deleted file mode 100644 index c160faa0f..000000000 --- a/src/test/tokenerc20-BTT/verify/verify.tree +++ /dev/null @@ -1,12 +0,0 @@ -verify(MintRequest calldata _req, bytes calldata _signature) -├── when signer doesn't have MINTER_ROLE -│ └── it should return false ✅ -│ └── it should return recovered signer equal to the actual signer of the request ✅ -└── when signer has MINTER_ROLE - └── when `_req.uid` has already been used - │ └── it should return false ✅ - │ └── it should return recovered signer equal to the actual signer of the request ✅ - └── when `_req.uid` has not been used - └── it should return true ✅ - └── it should return recovered signer equal to the actual signer of the request ✅ - diff --git a/src/test/tokenerc721-BTT/burn/burn.t.sol b/src/test/tokenerc721-BTT/burn/burn.t.sol deleted file mode 100644 index 01fa7b43c..000000000 --- a/src/test/tokenerc721-BTT/burn/burn.t.sol +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 {} - -contract TokenERC721Test_Burn is BaseTest { - address public implementation; - address public proxy; - address public caller; - address public recipient; - string public uri; - - MyTokenERC721 internal tokenContract; - - event MetadataUpdate(uint256 _tokenId); - event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - caller = getActor(1); - recipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - uri = "uri"; - - vm.prank(deployer); - tokenContract.grantRole(keccak256("MINTER_ROLE"), caller); - } - - function test_burn_whenNotOwnerNorApproved() public { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - uint256 _tokenId = tokenContract.mintTo(recipient, uri); - - // burn - vm.expectRevert("ERC721Burnable: caller is not owner nor approved"); - tokenContract.burn(_tokenId); - } - - function test_burn_whenOwner() public { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - uint256 _tokenId = tokenContract.mintTo(recipient, uri); - - // burn - vm.prank(recipient); - tokenContract.burn(_tokenId); - - vm.expectRevert(); // checking non-existent token, because burned - tokenContract.ownerOf(_tokenId); - } - - function test_burn_whenApproved() public { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - uint256 _tokenId = tokenContract.mintTo(recipient, uri); - - vm.prank(recipient); - tokenContract.setApprovalForAll(caller, true); - - // burn - vm.prank(caller); - tokenContract.burn(_tokenId); - - vm.expectRevert(); // checking non-existent token, because burned - tokenContract.ownerOf(_tokenId); - } -} diff --git a/src/test/tokenerc721-BTT/burn/burn.tree b/src/test/tokenerc721-BTT/burn/burn.tree deleted file mode 100644 index 0a6e2ff43..000000000 --- a/src/test/tokenerc721-BTT/burn/burn.tree +++ /dev/null @@ -1,8 +0,0 @@ -burn(uint256 tokenId) -├── when the caller isn't the owner of `tokenId` or token not approved to caller -│ └── it should revert ✅ -└── when the caller owns `tokenId` -│ └── it should burn the token ✅ -└── when the `tokenId` is approved to caller - └── it should burn the token ✅ - diff --git a/src/test/tokenerc721-BTT/initialize/initialize.t.sol b/src/test/tokenerc721-BTT/initialize/initialize.t.sol deleted file mode 100644 index e16c27cf7..000000000 --- a/src/test/tokenerc721-BTT/initialize/initialize.t.sol +++ /dev/null @@ -1,311 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 { - function eip712NameHash() external view returns (bytes32) { - return _EIP712NameHash(); - } - - function eip712VersionHash() external view returns (bytes32) { - return _EIP712VersionHash(); - } -} - -contract TokenERC721Test_Initialize is BaseTest { - address public implementation; - address public proxy; - - event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); - event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - } - - function test_initialize_initializingImplementation() public { - vm.expectRevert("Initializable: contract is already initialized"); - TokenERC721(implementation).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - modifier whenNotImplementation() { - _; - } - - function test_initialize_proxyAlreadyInitialized() public whenNotImplementation { - vm.expectRevert("Initializable: contract is already initialized"); - MyTokenERC721(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - modifier whenProxyNotInitialized() { - proxy = address(new TWProxy(implementation, "")); - _; - } - - function test_initialize_exceedsMaxBps() public whenNotImplementation whenProxyNotInitialized { - vm.expectRevert("exceeds MAX_BPS"); - MyTokenERC721(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - uint128(MAX_BPS) + 1, // platformFeeBps greater than MAX_BPS - platformFeeRecipient - ); - } - - modifier whenPlatformFeeBpsWithinMaxBps() { - _; - } - - function test_initialize() public whenNotImplementation whenProxyNotInitialized whenPlatformFeeBpsWithinMaxBps { - MyTokenERC721(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - - // check state - MyTokenERC721 tokenContract = MyTokenERC721(proxy); - - assertEq(tokenContract.eip712NameHash(), keccak256(bytes("TokenERC721"))); - assertEq(tokenContract.eip712VersionHash(), keccak256(bytes("1"))); - - address[] memory _trustedForwarders = forwarders(); - for (uint256 i = 0; i < _trustedForwarders.length; i++) { - assertTrue(tokenContract.isTrustedForwarder(_trustedForwarders[i])); - } - - assertEq(tokenContract.name(), NAME); - assertEq(tokenContract.symbol(), SYMBOL); - assertEq(tokenContract.contractURI(), CONTRACT_URI); - - (address _platformFeeRecipient, uint16 _platformFeeBps) = tokenContract.getPlatformFeeInfo(); - assertEq(_platformFeeBps, platformFeeBps); - assertEq(_platformFeeRecipient, platformFeeRecipient); - assertEq(tokenContract.platformFeeRecipient(), platformFeeRecipient); - - (address _royaltyRecipient, uint16 _royaltyBps) = tokenContract.getDefaultRoyaltyInfo(); - (address _royaltyRecipientForToken, uint16 _royaltyBpsForToken) = tokenContract.getRoyaltyInfoForToken(1); // random tokenId - assertEq(_royaltyBps, royaltyBps); - assertEq(_royaltyRecipient, royaltyRecipient); - assertEq(_royaltyRecipient, _royaltyRecipientForToken); - assertEq(_royaltyBps, _royaltyBpsForToken); - - assertEq(tokenContract.primarySaleRecipient(), saleRecipient); - - assertEq(tokenContract.owner(), deployer); - assertTrue(tokenContract.hasRole(bytes32(0x00), deployer)); - assertTrue(tokenContract.hasRole(keccak256("TRANSFER_ROLE"), deployer)); - assertTrue(tokenContract.hasRole(keccak256("TRANSFER_ROLE"), address(0))); - assertTrue(tokenContract.hasRole(keccak256("MINTER_ROLE"), deployer)); - assertTrue(tokenContract.hasRole(keccak256("METADATA_ROLE"), deployer)); - assertEq(tokenContract.getRoleAdmin(keccak256("METADATA_ROLE")), keccak256("METADATA_ROLE")); - } - - function test_initialize_event_RoleGranted_DefaultAdmin() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _defaultAdminRole = bytes32(0x00); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_defaultAdminRole, deployer, deployer); - MyTokenERC721(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_MinterRole() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _minterRole = keccak256("MINTER_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_minterRole, deployer, deployer); - MyTokenERC721(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_TransferRole() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _transferRole = keccak256("TRANSFER_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_transferRole, deployer, deployer); - MyTokenERC721(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_TransferRole_AddressZero() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _transferRole = keccak256("TRANSFER_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_transferRole, address(0), deployer); - MyTokenERC721(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleGranted_MetadataRole() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _metadataRole = keccak256("METADATA_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleGranted(_metadataRole, deployer, deployer); - MyTokenERC721(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } - - function test_initialize_event_RoleAdminChanged_MetadataRole() - public - whenNotImplementation - whenProxyNotInitialized - whenPlatformFeeBpsWithinMaxBps - { - bytes32 _metadataRole = keccak256("METADATA_ROLE"); - vm.prank(deployer); - vm.expectEmit(true, true, true, false); - emit RoleAdminChanged(_metadataRole, bytes32(0x00), _metadataRole); - MyTokenERC721(proxy).initialize( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ); - } -} diff --git a/src/test/tokenerc721-BTT/initialize/initialize.tree b/src/test/tokenerc721-BTT/initialize/initialize.tree deleted file mode 100644 index 041a9e248..000000000 --- a/src/test/tokenerc721-BTT/initialize/initialize.tree +++ /dev/null @@ -1,42 +0,0 @@ -initialize( - address _defaultAdmin, - string memory _name, - string memory _symbol, - string memory _contractURI, - address[] memory _trustedForwarders, - address _saleRecipient, - address _royaltyRecipient, - uint128 _royaltyBps, - uint128 _platformFeeBps, - address _platformFeeRecipient -) -├── when initializing the implementation contract (not proxy) -│ └── it should revert ✅ -└── when it is a proxy to the implementation - └── when it is already initialized - │ └── it should revert ✅ - └── when it is not initialized - └── when platformFeeBps is greater than MAX_BPS - │ └── it should revert ✅ - └── when platformFeeBps is less than or equal to MAX_BPS - └── it should correctly set EIP712 name hash and version hash ✅ - └── it should set trustedForwarder mapping to true for all addresses in `_trustedForwarders` ✅ - └── it should set _name and _symbol to `_name` and `_symbol` param values respectively ✅ - └── it should set contractURI to `_contractURI` param value ✅ - └── it should set platformFeeRecipient and platformFeeBps as `_platformFeeRecipient` and `_platformFeeBps` respectively ✅ - └── it should set royaltyRecipient and royaltyBps as `_royaltyRecipient` and `_royaltyBps` respectively ✅ - └── it should set primary sale recipient as `_saleRecipient` param value ✅ - └── it should set _owner to `_defaultAdmin` param value ✅ - └── it should grant 0x00 (DEFAULT_ADMIN_ROLE) to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should grant MINTER_ROLE to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should grant TRANSFER_ROLE to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should grant TRANSFER_ROLE to address(0) ✅ - └── it should emit RoleGranted event ✅ - └── it should grant METADATA_ROLE to `_defaultAdmin` address ✅ - └── it should emit RoleGranted event ✅ - └── it should set METADATA_ROLE as role admin for METADATA_ROLE ✅ - └── it should emit RoleAdminChanged event ✅ - diff --git a/src/test/tokenerc721-BTT/mint-to/mintTo.t.sol b/src/test/tokenerc721-BTT/mint-to/mintTo.t.sol deleted file mode 100644 index 4c9900e4c..000000000 --- a/src/test/tokenerc721-BTT/mint-to/mintTo.t.sol +++ /dev/null @@ -1,184 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 {} - -contract ERC721ReceiverCompliant is IERC721Receiver { - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external view virtual override returns (bytes4) { - return this.onERC721Received.selector; - } -} - -contract TokenERC721Test_MintTo is BaseTest { - address public implementation; - address public proxy; - address public caller; - address public recipient; - string public uri; - - MyTokenERC721 internal tokenContract; - ERC721ReceiverCompliant internal erc721ReceiverContract; - - event MetadataUpdate(uint256 _tokenId); - event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - erc721ReceiverContract = new ERC721ReceiverCompliant(); - caller = getActor(1); - recipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - } - - function test_mintTo_notMinterRole() public { - vm.prank(caller); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(keccak256("MINTER_ROLE")), 32) - ) - ); - tokenContract.mintTo(recipient, uri); - } - - modifier whenMinterRole() { - vm.prank(deployer); - tokenContract.grantRole(keccak256("MINTER_ROLE"), caller); - _; - } - - function test_mintTo_emptyUri() public whenMinterRole { - vm.prank(caller); - vm.expectRevert("empty uri."); - tokenContract.mintTo(recipient, uri); - } - - modifier whenNotEmptyUri() { - uri = "ipfs://uri/1"; - _; - } - - // ================== - // ======= Test branch: recipient EOA - // ================== - - function test_mintTo_EOA() public whenMinterRole whenNotEmptyUri { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - uint256 _tokenId = tokenContract.mintTo(recipient, uri); - - // check state after - assertEq(_tokenId, _tokenIdToMint); - assertEq(tokenContract.tokenURI(_tokenId), uri); - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.ownerOf(_tokenId), recipient); - } - - function test_mintTo_EOA_MetadataUpdateEvent() public whenMinterRole whenNotEmptyUri { - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(false, false, false, true); - emit MetadataUpdate(_tokenIdToMint); - tokenContract.mintTo(recipient, uri); - } - - function test_mintTo_EOA_TokensMintedEvent() public whenMinterRole whenNotEmptyUri { - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(true, true, false, true); - emit TokensMinted(recipient, _tokenIdToMint, uri); - tokenContract.mintTo(recipient, uri); - } - - // ================== - // ======= Test branch: recipient is a contract - // ================== - - function test_mintTo_nonERC721ReceiverContract() public whenMinterRole whenNotEmptyUri { - recipient = address(this); - vm.prank(caller); - vm.expectRevert(); - uint256 _tokenId = tokenContract.mintTo(recipient, uri); - } - - modifier whenERC721Receiver() { - recipient = address(erc721ReceiverContract); - _; - } - - function test_mintTo_contract() public whenMinterRole whenNotEmptyUri whenERC721Receiver { - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - uint256 _tokenId = tokenContract.mintTo(recipient, uri); - - // check state after - assertEq(_tokenId, _tokenIdToMint); - assertEq(tokenContract.tokenURI(_tokenId), uri); - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.ownerOf(_tokenId), recipient); - } - - function test_mintTo_contract_MetadataUpdateEvent() public whenMinterRole whenNotEmptyUri whenERC721Receiver { - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(false, false, false, true); - emit MetadataUpdate(_tokenIdToMint); - tokenContract.mintTo(recipient, uri); - } - - function test_mintTo_contract_TokensMintedEvent() public whenMinterRole whenNotEmptyUri whenERC721Receiver { - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(true, true, false, true); - emit TokensMinted(recipient, _tokenIdToMint, uri); - tokenContract.mintTo(recipient, uri); - } -} diff --git a/src/test/tokenerc721-BTT/mint-to/mintTo.tree b/src/test/tokenerc721-BTT/mint-to/mintTo.tree deleted file mode 100644 index 408dd48c9..000000000 --- a/src/test/tokenerc721-BTT/mint-to/mintTo.tree +++ /dev/null @@ -1,25 +0,0 @@ -mintTo(address _to, string calldata _uri) -├── when caller doesn't have MINTER_ROLE - │ └── it should revert ✅ - └── when caller has MINTER_ROLE - ├── when `_uri` is empty i.e. length is zero - │ └── it should revert ✅ - └── when `_uri` is not empty - ├── when `_to` address is an EOA - │ └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - │ └── it should set tokenURI for minted tokenId equal to `_uri` ✅ - │ └── it should increment `nextTokenIdToMint` by 1 ✅ - │ └── it should mint the tokenId to the `_to` address ✅ - │ └── it should emit MetadataUpdate event ✅ - │ └── it should emit TokensMinted event ✅ - └── when `_to` address is a contract - ├── when `_to` address is non ERC721Receiver implementer - │ └── it should revert ✅ - └── when `_to` address implements ERC721Receiver - └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - └── it should set tokenURI for minted tokenId equal to `_uri` ✅ - └── it should increment `nextTokenIdToMint` by 1 ✅ - └── it should mint the tokenId to the `_to` address ✅ - └── it should emit MetadataUpdate event ✅ - └── it should emit TokensMinted event ✅ - diff --git a/src/test/tokenerc721-BTT/mint-with-signature/mintWithSignature.t.sol b/src/test/tokenerc721-BTT/mint-with-signature/mintWithSignature.t.sol deleted file mode 100644 index 4ad653f7b..000000000 --- a/src/test/tokenerc721-BTT/mint-with-signature/mintWithSignature.t.sol +++ /dev/null @@ -1,717 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 { - function setMintedURI(MintRequest calldata _req, bytes calldata _signature) external { - verifyRequest(_req, _signature); - } -} - -contract ERC721ReceiverCompliant is IERC721Receiver { - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external view virtual override returns (bytes4) { - return this.onERC721Received.selector; - } -} - -contract ReentrantContract { - fallback() external payable { - TokenERC721.MintRequest memory _mintrequest; - bytes memory _signature; - MyTokenERC721(msg.sender).mintWithSignature(_mintrequest, _signature); - } -} - -contract TokenERC721Test_MintWithSignature is BaseTest { - address public implementation; - address public proxy; - address public caller; - address public recipient; - string public uri; - - MyTokenERC721 internal tokenContract; - ERC721ReceiverCompliant internal erc721ReceiverContract; - - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - TokenERC721.MintRequest _mintrequest; - - event MetadataUpdate(uint256 _tokenId); - event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri); - event TokensMintedWithSignature( - address indexed signer, - address indexed mintedTo, - uint256 indexed tokenIdMinted, - TokenERC721.MintRequest mintRequest - ); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - erc721ReceiverContract = new ERC721ReceiverCompliant(); - caller = getActor(1); - recipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,string uri,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("TokenERC721")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) - ); - - // construct default mintrequest - _mintrequest.to = address(0x1234); - _mintrequest.royaltyRecipient = royaltyRecipient; - _mintrequest.royaltyBps = royaltyBps; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.uri = "ipfs://"; - _mintrequest.price = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 0; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - - erc20.mint(deployer, 1_000 ether); - vm.deal(deployer, 1_000 ether); - erc20.mint(caller, 1_000 ether); - vm.deal(caller, 1_000 ether); - - vm.startPrank(deployer); - erc20.approve(address(tokenContract), type(uint256).max); - vm.stopPrank(); - - vm.startPrank(caller); - erc20.approve(address(tokenContract), type(uint256).max); - vm.stopPrank(); - } - - function signMintRequest( - TokenERC721.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - keccak256(bytes(_request.uri)), - _request.price, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - function test_mintWithSignature_notMinterRole() public { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - vm.expectRevert("invalid signature"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenMinterRole() { - vm.prank(deployer); - tokenContract.grantRole(keccak256("MINTER_ROLE"), signer); - _; - } - - function test_mintWithSignature_invalidUID() public whenMinterRole { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // set state with this mintrequest and signature, marking the UID as used - tokenContract.setMintedURI(_mintrequest, _signature); - - // pass the same UID mintrequest again - vm.prank(caller); - vm.expectRevert("invalid signature"); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenUidNotUsed() { - _; - } - - function test_mintWithSignature_invalidStartTimestamp() public whenMinterRole whenUidNotUsed { - _mintrequest.validityStartTimestamp = uint128(block.timestamp + 1); - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("request expired"); - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenValidStartTimestamp() { - _; - } - - function test_mintWithSignature_invalidEndTimestamp() public whenMinterRole whenUidNotUsed whenValidStartTimestamp { - _mintrequest.validityEndTimestamp = uint128(block.timestamp - 1); - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("request expired"); - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenValidEndTimestamp() { - _; - } - - function test_mintWithSignature_recipientAddressZero() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - { - _mintrequest.to = address(0); - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("recipient undefined"); - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenRecipientAddressNotZero() { - _; - } - - function test_mintWithSignature_emptyUri() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - { - _mintrequest.uri = ""; - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("empty uri."); - vm.prank(caller); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenNotEmptyUri() { - _; - } - - // ================== - // ======= Test branch: when mint price is zero - // ================== - - function test_mintWithSignature_zeroPrice_msgValueNonZero() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - { - _mintrequest.price = 0; - - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("!Value"); - vm.prank(caller); - tokenContract.mintWithSignature{ value: 1 }(_mintrequest, _signature); - } - - modifier whenMsgValueZero() { - _; - } - - function test_mintWithSignature_zeroPrice_EOA() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - { - _mintrequest.price = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - uint256 _tokenId = tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after - assertEq(_tokenId, _tokenIdToMint); - assertEq(tokenContract.tokenURI(_tokenId), _mintrequest.uri); - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.ownerOf(_tokenId), _mintrequest.to); - } - - function test_mintWithSignature_zeroPrice_EOA_MetadataUpdateEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - { - _mintrequest.price = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(false, false, false, true); - emit MetadataUpdate(_tokenIdToMint); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_mintWithSignature_zeroPrice_EOA_TokensMintedWithSignatureEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - { - _mintrequest.price = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(true, true, false, true); - emit TokensMintedWithSignature(signer, _mintrequest.to, _tokenIdToMint, _mintrequest); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_mintWithSignature_zeroPrice_nonERC721ReceiverContract() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - { - _mintrequest.price = 0; - _mintrequest.to = address(this); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // mint - vm.prank(caller); - vm.expectRevert(); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - modifier whenERC721Receiver() { - _mintrequest.to = address(erc721ReceiverContract); - _; - } - - function test_mintWithSignature_zeroPrice_contract() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - whenERC721Receiver - { - _mintrequest.price = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - uint256 _tokenId = tokenContract.mintWithSignature(_mintrequest, _signature); - - // check state after - assertEq(_tokenId, _tokenIdToMint); - assertEq(tokenContract.tokenURI(_tokenId), _mintrequest.uri); - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.ownerOf(_tokenId), _mintrequest.to); - } - - function test_mintWithSignature_zeroPrice_contract_MetadataUpdateEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - whenERC721Receiver - { - _mintrequest.price = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(false, false, false, true); - emit MetadataUpdate(_tokenIdToMint); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - function test_mintWithSignature_zeroPrice_contract_TokensMintedWithSignatureEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - whenERC721Receiver - { - _mintrequest.price = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(true, true, true, true); - emit TokensMintedWithSignature(signer, _mintrequest.to, _tokenIdToMint, _mintrequest); - tokenContract.mintWithSignature(_mintrequest, _signature); - } - - // ================== - // ======= Test branch: when mint price is not zero - // ================== - - function test_mintWithSignature_nonZeroPrice_nativeToken_incorrectMsgValue() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - { - _mintrequest.price = 10; - _mintrequest.currency = NATIVE_TOKEN; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 incorrectTotalPrice = (_mintrequest.price) + 1; - - vm.expectRevert("must send total price."); - vm.prank(caller); - tokenContract.mintWithSignature{ value: incorrectTotalPrice }(_mintrequest, _signature); - } - - modifier whenCorrectMsgValue() { - _; - } - - function test_mintWithSignature_nonZeroPrice_nativeToken() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenCorrectMsgValue - { - _mintrequest.price = 10; - _mintrequest.currency = NATIVE_TOKEN; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - uint256 _tokenId = tokenContract.mintWithSignature{ value: _mintrequest.price }(_mintrequest, _signature); - - // check state after - assertEq(_tokenId, _tokenIdToMint); - assertEq(tokenContract.tokenURI(_tokenId), _mintrequest.uri); - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.ownerOf(_tokenId), _mintrequest.to); - - uint256 _platformFee = (_mintrequest.price * platformFeeBps) / 10_000; - uint256 _saleProceeds = _mintrequest.price - _platformFee; - assertEq(caller.balance, 1000 ether - _mintrequest.price); - assertEq(tokenContract.platformFeeRecipient().balance, _platformFee); - assertEq(tokenContract.primarySaleRecipient().balance, _saleProceeds); - } - - function test_mintWithSignature_nonZeroPrice_nativeToken_MetadataUpdateEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenCorrectMsgValue - { - _mintrequest.price = 10; - _mintrequest.currency = NATIVE_TOKEN; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(false, false, false, true); - emit MetadataUpdate(_tokenIdToMint); - tokenContract.mintWithSignature{ value: _mintrequest.price }(_mintrequest, _signature); - } - - function test_mintWithSignature_nonZeroPrice_nativeToken_TokensMintedWithSignatureEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenCorrectMsgValue - { - _mintrequest.price = 10; - _mintrequest.currency = NATIVE_TOKEN; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(true, true, true, true); - emit TokensMintedWithSignature(signer, _mintrequest.to, _tokenIdToMint, _mintrequest); - tokenContract.mintWithSignature{ value: _mintrequest.price }(_mintrequest, _signature); - } - - function test_mintWithSignature_nonZeroPrice_ERC20_nonZeroMsgValue() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - { - _mintrequest.price = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.expectRevert("msg value not zero"); - vm.prank(caller); - tokenContract.mintWithSignature{ value: 1 }(_mintrequest, _signature); - } - - function test_mintWithSignature_nonZeroPrice_ERC20() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - { - _mintrequest.price = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // state before - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - // mint - vm.prank(caller); - uint256 _tokenId = tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - - // check state after - assertEq(_tokenId, _tokenIdToMint); - assertEq(tokenContract.tokenURI(_tokenId), _mintrequest.uri); - assertEq(tokenContract.nextTokenIdToMint(), _tokenIdToMint + 1); - assertEq(tokenContract.ownerOf(_tokenId), _mintrequest.to); - - uint256 _platformFee = (_mintrequest.price * platformFeeBps) / 10_000; - uint256 _saleProceeds = _mintrequest.price - _platformFee; - assertEq(erc20.balanceOf(caller), 1000 ether - _mintrequest.price); - assertEq(erc20.balanceOf(tokenContract.platformFeeRecipient()), _platformFee); - assertEq(erc20.balanceOf(tokenContract.primarySaleRecipient()), _saleProceeds); - } - - function test_mintWithSignature_nonZeroPrice_ERC20_MetadataUpdateEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - { - _mintrequest.price = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(false, false, false, true); - emit MetadataUpdate(_tokenIdToMint); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - function test_mintWithSignature_nonZeroPrice_ERC20_TokensMintedWithSignatureEvent() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - { - _mintrequest.price = 10; - _mintrequest.currency = address(erc20); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - uint256 _tokenIdToMint = tokenContract.nextTokenIdToMint(); - - vm.prank(caller); - vm.expectEmit(true, true, true, true); - emit TokensMintedWithSignature(signer, _mintrequest.to, _tokenIdToMint, _mintrequest); - tokenContract.mintWithSignature{ value: 0 }(_mintrequest, _signature); - } - - // ================== - // ======= Test branch: other cases - // ================== - - function test_mintWithSignature_nonZeroRoyaltyRecipient() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - { - _mintrequest.price = 0; - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - uint256 _tokenId = tokenContract.mintWithSignature(_mintrequest, _signature); - - (address _royaltyRecipient, uint16 _royaltyBps) = tokenContract.getRoyaltyInfoForToken(_tokenId); - assertEq(_royaltyRecipient, royaltyRecipient); - assertEq(_royaltyBps, royaltyBps); - } - - function test_mintWithSignature_royaltyRecipientZeroAddress() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - { - _mintrequest.price = 0; - _mintrequest.royaltyRecipient = address(0); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - uint256 _tokenId = tokenContract.mintWithSignature(_mintrequest, _signature); - - (address _royaltyRecipient, uint16 _royaltyBps) = tokenContract.getRoyaltyInfoForToken(_tokenId); - (address _defaultRoyaltyRecipient, uint16 _defaultRoyaltyBps) = tokenContract.getDefaultRoyaltyInfo(); - assertEq(_royaltyRecipient, _defaultRoyaltyRecipient); - assertEq(_royaltyBps, _defaultRoyaltyBps); - } - - function test_mintWithSignature_reentrantRecipientContract() - public - whenMinterRole - whenUidNotUsed - whenValidStartTimestamp - whenValidEndTimestamp - whenRecipientAddressNotZero - whenNotEmptyUri - whenMsgValueZero - { - _mintrequest.price = 0; - _mintrequest.to = address(new ReentrantContract()); - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - vm.prank(caller); - vm.expectRevert("ReentrancyGuard: reentrant call"); - uint256 _tokenId = tokenContract.mintWithSignature(_mintrequest, _signature); - } -} diff --git a/src/test/tokenerc721-BTT/mint-with-signature/mintWithSignature.tree b/src/test/tokenerc721-BTT/mint-with-signature/mintWithSignature.tree deleted file mode 100644 index 347c0f674..000000000 --- a/src/test/tokenerc721-BTT/mint-with-signature/mintWithSignature.tree +++ /dev/null @@ -1,85 +0,0 @@ -mintWithSignature(MintRequest calldata _req, bytes calldata _signature) -├── when signer doesn't have MINTER_ROLE -│ └── it should revert ✅ -└── when signer has MINTER_ROLE - └── when `_req.uid` has already been used - │ └── it should revert ✅ - └── when `_req.uid` has not been used - └── when `_req.validityStartTimestamp` is greater than block timestamp - │ └── it should revert ✅ - └── when `_req.validityStartTimestamp` is less than or equal to block timestamp - └── when `_req.validityEndTimestamp` is less than block timestamp - │ └── it should revert ✅ - └── when `_req.validityEndTimestamp` is greater than or equal to block timestamp - └── when `_req.to` is address(0) - │ └── it should revert ✅ - └── when `_req.to` is not address(0) - ├── when `_req.uri` is empty i.e. length is zero - │ └── it should revert ✅ - └── when `_req.uri` is not empty - │ - │ // case: price is zero - └── when `_req.price` is zero - │ └── when msg.value is not zero - │ │ └── it should revert ✅ - │ └── when msg.value is zero - │ ├── when `_req.to` address is an EOA - │ │ └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - │ │ └── it should set tokenURI for minted tokenId equal to `_req.uri` ✅ - │ │ └── it should increment `nextTokenIdToMint` by 1 ✅ - │ │ └── it should mint the tokenId to the `_req.to` address ✅ - │ │ └── it should set `_req.uid` as minted ✅ - │ │ └── it should emit MetadataUpdate event ✅ - │ │ └── it should emit TokensMintedWithSignature event ✅ - │ └── when `_to` address is a contract - │ ├── when `_to` address is non ERC721Receiver implementer - │ │ └── it should revert ✅ - │ └── when `_to` address implements ERC721Receiver - │ └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - │ └── it should set tokenURI for minted tokenId equal to `_uri` ✅ - │ └── it should increment `nextTokenIdToMint` by 1 ✅ - │ └── it should mint the tokenId to the `_to` address ✅ - │ └── it should set `_req.uid` as minted ✅ - │ └── it should emit MetadataUpdate event ✅ - │ └── it should emit TokensMintedWithSignature event ✅ - │ - │ // case: price is not zero - └── when `_req.price` is not zero - └── when currency is native token - │ └── when msg.value is not equal to total price - │ │ └── it should revert ✅ - │ └── when msg.value is equal to total price - │ └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - │ └── it should set tokenURI for minted tokenId equal to `_uri` ✅ - │ └── it should increment `nextTokenIdToMint` by 1 ✅ - │ └── it should mint the tokenId to the `_to` address ✅ - │ └── it should set `_req.uid` as minted ✅ - │ └── (transfer to sale recipient) ✅ - │ └── (transfer to fee recipient) ✅ - │ └── it should emit MetadataUpdate event ✅ - │ └── it should emit TokensMintedWithSignature event ✅ - └── when currency is some ERC20 token - └── when msg.value is not zero - │ └── it should revert ✅ - └── when msg.value is zero - └── it should mint tokenId equal to current value of `nextTokenIdToMint` ✅ - └── it should set tokenURI for minted tokenId equal to `_uri` ✅ - └── it should increment `nextTokenIdToMint` by 1 ✅ - └── it should mint the tokenId to the `_to` address ✅ - └── it should set `_req.uid` as minted ✅ - └── (transfer to sale recipient) ✅ - └── (transfer to fee recipient) ✅ - └── it should emit MetadataUpdate event ✅ - └── it should emit TokensMintedWithSignature event ✅ - -// other cases - -├── when `_req.royaltyRecipient` is not address(0) - │ └── it should set royaltyInfoForToken ✅ - └── when `_req.royaltyRecipient` is address(0) - └── it should use default royalty info ✅ - -├── when reentrant call - └── it should revert ✅ - - diff --git a/src/test/tokenerc721-BTT/other-functions/other.t.sol b/src/test/tokenerc721-BTT/other-functions/other.t.sol deleted file mode 100644 index 43a70d662..000000000 --- a/src/test/tokenerc721-BTT/other-functions/other.t.sol +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; -import { IStaking721 } from "contracts/extension/interface/IStaking721.sol"; -import { IERC2981 } from "contracts/eip/interface/IERC2981.sol"; - -import "@openzeppelin/contracts-upgradeable/access/IAccessControlEnumerableUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/access/IAccessControlUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/IERC721EnumerableUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; -import "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 { - function canSetMetadata() public view returns (bool) { - return _canSetMetadata(); - } - - function canFreezeMetadata() public view returns (bool) { - return _canFreezeMetadata(); - } -} - -contract TokenERC721Test_OtherFunctions is BaseTest { - address public implementation; - address public proxy; - - MyTokenERC721 public tokenContract; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - } - - function test_contractType() public { - assertEq(tokenContract.contractType(), bytes32("TokenERC721")); - } - - function test_contractVersion() public { - assertEq(tokenContract.contractVersion(), uint8(1)); - } - - function test_canSetMetadata_notMetadataRole() public { - assertFalse(tokenContract.canSetMetadata()); - } - - modifier whenMetadataRoleRole() { - _; - } - - function test_canSetMetadata() public whenMetadataRoleRole { - vm.prank(deployer); - assertTrue(tokenContract.canSetMetadata()); - } - - function test_canFreezeMetadata_notMetadataRole() public { - assertFalse(tokenContract.canFreezeMetadata()); - } - - function test_canFreezeMetadata() public whenMetadataRoleRole { - vm.prank(deployer); - assertTrue(tokenContract.canFreezeMetadata()); - } - - function test_supportsInterface() public { - assertTrue(tokenContract.supportsInterface(type(IERC2981).interfaceId)); - assertTrue(tokenContract.supportsInterface(type(IERC165).interfaceId)); - assertTrue(tokenContract.supportsInterface(type(IERC165Upgradeable).interfaceId)); - assertTrue(tokenContract.supportsInterface(type(IAccessControlEnumerableUpgradeable).interfaceId)); - assertTrue(tokenContract.supportsInterface(type(IAccessControlUpgradeable).interfaceId)); - assertTrue(tokenContract.supportsInterface(type(IERC721EnumerableUpgradeable).interfaceId)); - assertTrue(tokenContract.supportsInterface(type(IERC721Upgradeable).interfaceId)); - - // false for other not supported interfaces - assertFalse(tokenContract.supportsInterface(type(IStaking721).interfaceId)); - } -} diff --git a/src/test/tokenerc721-BTT/other-functions/other.tree b/src/test/tokenerc721-BTT/other-functions/other.tree deleted file mode 100644 index c6611fa64..000000000 --- a/src/test/tokenerc721-BTT/other-functions/other.tree +++ /dev/null @@ -1,31 +0,0 @@ -contractType() -├── it should return bytes32("TokenERC721") ✅ - -contractVersion() -├── it should return uint8(1) ✅ - -_beforeTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity -) -├── when transfers are restricted (i.e. address(0) doesn't have transfer role, or from-to addresses are not address(0) -│ └── when from and to don't have transfer role -│ └── it should revert ✅ - -_canSetMetadata() -├── when the caller doesn't have METADATA_ROLE -│ └── it should revert ✅ -└── when the caller has METADATA_ROLE - └── it should return true ✅ - -_canFreezeMetadata() -├── when the caller doesn't have METADATA_ROLE -│ └── it should revert ✅ -└── when the caller has METADATA_ROLE - └── it should return true ✅ - -supportsInterface(bytes4 interfaceId) -├── it should return true for supported interface ✅ -├── it should return false for not supported interface ✅ diff --git a/src/test/tokenerc721-BTT/owner/owner.t.sol b/src/test/tokenerc721-BTT/owner/owner.t.sol deleted file mode 100644 index 1f9f88ddc..000000000 --- a/src/test/tokenerc721-BTT/owner/owner.t.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 {} - -contract TokenERC721Test_Owner is BaseTest { - address public implementation; - address public proxy; - - MyTokenERC721 internal tokenContract; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - } - - function test_owner() public { - assertEq(tokenContract.owner(), deployer); - } - - function test_owner_notDefaultAdmin() public { - vm.prank(deployer); - tokenContract.renounceRole(bytes32(0x00), deployer); - - assertEq(tokenContract.owner(), address(0)); - } -} diff --git a/src/test/tokenerc721-BTT/owner/owner.tree b/src/test/tokenerc721-BTT/owner/owner.tree deleted file mode 100644 index 576cfcb91..000000000 --- a/src/test/tokenerc721-BTT/owner/owner.tree +++ /dev/null @@ -1,6 +0,0 @@ -owner() -├── when private variable `_owner` DEFAULT_ADMIN_ROLE -│ └── it should return `_owner` ✅ -└── when private variable `_owner` doesn't have DEFAULT_ADMIN_ROLE - └── it should return address(0) ✅ - diff --git a/src/test/tokenerc721-BTT/set-contract-uri/setContractURI.t.sol b/src/test/tokenerc721-BTT/set-contract-uri/setContractURI.t.sol deleted file mode 100644 index 1dc0d8855..000000000 --- a/src/test/tokenerc721-BTT/set-contract-uri/setContractURI.t.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 {} - -contract TokenERC721Test_SetContractURI is BaseTest { - address public implementation; - address public proxy; - address internal caller; - string internal _contractURI; - - MyTokenERC721 internal tokenContract; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - - caller = getActor(1); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - _contractURI = "ipfs://contracturi"; - } - - function test_setContractURI_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setContractURI(_contractURI); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setContractURI_empty() public whenCallerAuthorized { - vm.prank(address(caller)); - tokenContract.setContractURI(""); - - // get contract uri - assertEq(tokenContract.contractURI(), ""); - } - - function test_setContractURI_notEmpty() public whenCallerAuthorized { - vm.prank(address(caller)); - tokenContract.setContractURI(_contractURI); - - // get contract uri - assertEq(tokenContract.contractURI(), _contractURI); - } -} diff --git a/src/test/tokenerc721-BTT/set-contract-uri/setContractURI.tree b/src/test/tokenerc721-BTT/set-contract-uri/setContractURI.tree deleted file mode 100644 index 8fc480b19..000000000 --- a/src/test/tokenerc721-BTT/set-contract-uri/setContractURI.tree +++ /dev/null @@ -1,8 +0,0 @@ -setContractURI(string calldata _uri) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── when `uri` is empty - │ └── it should update contract URI to empty string ✅ - └── when `uri` is not empty - └── it should update contract URI to `_uri` ✅ \ No newline at end of file diff --git a/src/test/tokenerc721-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol b/src/test/tokenerc721-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol deleted file mode 100644 index d63175b76..000000000 --- a/src/test/tokenerc721-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 {} - -contract TokenERC721Test_SetDefaultRoyaltyInfo is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal defaultRoyaltyRecipient; - uint256 internal defaultRoyaltyBps; - - MyTokenERC721 internal tokenContract; - - event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - - caller = getActor(1); - defaultRoyaltyRecipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - } - - function test_setDefaultRoyaltyInfo_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setDefaultRoyaltyInfo_exceedMaxBps() public whenCallerAuthorized { - defaultRoyaltyBps = 10_001; - vm.prank(address(caller)); - vm.expectRevert("exceed royalty bps"); - tokenContract.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } - - modifier whenNotExceedMaxBps() { - defaultRoyaltyBps = 500; - _; - } - - function test_setDefaultRoyaltyInfo() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - tokenContract.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - - // get default royalty info - (address _recipient, uint16 _royaltyBps) = tokenContract.getDefaultRoyaltyInfo(); - assertEq(_recipient, defaultRoyaltyRecipient); - assertEq(_royaltyBps, uint16(defaultRoyaltyBps)); - - // get royalty info for token - uint256 tokenId = 0; - (_recipient, _royaltyBps) = tokenContract.getRoyaltyInfoForToken(tokenId); - assertEq(_recipient, defaultRoyaltyRecipient); - assertEq(_royaltyBps, uint16(defaultRoyaltyBps)); - - // royaltyInfo - ERC2981 - uint256 salePrice = 1000; - (address _royaltyRecipient, uint256 _royaltyAmount) = tokenContract.royaltyInfo(tokenId, salePrice); - assertEq(_royaltyRecipient, defaultRoyaltyRecipient); - assertEq(_royaltyAmount, (salePrice * defaultRoyaltyBps) / 10_000); - } - - function test_setDefaultRoyaltyInfo_event() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - vm.expectEmit(true, false, false, true); - emit DefaultRoyalty(defaultRoyaltyRecipient, defaultRoyaltyBps); - tokenContract.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } -} diff --git a/src/test/tokenerc721-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.tree b/src/test/tokenerc721-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.tree deleted file mode 100644 index 78a4312de..000000000 --- a/src/test/tokenerc721-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.tree +++ /dev/null @@ -1,11 +0,0 @@ -setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when royalty bps input is greater than MAX_BPS - │ └── it should revert ✅ - └── when royalty bps input is less than or equal to MAX_BPS - └── it should update default royalty recipient ✅ - └── it should update default royalty bps ✅ - └── it should calculate royalty amount for a token-id based on default royalty info ✅ - └── it should emit DefaultRoyalty event ✅ \ No newline at end of file diff --git a/src/test/tokenerc721-BTT/set-owner/setOwner.t.sol b/src/test/tokenerc721-BTT/set-owner/setOwner.t.sol deleted file mode 100644 index 1ea57ba2c..000000000 --- a/src/test/tokenerc721-BTT/set-owner/setOwner.t.sol +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 {} - -contract TokenERC721Test_SetOwner is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal _newOwner; - - MyTokenERC721 internal tokenContract; - - event OwnerUpdated(address indexed prevOwner, address indexed newOwner); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - - caller = getActor(1); - _newOwner = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - } - - function test_setOwner_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setOwner(_newOwner); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setOwner_newOwnerNotAdmin() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectRevert("new owner not module admin."); - tokenContract.setOwner(_newOwner); - } - - modifier whenNewOwnerIsAnAdmin() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), _newOwner); - _; - } - - function test_setOwner() public whenCallerAuthorized whenNewOwnerIsAnAdmin { - vm.prank(address(caller)); - tokenContract.setOwner(_newOwner); - - assertEq(tokenContract.owner(), _newOwner); - } - - function test_setOwner_event() public whenCallerAuthorized whenNewOwnerIsAnAdmin { - vm.prank(address(caller)); - vm.expectEmit(true, true, false, false); - emit OwnerUpdated(deployer, _newOwner); - tokenContract.setOwner(_newOwner); - } -} diff --git a/src/test/tokenerc721-BTT/set-owner/setOwner.tree b/src/test/tokenerc721-BTT/set-owner/setOwner.tree deleted file mode 100644 index 964e97cac..000000000 --- a/src/test/tokenerc721-BTT/set-owner/setOwner.tree +++ /dev/null @@ -1,9 +0,0 @@ -setOwner(address _newOwner) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── when incoming `_owner` doesn't have DEFAULT_ADMIN_ROLE - │ └── it should revert ✅ - └── when incoming `_owner` has DEFAULT_ADMIN_ROLE - └── it should update owner ✅ - └── it should emit OwnerUpdated event ✅ \ No newline at end of file diff --git a/src/test/tokenerc721-BTT/set-platform-fee-info/setPlatformFeeInfo.t.sol b/src/test/tokenerc721-BTT/set-platform-fee-info/setPlatformFeeInfo.t.sol deleted file mode 100644 index e2a9b0ab4..000000000 --- a/src/test/tokenerc721-BTT/set-platform-fee-info/setPlatformFeeInfo.t.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 {} - -contract TokenERC721Test_SetPlatformFeeInfo is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal _platformFeeRecipient; - uint256 internal _platformFeeBps; - - MyTokenERC721 internal tokenContract; - - event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - - caller = getActor(1); - _platformFeeRecipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - } - - function test_setPlatformFeeInfo_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setPlatformFeeInfo_exceedMaxBps() public whenCallerAuthorized { - _platformFeeBps = 10_001; - vm.prank(address(caller)); - vm.expectRevert("exceeds MAX_BPS"); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } - - modifier whenNotExceedMaxBps() { - _platformFeeBps = 500; - _; - } - - function test_setPlatformFeeInfo() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - - // get platform fee info - (address _recipient, uint16 _bps) = tokenContract.getPlatformFeeInfo(); - assertEq(_recipient, _platformFeeRecipient); - assertEq(_bps, uint16(_platformFeeBps)); - assertEq(tokenContract.platformFeeRecipient(), _platformFeeRecipient); - } - - function test_setPlatformFeeInfo_event() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - vm.expectEmit(true, false, false, true); - emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps); - tokenContract.setPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); - } -} diff --git a/src/test/tokenerc721-BTT/set-platform-fee-info/setPlatformFeeInfo.tree b/src/test/tokenerc721-BTT/set-platform-fee-info/setPlatformFeeInfo.tree deleted file mode 100644 index dcef9965e..000000000 --- a/src/test/tokenerc721-BTT/set-platform-fee-info/setPlatformFeeInfo.tree +++ /dev/null @@ -1,10 +0,0 @@ -setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when `_platformFeeBps` is greater than MAX_BPS - │ └── it should revert ✅ - └── when `_platformFeeBps` is less than or equal to MAX_BPS - └── it should update platform fee recipient ✅ - └── it should update platform fee bps ✅ - └── it should emit PlatformFeeInfoUpdated event ✅ \ No newline at end of file diff --git a/src/test/tokenerc721-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.t.sol b/src/test/tokenerc721-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.t.sol deleted file mode 100644 index 325b801ac..000000000 --- a/src/test/tokenerc721-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.t.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 {} - -contract TokenERC721Test_SetPrimarySaleRecipient is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal _primarySaleRecipient; - - MyTokenERC721 internal tokenContract; - - event PrimarySaleRecipientUpdated(address indexed recipient); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - - caller = getActor(1); - _primarySaleRecipient = getActor(2); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - } - - function test_setPrimarySaleRecipient_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setPrimarySaleRecipient() public whenCallerAuthorized { - vm.prank(address(caller)); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - - // get primary sale recipient info - assertEq(tokenContract.primarySaleRecipient(), _primarySaleRecipient); - } - - function test_setPrimarySaleRecipient_event() public whenCallerAuthorized { - vm.prank(address(caller)); - vm.expectEmit(true, false, false, false); - emit PrimarySaleRecipientUpdated(_primarySaleRecipient); - tokenContract.setPrimarySaleRecipient(_primarySaleRecipient); - } -} diff --git a/src/test/tokenerc721-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.tree b/src/test/tokenerc721-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.tree deleted file mode 100644 index 230035a07..000000000 --- a/src/test/tokenerc721-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.tree +++ /dev/null @@ -1,6 +0,0 @@ -setPrimarySaleRecipient(address _saleRecipient) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - └── it should update primary sale recipient ✅ - └── it should emit PrimarySaleRecipientUpdated event ✅ \ No newline at end of file diff --git a/src/test/tokenerc721-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol b/src/test/tokenerc721-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol deleted file mode 100644 index f5c5e3b83..000000000 --- a/src/test/tokenerc721-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 {} - -contract TokenERC721Test_SetRoyaltyInfoForToken is BaseTest { - address public implementation; - address public proxy; - address internal caller; - address internal defaultRoyaltyRecipient; - uint256 internal defaultRoyaltyBps; - - MyTokenERC721 internal tokenContract; - - address internal royaltyRecipientForToken; - uint256 internal royaltyBpsForToken; - uint256 internal tokenId; - - event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps); - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - - caller = getActor(1); - defaultRoyaltyRecipient = getActor(2); - royaltyRecipientForToken = getActor(3); - defaultRoyaltyBps = 500; - tokenId = 1; - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - - vm.prank(deployer); - tokenContract.setDefaultRoyaltyInfo(defaultRoyaltyRecipient, defaultRoyaltyBps); - } - - function test_setRoyaltyInfoForToken_callerNotAuthorized() public { - vm.prank(address(caller)); - vm.expectRevert( - abi.encodePacked( - "AccessControl: account ", - Strings.toHexString(uint160(caller), 20), - " is missing role ", - Strings.toHexString(uint256(0), 32) - ) - ); - tokenContract.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - } - - modifier whenCallerAuthorized() { - vm.prank(deployer); - tokenContract.grantRole(bytes32(0x00), caller); - _; - } - - function test_setRoyaltyInfoForToken_exceedMaxBps() public whenCallerAuthorized { - royaltyBpsForToken = 10_001; - vm.prank(address(caller)); - vm.expectRevert("exceed royalty bps"); - tokenContract.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - } - - modifier whenNotExceedMaxBps() { - royaltyBpsForToken = 1000; - _; - } - - function test_setRoyaltyInfoForToken() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - tokenContract.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - - // get default royalty info - (address _defaultRecipient, uint16 _defaultRoyaltyBps) = tokenContract.getDefaultRoyaltyInfo(); - assertEq(_defaultRecipient, defaultRoyaltyRecipient); - assertEq(_defaultRoyaltyBps, uint16(defaultRoyaltyBps)); - - // get royalty info for token - (address _royaltyRecipientForToken, uint16 _royaltyBpsForToken) = tokenContract.getRoyaltyInfoForToken(tokenId); - assertEq(_royaltyRecipientForToken, royaltyRecipientForToken); - assertEq(_royaltyBpsForToken, uint16(royaltyBpsForToken)); - - // royaltyInfo - ERC2981: calculate for default - uint256 salePrice = 1000; - (address _royaltyRecipient, uint256 _royaltyAmount) = tokenContract.royaltyInfo(0, salePrice); - assertEq(_royaltyRecipient, defaultRoyaltyRecipient); - assertEq(_royaltyAmount, (salePrice * defaultRoyaltyBps) / 10_000); - - // royaltyInfo - ERC2981: calculate for specific tokenId we set the royalty info for - (_royaltyRecipient, _royaltyAmount) = tokenContract.royaltyInfo(tokenId, salePrice); - assertEq(_royaltyRecipient, royaltyRecipientForToken); - assertEq(_royaltyAmount, (salePrice * royaltyBpsForToken) / 10_000); - } - - function test_setRoyaltyInfoForToken_event() public whenCallerAuthorized whenNotExceedMaxBps { - vm.prank(address(caller)); - vm.expectEmit(true, true, false, true); - emit RoyaltyForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - tokenContract.setRoyaltyInfoForToken(tokenId, royaltyRecipientForToken, royaltyBpsForToken); - } -} diff --git a/src/test/tokenerc721-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.tree b/src/test/tokenerc721-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.tree deleted file mode 100644 index e28295634..000000000 --- a/src/test/tokenerc721-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.tree +++ /dev/null @@ -1,15 +0,0 @@ -function setRoyaltyInfoForToken( - uint256 _tokenId, - address _recipient, - uint256 _bps -) -├── when caller not authorized - │ └── it should revert ✅ - └── when caller is authorized - ├── when royalty bps input is greater than MAX_BPS - │ └── it should revert ✅ - └── when royalty bps input is less than or equal to MAX_BPS - └── it should update default royalty recipient ✅ - └── it should update default royalty bps ✅ - └── it should calculate royalty amount for a token-id based on default royalty info ✅ - └── it should emit DefaultRoyalty event ✅ \ No newline at end of file diff --git a/src/test/tokenerc721-BTT/token-uri/tokenURI.t.sol b/src/test/tokenerc721-BTT/token-uri/tokenURI.t.sol deleted file mode 100644 index 260048e0c..000000000 --- a/src/test/tokenerc721-BTT/token-uri/tokenURI.t.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 {} - -contract TokenERC721Test_TokenURI is BaseTest { - address public implementation; - address public proxy; - - MyTokenERC721 internal tokenContract; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - } - - function test_tokenURI() public { - uint256 _tokenId = 1; - string memory _uri = "ipfs://uri/1"; - - vm.prank(deployer); - tokenContract.setTokenURI(_tokenId, _uri); - - assertEq(tokenContract.tokenURI(_tokenId), _uri); - } -} diff --git a/src/test/tokenerc721-BTT/token-uri/tokenURI.tree b/src/test/tokenerc721-BTT/token-uri/tokenURI.tree deleted file mode 100644 index c97d2c9d1..000000000 --- a/src/test/tokenerc721-BTT/token-uri/tokenURI.tree +++ /dev/null @@ -1,3 +0,0 @@ -tokenURI(uint256 _tokenId) -├── it should return tokenURI associated with the given `_tokenId` ✅ - diff --git a/src/test/tokenerc721-BTT/verify/verify.t.sol b/src/test/tokenerc721-BTT/verify/verify.t.sol deleted file mode 100644 index 2d219b9f0..000000000 --- a/src/test/tokenerc721-BTT/verify/verify.t.sol +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; - -contract MyTokenERC721 is TokenERC721 { - function setMintedURI(MintRequest calldata _req, bytes calldata _signature) external { - verifyRequest(_req, _signature); - } -} - -contract TokenERC721Test_Verify is BaseTest { - address public implementation; - address public proxy; - - MyTokenERC721 internal tokenContract; - bytes32 internal typehashMintRequest; - bytes32 internal nameHash; - bytes32 internal versionHash; - bytes32 internal typehashEip712; - bytes32 internal domainSeparator; - - TokenERC721.MintRequest _mintrequest; - - function setUp() public override { - super.setUp(); - - // Deploy implementation. - implementation = address(new MyTokenERC721()); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = address( - new TWProxy( - implementation, - abi.encodeCall( - TokenERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ) - ); - - tokenContract = MyTokenERC721(proxy); - - typehashMintRequest = keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,string uri,uint256 price,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - nameHash = keccak256(bytes("TokenERC721")); - versionHash = keccak256(bytes("1")); - typehashEip712 = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - domainSeparator = keccak256( - abi.encode(typehashEip712, nameHash, versionHash, block.chainid, address(tokenContract)) - ); - - // construct default mintrequest - _mintrequest.to = address(0x1234); - _mintrequest.royaltyRecipient = royaltyRecipient; - _mintrequest.royaltyBps = royaltyBps; - _mintrequest.primarySaleRecipient = saleRecipient; - _mintrequest.uri = "ipfs://"; - _mintrequest.price = 0; - _mintrequest.currency = address(0); - _mintrequest.validityStartTimestamp = 0; - _mintrequest.validityEndTimestamp = 2000; - _mintrequest.uid = bytes32(0); - } - - function signMintRequest( - TokenERC721.MintRequest memory _request, - uint256 _privateKey - ) internal view returns (bytes memory) { - bytes memory encodedRequest = abi.encode( - typehashMintRequest, - _request.to, - _request.royaltyRecipient, - _request.royaltyBps, - _request.primarySaleRecipient, - keccak256(bytes(_request.uri)), - _request.price, - _request.currency, - _request.validityStartTimestamp, - _request.validityEndTimestamp, - _request.uid - ); - bytes32 structHash = keccak256(encodedRequest); - bytes32 typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign(_privateKey, typedDataHash); - bytes memory sig = abi.encodePacked(r, s, v); - - return sig; - } - - function test_verify_notMinterRole() public { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - (bool _isValid, address _recoveredSigner) = tokenContract.verify(_mintrequest, _signature); - - assertFalse(_isValid); - assertEq(_recoveredSigner, signer); - } - - modifier whenMinterRole() { - vm.prank(deployer); - tokenContract.grantRole(keccak256("MINTER_ROLE"), signer); - _; - } - - function test_verify_invalidUID() public whenMinterRole { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - // set state with this mintrequest and signature, marking the UID as used - tokenContract.setMintedURI(_mintrequest, _signature); - - // pass the same UID mintrequest again - (bool _isValid, address _recoveredSigner) = tokenContract.verify(_mintrequest, _signature); - - assertFalse(_isValid); - assertEq(_recoveredSigner, signer); - } - - modifier whenUidNotUsed() { - _; - } - - function test_verify() public whenMinterRole whenUidNotUsed { - bytes memory _signature = signMintRequest(_mintrequest, privateKey); - - (bool _isValid, address _recoveredSigner) = tokenContract.verify(_mintrequest, _signature); - - assertTrue(_isValid); - assertEq(_recoveredSigner, signer); - } -} diff --git a/src/test/tokenerc721-BTT/verify/verify.tree b/src/test/tokenerc721-BTT/verify/verify.tree deleted file mode 100644 index c160faa0f..000000000 --- a/src/test/tokenerc721-BTT/verify/verify.tree +++ /dev/null @@ -1,12 +0,0 @@ -verify(MintRequest calldata _req, bytes calldata _signature) -├── when signer doesn't have MINTER_ROLE -│ └── it should return false ✅ -│ └── it should return recovered signer equal to the actual signer of the request ✅ -└── when signer has MINTER_ROLE - └── when `_req.uid` has already been used - │ └── it should return false ✅ - │ └── it should return recovered signer equal to the actual signer of the request ✅ - └── when `_req.uid` has not been used - └── it should return true ✅ - └── it should return recovered signer equal to the actual signer of the request ✅ - diff --git a/src/test/utils/BaseTest.sol b/src/test/utils/BaseTest.sol deleted file mode 100644 index a26ff27ad..000000000 --- a/src/test/utils/BaseTest.sol +++ /dev/null @@ -1,447 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@std/Test.sol"; -import "@ds-test/test.sol"; -// import "./Console.sol"; -import { Wallet } from "./Wallet.sol"; -import "./ChainlinkVRF.sol"; -import { WETH9 } from "../mocks/WETH9.sol"; -import { MockERC20, ERC20, IERC20 } from "../mocks/MockERC20.sol"; -import { MockERC721, IERC721 } from "../mocks/MockERC721.sol"; -import { MockERC1155, IERC1155 } from "../mocks/MockERC1155.sol"; -import { MockERC721NonBurnable } from "../mocks/MockERC721NonBurnable.sol"; -import { MockERC1155NonBurnable } from "../mocks/MockERC1155NonBurnable.sol"; -import { Forwarder } from "contracts/infra/forwarder/Forwarder.sol"; -import { ForwarderEOAOnly } from "contracts/infra/forwarder/ForwarderEOAOnly.sol"; -import { TWRegistry } from "contracts/infra/TWRegistry.sol"; -import { TWFactory } from "contracts/infra/TWFactory.sol"; -import { Multiwrap } from "contracts/prebuilts/multiwrap/Multiwrap.sol"; -import { Pack } from "contracts/prebuilts/pack/Pack.sol"; -import { PackVRFDirect } from "contracts/prebuilts/pack/PackVRFDirect.sol"; -import { Split } from "contracts/prebuilts/split/Split.sol"; -import { DropERC20 } from "contracts/prebuilts/drop/DropERC20.sol"; -import { DropERC721 } from "contracts/prebuilts/drop/DropERC721.sol"; -import { DropERC1155 } from "contracts/prebuilts/drop/DropERC1155.sol"; -import { TokenERC20 } from "contracts/prebuilts/token/TokenERC20.sol"; -import { TokenERC721 } from "contracts/prebuilts/token/TokenERC721.sol"; -import { TokenERC1155 } from "contracts/prebuilts/token/TokenERC1155.sol"; -import { Marketplace } from "contracts/prebuilts/marketplace-legacy/Marketplace.sol"; -import { VoteERC20 } from "contracts/prebuilts/vote/VoteERC20.sol"; -import { SignatureDrop } from "contracts/prebuilts/signature-drop/SignatureDrop.sol"; -import { ContractPublisher } from "contracts/infra/ContractPublisher.sol"; -import { IContractPublisher } from "contracts/infra/interface/IContractPublisher.sol"; -import { AirdropERC721 } from "contracts/prebuilts/unaudited/airdrop/AirdropERC721.sol"; -import { AirdropERC721Claimable } from "contracts/prebuilts/unaudited/airdrop/AirdropERC721Claimable.sol"; -import { AirdropERC20 } from "contracts/prebuilts/unaudited/airdrop/AirdropERC20.sol"; -import { AirdropERC20Claimable } from "contracts/prebuilts/unaudited/airdrop/AirdropERC20Claimable.sol"; -import { AirdropERC1155 } from "contracts/prebuilts/unaudited/airdrop/AirdropERC1155.sol"; -import { AirdropERC1155Claimable } from "contracts/prebuilts/unaudited/airdrop/AirdropERC1155Claimable.sol"; -import { NFTStake } from "contracts/prebuilts/staking/NFTStake.sol"; -import { EditionStake } from "contracts/prebuilts/staking/EditionStake.sol"; -import { TokenStake } from "contracts/prebuilts/staking/TokenStake.sol"; -import { Mock, MockContract } from "../mocks/Mock.sol"; -import { MockContractPublisher } from "../mocks/MockContractPublisher.sol"; -import { Permissions } from "contracts/extension/Permissions.sol"; -import { PermissionsEnumerable } from "contracts/extension/PermissionsEnumerable.sol"; -import { ERC1155Holder, IERC1155Receiver } from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; -import { ERC721Holder, IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; -import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol"; -import { Strings } from "contracts/lib/Strings.sol"; -import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; - -abstract contract BaseTest is DSTest, Test { - string public constant NAME = "NAME"; - string public constant SYMBOL = "SYMBOL"; - string public constant CONTRACT_URI = "CONTRACT_URI"; - address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - - MockERC20 public erc20; - MockERC20 public erc20Aux; - MockERC721 public erc721; - MockERC1155 public erc1155; - MockERC721NonBurnable public erc721NonBurnable; - MockERC1155NonBurnable public erc1155NonBurnable; - WETH9 public weth; - - address public forwarder; - address public eoaForwarder; - address public registry; - address public factory; - address public fee; - address public contractPublisher; - address public linkToken; - address public vrfV2Wrapper; - - address public factoryAdmin = address(0x10000); - address public deployer = address(0x20000); - address public saleRecipient = address(0x30000); - address public royaltyRecipient = address(0x30001); - address public platformFeeRecipient = address(0x30002); - uint128 public royaltyBps = 500; // 5% - uint128 public platformFeeBps = 500; // 5% - uint256 public constant MAX_BPS = 10_000; // 100% - - uint256 public privateKey = 1234; - address public signer; - - // airdrop-claimable inputs - uint256[] internal _airdropTokenIdsERC721; - bytes32 internal _airdropMerkleRootERC721; - - uint256[] internal _airdropTokenIdsERC1155; - uint256[] internal _airdropWalletClaimCountERC1155; - uint256[] internal _airdropAmountsERC1155; - bytes32[] internal _airdropMerkleRootERC1155; - - bytes32 internal _airdropMerkleRootERC20; - - Wallet internal airdropTokenOwner; - // airdrop-claimable inputs -- over - - mapping(bytes32 => address) public contracts; - - function setUp() public virtual { - /// setup main factory contracts. registry, fee, factory. - vm.startPrank(factoryAdmin); - - signer = vm.addr(privateKey); - - erc20 = new MockERC20(); - erc20Aux = new MockERC20(); - erc721 = new MockERC721(); - erc1155 = new MockERC1155(); - erc721NonBurnable = new MockERC721NonBurnable(); - erc1155NonBurnable = new MockERC1155NonBurnable(); - weth = new WETH9(); - forwarder = address(new Forwarder()); - eoaForwarder = address(new ForwarderEOAOnly()); - registry = address(new TWRegistry(forwarders())); - factory = address(new TWFactory(forwarders(), registry)); - contractPublisher = address(new ContractPublisher(factoryAdmin, forwarders(), new MockContractPublisher())); - linkToken = address(new Link()); - vrfV2Wrapper = address(new VRFV2Wrapper()); - TWRegistry(registry).grantRole(TWRegistry(registry).OPERATOR_ROLE(), factory); - TWRegistry(registry).grantRole(TWRegistry(registry).OPERATOR_ROLE(), contractPublisher); - - TWFactory(factory).addImplementation(address(new TokenERC20())); - TWFactory(factory).addImplementation(address(new TokenERC721())); - TWFactory(factory).addImplementation(address(new TokenERC1155())); - TWFactory(factory).addImplementation(address(new DropERC20())); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("DropERC721"), 1))); - TWFactory(factory).addImplementation(address(new DropERC721())); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("DropERC1155"), 1))); - TWFactory(factory).addImplementation(address(new DropERC1155())); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("SignatureDrop"), 1))); - TWFactory(factory).addImplementation(address(new SignatureDrop())); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("Marketplace"), 1))); - TWFactory(factory).addImplementation(address(new Marketplace(address(weth)))); - TWFactory(factory).addImplementation(address(new Split())); - TWFactory(factory).addImplementation(address(new Multiwrap(address(weth)))); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("Pack"), 1))); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("AirdropERC721"), 1))); - TWFactory(factory).addImplementation(address(new AirdropERC721())); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("AirdropERC20"), 1))); - TWFactory(factory).addImplementation(address(new AirdropERC20())); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("AirdropERC1155"), 1))); - TWFactory(factory).addImplementation(address(new AirdropERC1155())); - TWFactory(factory).addImplementation( - address(new PackVRFDirect(address(weth), eoaForwarder, linkToken, vrfV2Wrapper)) - ); - TWFactory(factory).addImplementation(address(new Pack(address(weth)))); - TWFactory(factory).addImplementation(address(new VoteERC20())); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("NFTStake"), 1))); - TWFactory(factory).addImplementation(address(new NFTStake(address(weth)))); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("EditionStake"), 1))); - TWFactory(factory).addImplementation(address(new EditionStake(address(weth)))); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("TokenStake"), 1))); - TWFactory(factory).addImplementation(address(new TokenStake(address(weth)))); - vm.stopPrank(); - - // setup airdrop logic - setupAirdropClaimable(); - - /// deploy proxy for tests - deployContractProxy( - "TokenERC20", - abi.encodeCall( - TokenERC20.initialize, - (signer, NAME, SYMBOL, CONTRACT_URI, forwarders(), saleRecipient, platformFeeRecipient, platformFeeBps) - ) - ); - deployContractProxy( - "TokenERC721", - abi.encodeCall( - TokenERC721.initialize, - ( - signer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - deployContractProxy( - "TokenERC1155", - abi.encodeCall( - TokenERC1155.initialize, - ( - signer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - deployContractProxy( - "DropERC20", - abi.encodeCall( - DropERC20.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - platformFeeRecipient, - platformFeeBps - ) - ) - ); - deployContractProxy( - "DropERC721", - abi.encodeCall( - DropERC721.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - deployContractProxy( - "DropERC1155", - abi.encodeCall( - DropERC1155.initialize, - ( - deployer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - deployContractProxy( - "SignatureDrop", - abi.encodeCall( - SignatureDrop.initialize, - ( - signer, - NAME, - SYMBOL, - CONTRACT_URI, - forwarders(), - saleRecipient, - royaltyRecipient, - royaltyBps, - platformFeeBps, - platformFeeRecipient - ) - ) - ); - deployContractProxy( - "Marketplace", - abi.encodeCall( - Marketplace.initialize, - (deployer, CONTRACT_URI, forwarders(), platformFeeRecipient, platformFeeBps) - ) - ); - deployContractProxy( - "Multiwrap", - abi.encodeCall( - Multiwrap.initialize, - (deployer, NAME, SYMBOL, CONTRACT_URI, forwarders(), royaltyRecipient, royaltyBps) - ) - ); - deployContractProxy( - "Pack", - abi.encodeCall( - Pack.initialize, - (deployer, NAME, SYMBOL, CONTRACT_URI, forwarders(), royaltyRecipient, royaltyBps) - ) - ); - - deployContractProxy( - "PackVRFDirect", - abi.encodeCall( - PackVRFDirect.initialize, - (deployer, NAME, SYMBOL, CONTRACT_URI, forwarders(), royaltyRecipient, royaltyBps) - ) - ); - - deployContractProxy( - "AirdropERC721", - abi.encodeCall(AirdropERC721.initialize, (deployer, CONTRACT_URI, forwarders())) - ); - deployContractProxy( - "AirdropERC20", - abi.encodeCall(AirdropERC20.initialize, (deployer, CONTRACT_URI, forwarders())) - ); - deployContractProxy( - "AirdropERC1155", - abi.encodeCall(AirdropERC1155.initialize, (deployer, CONTRACT_URI, forwarders())) - ); - deployContractProxy( - "NFTStake", - abi.encodeCall( - NFTStake.initialize, - (deployer, CONTRACT_URI, forwarders(), address(erc20), address(erc721), 60, 1) - ) - ); - deployContractProxy( - "EditionStake", - abi.encodeCall( - EditionStake.initialize, - (deployer, CONTRACT_URI, forwarders(), address(erc20), address(erc1155), 60, 1) - ) - ); - deployContractProxy( - "TokenStake", - abi.encodeCall( - TokenStake.initialize, - (deployer, CONTRACT_URI, forwarders(), address(erc20), address(erc20Aux), 60, 3, 50) - ) - ); - } - - function deployContractProxy( - string memory _contractType, - bytes memory _initializer - ) public returns (address proxyAddress) { - vm.startPrank(deployer); - proxyAddress = TWFactory(factory).deployProxy(bytes32(bytes(_contractType)), _initializer); - contracts[bytes32(bytes(_contractType))] = proxyAddress; - vm.stopPrank(); - } - - function getContract(string memory _name) public view returns (address) { - return contracts[bytes32(bytes(_name))]; - } - - function getActor(uint160 _index) public pure returns (address) { - return address(uint160(0x50000 + _index)); - } - - function getWallet() public returns (Wallet wallet) { - wallet = new Wallet(); - } - - function assertIsOwnerERC721(address _token, address _owner, uint256[] memory _tokenIds) internal { - for (uint256 i = 0; i < _tokenIds.length; i += 1) { - bool isOwnerOfToken = MockERC721(_token).ownerOf(_tokenIds[i]) == _owner; - assertTrue(isOwnerOfToken); - } - } - - function assertIsNotOwnerERC721(address _token, address _owner, uint256[] memory _tokenIds) internal { - for (uint256 i = 0; i < _tokenIds.length; i += 1) { - bool isOwnerOfToken = MockERC721(_token).ownerOf(_tokenIds[i]) == _owner; - assertTrue(!isOwnerOfToken); - } - } - - function assertBalERC1155Eq( - address _token, - address _owner, - uint256[] memory _tokenIds, - uint256[] memory _amounts - ) internal { - require(_tokenIds.length == _amounts.length, "unequal lengths"); - - for (uint256 i = 0; i < _tokenIds.length; i += 1) { - assertEq(MockERC1155(_token).balanceOf(_owner, _tokenIds[i]), _amounts[i]); - } - } - - function assertBalERC1155Gte( - address _token, - address _owner, - uint256[] memory _tokenIds, - uint256[] memory _amounts - ) internal { - require(_tokenIds.length == _amounts.length, "unequal lengths"); - - for (uint256 i = 0; i < _tokenIds.length; i += 1) { - assertTrue(MockERC1155(_token).balanceOf(_owner, _tokenIds[i]) >= _amounts[i]); - } - } - - function assertBalERC20Eq(address _token, address _owner, uint256 _amount) internal { - assertEq(MockERC20(_token).balanceOf(_owner), _amount); - } - - function assertBalERC20Gte(address _token, address _owner, uint256 _amount) internal { - assertTrue(MockERC20(_token).balanceOf(_owner) >= _amount); - } - - function forwarders() public view returns (address[] memory) { - address[] memory _forwarders = new address[](1); - _forwarders[0] = forwarder; - return _forwarders; - } - - function setupAirdropClaimable() public { - string[] memory inputs = new string[](3); - inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRootAirdrop.ts"; - inputs[2] = Strings.toString(uint256(5)); - - bytes memory result = vm.ffi(inputs); - bytes32 root = abi.decode(result, (bytes32)); - - airdropTokenOwner = getWallet(); - - // ERC721 - for (uint256 i = 0; i < 1000; i++) { - _airdropTokenIdsERC721.push(i); - } - _airdropMerkleRootERC721 = root; - - // ERC1155 - for (uint256 i = 0; i < 5; i++) { - _airdropTokenIdsERC1155.push(i); - _airdropAmountsERC1155.push(100); - _airdropWalletClaimCountERC1155.push(1); - _airdropMerkleRootERC1155.push(root); - } - - // ERC20 - _airdropMerkleRootERC20 = root; - } -} diff --git a/src/test/utils/ChainlinkVRF.sol b/src/test/utils/ChainlinkVRF.sol deleted file mode 100644 index cd5eaf76a..000000000 --- a/src/test/utils/ChainlinkVRF.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: Apache 2.0 -pragma solidity ^0.8.0; - -contract Link { - function transferAndCall(address, uint256, bytes calldata) external returns (bool) {} -} - -contract VRFV2Wrapper { - uint256 private nextId = 5; - - function lastRequestId() external view returns (uint256 id) { - id = nextId; - } - - function calculateRequestPrice(uint32 _callbackGasLimit) external pure returns (uint256) { - return _callbackGasLimit; - } -} diff --git a/src/test/utils/Console.sol b/src/test/utils/Console.sol deleted file mode 100644 index c8e655ca6..000000000 --- a/src/test/utils/Console.sol +++ /dev/null @@ -1,1531 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.4.22 <0.9.0; - -library console { - address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); - - function _sendLogPayload(bytes memory payload) private view { - uint256 payloadLength = payload.length; - address consoleAddress = CONSOLE_ADDRESS; - assembly { - let payloadStart := add(payload, 32) - let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) - } - } - - function log() internal view { - _sendLogPayload(abi.encodeWithSignature("log()")); - } - - function logInt(int256 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(int)", p0)); - } - - function logUint(uint256 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); - } - - function logString(string memory p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); - } - - function logBool(bool p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); - } - - function logAddress(address p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); - } - - function logBytes(bytes memory p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); - } - - function logBytes1(bytes1 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); - } - - function logBytes2(bytes2 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); - } - - function logBytes3(bytes3 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); - } - - function logBytes4(bytes4 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); - } - - function logBytes5(bytes5 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); - } - - function logBytes6(bytes6 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); - } - - function logBytes7(bytes7 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); - } - - function logBytes8(bytes8 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); - } - - function logBytes9(bytes9 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); - } - - function logBytes10(bytes10 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); - } - - function logBytes11(bytes11 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); - } - - function logBytes12(bytes12 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); - } - - function logBytes13(bytes13 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); - } - - function logBytes14(bytes14 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); - } - - function logBytes15(bytes15 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); - } - - function logBytes16(bytes16 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); - } - - function logBytes17(bytes17 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); - } - - function logBytes18(bytes18 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); - } - - function logBytes19(bytes19 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); - } - - function logBytes20(bytes20 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); - } - - function logBytes21(bytes21 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); - } - - function logBytes22(bytes22 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); - } - - function logBytes23(bytes23 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); - } - - function logBytes24(bytes24 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); - } - - function logBytes25(bytes25 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); - } - - function logBytes26(bytes26 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); - } - - function logBytes27(bytes27 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); - } - - function logBytes28(bytes28 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); - } - - function logBytes29(bytes29 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); - } - - function logBytes30(bytes30 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); - } - - function logBytes31(bytes31 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); - } - - function logBytes32(bytes32 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); - } - - function log(uint256 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); - } - - function log(string memory p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); - } - - function log(bool p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); - } - - function log(address p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); - } - - function log(uint256 p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1)); - } - - function log(uint256 p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1)); - } - - function log(uint256 p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1)); - } - - function log(uint256 p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1)); - } - - function log(string memory p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1)); - } - - function log(string memory p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); - } - - function log(string memory p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); - } - - function log(string memory p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); - } - - function log(bool p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1)); - } - - function log(bool p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); - } - - function log(bool p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); - } - - function log(bool p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); - } - - function log(address p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1)); - } - - function log(address p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); - } - - function log(address p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); - } - - function log(address p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); - } - - function log(uint256 p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); - } - - function log(uint256 p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); - } - - function log(uint256 p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); - } - - function log(uint256 p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); - } - - function log(uint256 p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); - } - - function log(uint256 p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); - } - - function log(uint256 p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); - } - - function log(uint256 p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); - } - - function log(uint256 p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); - } - - function log(uint256 p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); - } - - function log(uint256 p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); - } - - function log(uint256 p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); - } - - function log(uint256 p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); - } - - function log(uint256 p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); - } - - function log(uint256 p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); - } - - function log(uint256 p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); - } - - function log(string memory p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); - } - - function log(string memory p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); - } - - function log(string memory p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); - } - - function log(string memory p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); - } - - function log(string memory p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); - } - - function log(string memory p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); - } - - function log(string memory p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); - } - - function log(string memory p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); - } - - function log(string memory p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); - } - - function log(string memory p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); - } - - function log(bool p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); - } - - function log(bool p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); - } - - function log(bool p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); - } - - function log(bool p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); - } - - function log(bool p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); - } - - function log(bool p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); - } - - function log(bool p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); - } - - function log(bool p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); - } - - function log(bool p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); - } - - function log(bool p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); - } - - function log(bool p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); - } - - function log(bool p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); - } - - function log(bool p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); - } - - function log(address p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); - } - - function log(address p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); - } - - function log(address p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); - } - - function log(address p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); - } - - function log(address p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); - } - - function log(address p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); - } - - function log(address p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); - } - - function log(address p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); - } - - function log(address p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); - } - - function log(address p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); - } - - function log(address p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); - } - - function log(address p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); - } - - function log(address p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); - } - - function log(address p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); - } - - function log(address p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); - } - - function log(address p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); - } - - function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); - } - - function log(uint256 p0, address p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3)); - } - - function log(string memory p0, address p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); - } - - function log(bool p0, address p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, string memory p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3)); - } - - function log(address p0, address p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3)); - } -} diff --git a/src/test/utils/SignatureMint1155Utils.sol b/src/test/utils/SignatureMint1155Utils.sol deleted file mode 100644 index e6be03f55..000000000 --- a/src/test/utils/SignatureMint1155Utils.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: Apache 2.0 -pragma solidity ^0.8.0; - -import "contracts/extension/interface/ISignatureMintERC1155.sol"; - -contract SignatureMint1155Utils { - bytes32 internal DOMAIN_SEPARATOR; - - constructor() { - bytes32 typeHash = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ); - bytes32 hashedName = keccak256(bytes("SignatureMintERC1155")); - bytes32 hashedVersion = keccak256(bytes("1")); - DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); - } - - bytes32 internal constant TYPEHASH = - keccak256( - "MintRequest(address to,address royaltyRecipient,uint256 royaltyBps,address primarySaleRecipient,uint256 tokenId,string uri,uint256 quantity,uint256 pricePerToken,address currency,uint128 validityStartTimestamp,uint128 validityEndTimestamp,bytes32 uid)" - ); - - function _buildDomainSeparator( - bytes32 typeHash, - bytes32 nameHash, - bytes32 versionHash - ) private view returns (bytes32) { - return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); - } - - // computes the hash of a permit - function getStructHash(ISignatureMintERC1155.MintRequest memory _req) internal pure returns (bytes32) { - return - keccak256( - bytes.concat( - abi.encode( - TYPEHASH, - _req.to, - _req.royaltyRecipient, - _req.royaltyBps, - _req.primarySaleRecipient, - _req.tokenId, - keccak256(bytes(_req.uri)) - ), - abi.encode( - _req.quantity, - _req.pricePerToken, - _req.currency, - _req.validityStartTimestamp, - _req.validityEndTimestamp, - _req.uid - ) - ) - ); - } - - // computes the hash of the fully encoded EIP-712 message for the domain, which can be used to recover the signer - function getTypedDataHash(ISignatureMintERC1155.MintRequest memory _req) public view returns (bytes32) { - return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, getStructHash(_req))); - } -} diff --git a/src/test/utils/Wallet.sol b/src/test/utils/Wallet.sol deleted file mode 100644 index e3f8dc4a9..000000000 --- a/src/test/utils/Wallet.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; -import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; - -import "../mocks/MockERC20.sol"; -import "../mocks/MockERC721.sol"; -import "../mocks/MockERC1155.sol"; - -contract Wallet is ERC721Holder, ERC1155Holder { - function transferERC20(address token, address to, uint256 amount) public { - MockERC20(token).transfer(to, amount); - } - - function setAllowanceERC20(address token, address spender, uint256 allowanceAmount) public { - MockERC20(token).approve(spender, allowanceAmount); - } - - function burnERC20(address token, uint256 amount) public { - MockERC20(token).burn(amount); - } - - function transferERC721(address token, address to, uint256 tokenId) public { - MockERC721(token).transferFrom(address(this), to, tokenId); - } - - function setApprovalForAllERC721(address token, address operator, bool toApprove) public { - MockERC721(token).setApprovalForAll(operator, toApprove); - } - - function burnERC721(address token, uint256 tokenId) public { - MockERC721(token).burn(tokenId); - } - - function transferERC1155(address token, address to, uint256 tokenId, uint256 amount, bytes calldata data) external { - MockERC1155(token).safeTransferFrom(address(this), to, tokenId, amount, data); - } - - function setApprovalForAllERC1155(address token, address operator, bool toApprove) public { - MockERC1155(token).setApprovalForAll(operator, toApprove); - } - - function burnERC1155(address token, uint256 tokenId, uint256 amount) public { - MockERC1155(token).burn(address(this), tokenId, amount); - } -} diff --git a/src/test/vote-BTT/initialize/initialize.t.sol b/src/test/vote-BTT/initialize/initialize.t.sol deleted file mode 100644 index 9e9227ac6..000000000 --- a/src/test/vote-BTT/initialize/initialize.t.sol +++ /dev/null @@ -1,200 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC20Vote } from "contracts/base/ERC20Vote.sol"; - -contract MyVoteERC20 is VoteERC20 { - function eip712NameHash() external view returns (bytes32) { - return _EIP712NameHash(); - } - - function eip712VersionHash() external view returns (bytes32) { - return _EIP712VersionHash(); - } -} - -contract VoteERC20Test_Initialize is BaseTest { - address payable public implementation; - address payable public proxy; - address public token; - uint256 public initialVotingDelay; - uint256 public initialVotingPeriod; - uint256 public initialProposalThreshold; - uint256 public initialVoteQuorumFraction; - - event VotingDelaySet(uint256 oldVotingDelay, uint256 newVotingDelay); - event VotingPeriodSet(uint256 oldVotingPeriod, uint256 newVotingPeriod); - event ProposalThresholdSet(uint256 oldProposalThreshold, uint256 newProposalThreshold); - event QuorumNumeratorUpdated(uint256 oldQuorumNumerator, uint256 newQuorumNumerator); - - function setUp() public override { - super.setUp(); - - // Deploy voting token - token = address(new ERC20Vote(deployer, "Voting VoteERC20", "VT")); - - // Voting param initial values - initialVotingDelay = 5; - initialVotingPeriod = 10; - initialProposalThreshold = 100; - initialVoteQuorumFraction = 50; - - // Deploy implementation. - implementation = payable(address(new MyVoteERC20())); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = payable( - address( - new TWProxy( - implementation, - abi.encodeCall( - VoteERC20.initialize, - ( - NAME, - CONTRACT_URI, - forwarders(), - token, - initialVotingDelay, - initialVotingPeriod, - initialProposalThreshold, - initialVoteQuorumFraction - ) - ) - ) - ) - ); - } - - function test_initialize_initializingImplementation() public { - vm.expectRevert("Initializable: contract is already initialized"); - VoteERC20(implementation).initialize( - NAME, - CONTRACT_URI, - forwarders(), - token, - initialVotingDelay, - initialVotingPeriod, - initialProposalThreshold, - initialVoteQuorumFraction - ); - } - - modifier whenNotImplementation() { - _; - } - - function test_initialize_proxyAlreadyInitialized() public whenNotImplementation { - vm.expectRevert("Initializable: contract is already initialized"); - MyVoteERC20(proxy).initialize( - NAME, - CONTRACT_URI, - forwarders(), - token, - initialVotingDelay, - initialVotingPeriod, - initialProposalThreshold, - initialVoteQuorumFraction - ); - } - - modifier whenProxyNotInitialized() { - proxy = payable(address(new TWProxy(implementation, ""))); - _; - } - - function test_initialize() public whenNotImplementation whenProxyNotInitialized { - MyVoteERC20(proxy).initialize( - NAME, - CONTRACT_URI, - forwarders(), - token, - initialVotingDelay, - initialVotingPeriod, - initialProposalThreshold, - initialVoteQuorumFraction - ); - - // check state - MyVoteERC20 voteContract = MyVoteERC20(proxy); - - assertEq(voteContract.eip712NameHash(), keccak256(bytes(NAME))); - assertEq(voteContract.eip712VersionHash(), keccak256(bytes("1"))); - - address[] memory _trustedForwarders = forwarders(); - for (uint256 i = 0; i < _trustedForwarders.length; i++) { - assertTrue(voteContract.isTrustedForwarder(_trustedForwarders[i])); - } - - assertEq(voteContract.name(), NAME); - assertEq(voteContract.contractURI(), CONTRACT_URI); - assertEq(voteContract.votingDelay(), initialVotingDelay); - assertEq(voteContract.votingPeriod(), initialVotingPeriod); - assertEq(voteContract.proposalThreshold(), initialProposalThreshold); - assertEq(voteContract.quorumNumerator(), initialVoteQuorumFraction); - assertEq(address(voteContract.token()), token); - } - - function test_initialize_event_VotingDelaySet() public whenNotImplementation whenProxyNotInitialized { - vm.expectEmit(false, false, false, true); - emit VotingDelaySet(0, initialVotingDelay); - MyVoteERC20(proxy).initialize( - NAME, - CONTRACT_URI, - forwarders(), - token, - initialVotingDelay, - initialVotingPeriod, - initialProposalThreshold, - initialVoteQuorumFraction - ); - } - - function test_initialize_event_VotingPeriodSet() public whenNotImplementation whenProxyNotInitialized { - vm.expectEmit(false, false, false, true); - emit VotingPeriodSet(0, initialVotingPeriod); - MyVoteERC20(proxy).initialize( - NAME, - CONTRACT_URI, - forwarders(), - token, - initialVotingDelay, - initialVotingPeriod, - initialProposalThreshold, - initialVoteQuorumFraction - ); - } - - function test_initialize_event_ProposalThresholdSet() public whenNotImplementation whenProxyNotInitialized { - vm.expectEmit(false, false, false, true); - emit ProposalThresholdSet(0, initialProposalThreshold); - MyVoteERC20(proxy).initialize( - NAME, - CONTRACT_URI, - forwarders(), - token, - initialVotingDelay, - initialVotingPeriod, - initialProposalThreshold, - initialVoteQuorumFraction - ); - } - - function test_initialize_event_QuorumNumeratorUpdated() public whenNotImplementation whenProxyNotInitialized { - vm.expectEmit(false, false, false, true); - emit QuorumNumeratorUpdated(0, initialVoteQuorumFraction); - MyVoteERC20(proxy).initialize( - NAME, - CONTRACT_URI, - forwarders(), - token, - initialVotingDelay, - initialVotingPeriod, - initialProposalThreshold, - initialVoteQuorumFraction - ); - } -} diff --git a/src/test/vote-BTT/initialize/initialize.tree b/src/test/vote-BTT/initialize/initialize.tree deleted file mode 100644 index 6b935174b..000000000 --- a/src/test/vote-BTT/initialize/initialize.tree +++ /dev/null @@ -1,30 +0,0 @@ -initialize( - string memory _name, - string memory _contractURI, - address[] memory _trustedForwarders, - address _token, - uint256 _initialVotingDelay, - uint256 _initialVotingPeriod, - uint256 _initialProposalThreshold, - uint256 _initialVoteQuorumFraction -) -├── when initializing the implementation contract (not proxy) -│ └── it should revert ✅ -└── when it is a proxy to the implementation - └── when it is already initialized - │ └── it should revert ✅ - └── when it is not initialized - └── it should set trustedForwarder mapping to true for all addresses in `_trustedForwarders` ✅ - └── it should correctly set EIP712 name hash and version hash ✅ - └── it should set name to `_name` input param ✅ - └── it should set contractURI to `_contractURI` param value ✅ - └── it should set votingDelay to `_initialVotingDelay` param value ✅ - └── it should emit VotingDelaySet event ✅ - └── it should set votingPeriod to `_initialVotingPeriod` param value ✅ - └── it should emit VotingPeriodSet event ✅ - └── it should set proposalThreshold to `_initialProposalThreshold` param value ✅ - └── it should emit ProposalThresholdSet event ✅ - └── it should set voting token address as the `_token` param value ✅ - └── it should set initial quorum numerator as `_initialVoteQuorumFraction` param value ✅ - └── it should emit QuorumNumeratorUpdated event ✅ - diff --git a/src/test/vote-BTT/other-functions/other.t.sol b/src/test/vote-BTT/other-functions/other.t.sol deleted file mode 100644 index 960a94e7e..000000000 --- a/src/test/vote-BTT/other-functions/other.t.sol +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.0; - -import "../../utils/BaseTest.sol"; -import { IStaking721 } from "contracts/extension/interface/IStaking721.sol"; -import { IERC2981 } from "contracts/eip/interface/IERC2981.sol"; - -import "@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155ReceiverUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC20Vote } from "contracts/base/ERC20Vote.sol"; - -contract MyVoteERC20 is VoteERC20 {} - -contract VoteERC20Test_OtherFunctions is BaseTest { - address payable public implementation; - address payable public proxy; - - address public token; - uint256 public initialVotingDelay; - uint256 public initialVotingPeriod; - uint256 public initialProposalThreshold; - uint256 public initialVoteQuorumFraction; - - MyVoteERC20 public voteContract; - - function setUp() public override { - super.setUp(); - - // Deploy voting token - vm.prank(deployer); - token = address(new ERC20Vote(deployer, "Voting VoteERC20", "VT")); - - // Voting param initial values - initialVotingDelay = 1; - initialVotingPeriod = 100; - initialProposalThreshold = 10; - initialVoteQuorumFraction = 1; - - // Deploy implementation. - implementation = payable(address(new MyVoteERC20())); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = payable( - address( - new TWProxy( - implementation, - abi.encodeCall( - VoteERC20.initialize, - ( - NAME, - CONTRACT_URI, - forwarders(), - token, - initialVotingDelay, - initialVotingPeriod, - initialProposalThreshold, - initialVoteQuorumFraction - ) - ) - ) - ) - ); - - voteContract = MyVoteERC20(proxy); - } - - function test_contractType() public { - assertEq(voteContract.contractType(), bytes32("VoteERC20")); - } - - function test_contractVersion() public { - assertEq(voteContract.contractVersion(), uint8(1)); - } - - function test_supportsInterface() public { - assertTrue(voteContract.supportsInterface(type(IERC165).interfaceId)); - assertTrue(voteContract.supportsInterface(type(IERC165Upgradeable).interfaceId)); - assertTrue(voteContract.supportsInterface(type(IERC721ReceiverUpgradeable).interfaceId)); - assertTrue(voteContract.supportsInterface(type(IERC1155ReceiverUpgradeable).interfaceId)); - // assertTrue(voteContract.supportsInterface(type(IGovernorUpgradeable).interfaceId)); - - // false for other not supported interfaces - assertFalse(voteContract.supportsInterface(type(IStaking721).interfaceId)); - } -} diff --git a/src/test/vote-BTT/other-functions/other.tree b/src/test/vote-BTT/other-functions/other.tree deleted file mode 100644 index 2649d89ae..000000000 --- a/src/test/vote-BTT/other-functions/other.tree +++ /dev/null @@ -1,9 +0,0 @@ -contractType() -├── it should return bytes32("VoteERC20") ✅ - -contractVersion() -├── it should return uint8(1) ✅ - -supportsInterface(bytes4 interfaceId) -├── it should return true for supported interface ✅ -├── it should return false for not supported interface ✅ diff --git a/src/test/vote-BTT/propose/propose.t.sol b/src/test/vote-BTT/propose/propose.t.sol deleted file mode 100644 index 4bdd7f2ad..000000000 --- a/src/test/vote-BTT/propose/propose.t.sol +++ /dev/null @@ -1,281 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC20Vote } from "contracts/base/ERC20Vote.sol"; - -contract MyVoteERC20 is VoteERC20 {} - -contract VoteERC20Test_Propose is BaseTest { - address payable public implementation; - address payable public proxy; - address internal caller; - string internal _contractURI; - - address public token; - uint256 public initialVotingDelay; - uint256 public initialVotingPeriod; - uint256 public initialProposalThreshold; - uint256 public initialVoteQuorumFraction; - - uint256 public proposalIdOne; - address[] public targetsOne; - uint256[] public valuesOne; - bytes[] public calldatasOne; - string public descriptionOne; - - uint256 public proposalIdTwo; - address[] public targetsTwo; - uint256[] public valuesTwo; - bytes[] public calldatasTwo; - string public descriptionTwo; - - MyVoteERC20 internal voteContract; - - event ProposalCreated( - uint256 proposalId, - address proposer, - address[] targets, - uint256[] values, - string[] signatures, - bytes[] calldatas, - uint256 startBlock, - uint256 endBlock, - string description - ); - - function setUp() public override { - super.setUp(); - - // Deploy voting token - vm.prank(deployer); - token = address(new ERC20Vote(deployer, "Voting VoteERC20", "VT")); - - // Voting param initial values - initialVotingDelay = 1; - initialVotingPeriod = 100; - initialProposalThreshold = 10; - initialVoteQuorumFraction = 1; - - // Deploy implementation. - implementation = payable(address(new MyVoteERC20())); - - caller = getActor(1); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = payable( - address( - new TWProxy( - implementation, - abi.encodeCall( - VoteERC20.initialize, - ( - NAME, - CONTRACT_URI, - forwarders(), - token, - initialVotingDelay, - initialVotingPeriod, - initialProposalThreshold, - initialVoteQuorumFraction - ) - ) - ) - ) - ); - - voteContract = MyVoteERC20(proxy); - _contractURI = "ipfs://contracturi"; - - // mint governance tokens - vm.startPrank(deployer); - ERC20Vote(token).mintTo(caller, 100); - ERC20Vote(token).mintTo(deployer, 100); - vm.stopPrank(); - - // delegate votes to self - vm.prank(caller); - ERC20Vote(token).delegate(caller); - vm.prank(deployer); - ERC20Vote(token).delegate(deployer); - - vm.roll(2); - - // create first proposal - _createProposalOne(); - } - - function _createProposalOne() internal { - descriptionOne = "set proposal one"; - - bytes memory data = abi.encodeWithSelector(VoteERC20.setContractURI.selector, _contractURI); - - targetsOne.push(address(voteContract)); - valuesOne.push(0); - calldatasOne.push(data); - - vm.prank(deployer); - proposalIdOne = voteContract.propose(targetsOne, valuesOne, calldatasOne, descriptionOne); - } - - function _setupProposalTwo() internal { - descriptionTwo = "set proposal two"; - - bytes memory data = abi.encodeWithSelector(VoteERC20.setContractURI.selector, _contractURI); - - targetsTwo.push(address(voteContract)); - valuesTwo.push(0); - calldatasTwo.push(data); - } - - function test_propose_votesBelowThreshold() public { - _setupProposalTwo(); - - vm.prank(address(0x123)); // random address that doesn't have threshold votes - vm.expectRevert("Governor: proposer votes below proposal threshold"); - voteContract.propose(targetsTwo, valuesTwo, calldatasTwo, descriptionTwo); - } - - modifier hasThresholdVotes() { - _; - } - - function test_propose_emptyTargets() public hasThresholdVotes { - address[] memory _targets; - uint256[] memory _values; - bytes[] memory _calldatas; - string memory _description; - - vm.prank(caller); - vm.expectRevert("Governor: empty proposal"); - voteContract.propose(_targets, _values, _calldatas, _description); - } - - modifier whenNotEmptyTargets() { - _; - } - - function test_propose_lengthMismatchTargetsValues() public hasThresholdVotes whenNotEmptyTargets { - _setupProposalTwo(); - - uint256[] memory _values; - - vm.prank(caller); - vm.expectRevert("Governor: invalid proposal length"); - voteContract.propose(targetsTwo, _values, calldatasTwo, descriptionTwo); - } - - modifier whenTargetValuesEqualLength() { - _; - } - - function test_propose_lengthMismatchTargetsCalldatas() - public - hasThresholdVotes - whenNotEmptyTargets - whenTargetValuesEqualLength - { - _setupProposalTwo(); - - bytes[] memory _calldatas; - - vm.prank(caller); - vm.expectRevert("Governor: invalid proposal length"); - voteContract.propose(targetsTwo, valuesTwo, _calldatas, descriptionTwo); - } - - modifier whenTargetCalldatasEqualLength() { - _; - } - - function test_propose_proposalAlreadyExists() - public - hasThresholdVotes - whenNotEmptyTargets - whenTargetValuesEqualLength - whenTargetCalldatasEqualLength - { - // creating proposalOne again - - vm.prank(caller); - vm.expectRevert("Governor: proposal already exists"); - voteContract.propose(targetsOne, valuesOne, calldatasOne, descriptionOne); - } - - modifier whenProposalNotAlreadyExists() { - _; - } - - function test_propose() - public - hasThresholdVotes - whenNotEmptyTargets - whenTargetValuesEqualLength - whenTargetCalldatasEqualLength - whenProposalNotAlreadyExists - { - _setupProposalTwo(); - - vm.prank(caller); - proposalIdTwo = voteContract.propose(targetsTwo, valuesTwo, calldatasTwo, descriptionTwo); - - assertEq(voteContract.proposalSnapshot(proposalIdTwo), voteContract.votingDelay() + block.number); - assertEq( - voteContract.proposalDeadline(proposalIdTwo), - voteContract.proposalSnapshot(proposalIdTwo) + voteContract.votingPeriod() - ); - assertEq(voteContract.proposalIndex(), 2); // because two proposals have been created - assertEq(voteContract.getAllProposals().length, 2); - - ( - uint256 _proposalId, - address _proposer, - uint256 _startBlock, - uint256 _endBlock, - string memory _description - ) = voteContract.proposals(1); - - assertEq(_proposalId, proposalIdTwo); - assertEq(_proposer, caller); - assertEq(_startBlock, voteContract.proposalSnapshot(proposalIdTwo)); - assertEq(_endBlock, voteContract.proposalDeadline(proposalIdTwo)); - assertEq(_description, descriptionTwo); - } - - function test_propose_event_ProposalCreated() - public - hasThresholdVotes - whenNotEmptyTargets - whenTargetValuesEqualLength - whenTargetCalldatasEqualLength - whenProposalNotAlreadyExists - { - _setupProposalTwo(); - uint256 _expectedProposalId = voteContract.hashProposal( - targetsTwo, - valuesTwo, - calldatasTwo, - keccak256(bytes(descriptionTwo)) - ); - string[] memory signatures = new string[](targetsTwo.length); - - vm.startPrank(caller); - vm.expectEmit(false, false, false, true); - emit ProposalCreated( - _expectedProposalId, - caller, - targetsTwo, - valuesTwo, - signatures, - calldatasTwo, - voteContract.votingDelay() + block.number, - voteContract.votingDelay() + block.number + voteContract.votingPeriod(), - descriptionTwo - ); - voteContract.propose(targetsTwo, valuesTwo, calldatasTwo, descriptionTwo); - vm.stopPrank(); - } -} diff --git a/src/test/vote-BTT/propose/propose.tree b/src/test/vote-BTT/propose/propose.tree deleted file mode 100644 index 5df017a7e..000000000 --- a/src/test/vote-BTT/propose/propose.tree +++ /dev/null @@ -1,26 +0,0 @@ -propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description -) -├── when caller has votes below proposal threshold - │ └── it should revert ✅ - └── when caller has votes above or equal to proposal threshold - └── when length of `targets` is zero - │ └── it should revert ✅ - └── when length of `targets` is not zero - └── when lengths of `targets` and `values` not equal - │ └── it should revert ✅ - └── when lengths of `targets` and `values` are equal - └── when lengths of `targets` and `calldatas` not equal - │ └── it should revert ✅ - └── when lengths of `targets` and `calldatas` are equal - └── when proposal already exists - │ └── it should revert ✅ - └── when proposal doesn't already exist - └── it should set vote start deadline equal to block number plus voting delay ✅ - └── it should set vote end deadline equal to voting period plus vote start deadline ✅ - └── it should increment proposalIndex by 1 ✅ - └── it should add the new proposal in proposals mapping ✅ - └── it should emit ProposalCreated event ✅ \ No newline at end of file diff --git a/src/test/vote-BTT/set-contract-uri/setContractURI.t.sol b/src/test/vote-BTT/set-contract-uri/setContractURI.t.sol deleted file mode 100644 index cc35af82e..000000000 --- a/src/test/vote-BTT/set-contract-uri/setContractURI.t.sol +++ /dev/null @@ -1,130 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -import "../../utils/BaseTest.sol"; - -import { TWProxy } from "contracts/infra/TWProxy.sol"; -import { ERC20Vote } from "contracts/base/ERC20Vote.sol"; - -contract MyVoteERC20 is VoteERC20 {} - -contract VoteERC20Test_SetContractURI is BaseTest { - address payable public implementation; - address payable public proxy; - address internal caller; - string internal _contractURI; - - address public token; - uint256 public initialVotingDelay; - uint256 public initialVotingPeriod; - uint256 public initialProposalThreshold; - uint256 public initialVoteQuorumFraction; - - uint256 public proposalId; - address[] public targets; - uint256[] public values; - bytes[] public calldatas; - string public description; - - MyVoteERC20 internal voteContract; - - function setUp() public override { - super.setUp(); - - // Deploy voting token - vm.prank(deployer); - token = address(new ERC20Vote(deployer, "Voting VoteERC20", "VT")); - - // Voting param initial values - initialVotingDelay = 1; - initialVotingPeriod = 100; - initialProposalThreshold = 10; - initialVoteQuorumFraction = 1; - - // Deploy implementation. - implementation = payable(address(new MyVoteERC20())); - - caller = getActor(1); - - // Deploy proxy pointing to implementaion. - vm.prank(deployer); - proxy = payable( - address( - new TWProxy( - implementation, - abi.encodeCall( - VoteERC20.initialize, - ( - NAME, - CONTRACT_URI, - forwarders(), - token, - initialVotingDelay, - initialVotingPeriod, - initialProposalThreshold, - initialVoteQuorumFraction - ) - ) - ) - ) - ); - - voteContract = MyVoteERC20(proxy); - _contractURI = "ipfs://contracturi"; - - // mint governance tokens - vm.startPrank(deployer); - ERC20Vote(token).mintTo(caller, 100); - ERC20Vote(token).mintTo(deployer, 100); - vm.stopPrank(); - - // delegate votes to self - vm.prank(caller); - ERC20Vote(token).delegate(caller); - vm.prank(deployer); - ERC20Vote(token).delegate(deployer); - } - - function _createProposalForSetContractURI() internal { - description = "set contract URI"; - - bytes memory data = abi.encodeWithSelector(VoteERC20.setContractURI.selector, _contractURI); - - targets.push(address(voteContract)); - values.push(0); - calldatas.push(data); - - vm.prank(deployer); - proposalId = voteContract.propose(targets, values, calldatas, description); - } - - function test_setContractURI_callerNotAuthorized() public { - vm.prank(address(0x123)); - vm.expectRevert("Governor: onlyGovernance"); - voteContract.setContractURI(_contractURI); - } - - modifier whenCallerAuthorized() { - vm.roll(2); - _createProposalForSetContractURI(); - _; - } - - function test_setContractURI_empty() public whenCallerAuthorized { - vm.roll(10); - // first try execute without votes - vm.expectRevert("Governor: proposal not successful"); - voteContract.execute(targets, values, calldatas, keccak256(bytes(description))); - - // vote on proposal - vm.prank(caller); - voteContract.castVote(proposalId, 1); - - // execute - vm.roll(200); // deadline must be over, before execute can be called - voteContract.execute(targets, values, calldatas, keccak256(bytes(description))); - - // check state: get contract uri - assertEq(voteContract.contractURI(), _contractURI); - } -} diff --git a/src/test/vote-BTT/set-contract-uri/setContractURI.tree b/src/test/vote-BTT/set-contract-uri/setContractURI.tree deleted file mode 100644 index f7819fc38..000000000 --- a/src/test/vote-BTT/set-contract-uri/setContractURI.tree +++ /dev/null @@ -1,8 +0,0 @@ -setContractURI(string calldata uri) -├── when caller is not authorized (i.e. execution not going through governance proposals) - │ └── it should revert ✅ - └── when caller is authorized (execution through governance proposals) - └── when `uri` is empty - │ └── it should update contract URI to empty string ✅ - └── when `uri` is not empty - └── it should update contract URI to `uri` ✅ \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b446e0d3c..05b4beb14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,6 +29,16 @@ chalk "^2.4.2" js-tokens "^4.0.0" +"@balena/dockerignore@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz#9ffe4726915251e8eb69f44ef3547e0da2c03e0d" + integrity sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q== + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -73,7 +83,21 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b" integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": +"@ethereumjs/rlp@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" + integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== + +"@ethereumjs/util@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4" + integrity sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA== + dependencies: + "@ethereumjs/rlp" "^4.0.1" + ethereum-cryptography "^2.0.0" + micro-ftch "^0.3.1" + +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -112,7 +136,7 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/address@5.7.0", "@ethersproject/address@^5.7.0": +"@ethersproject/address@5.7.0", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== @@ -415,6 +439,11 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + "@humanwhocodes/config-array@^0.11.13": version "0.11.14" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" @@ -486,6 +515,174 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@matterlabs/hardhat-zksync-deploy@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-deploy/-/hardhat-zksync-deploy-0.9.0.tgz#026815303df792af50d722e4a59b5aa26baa5ed7" + integrity sha512-F9qPa7+Etq9/zAWEhsJ+oHCJy+B+yXxt8Tv1wgJsw5yc/race7VIdrdWW6xUS5YNpwhsiuM2cxYBcrE+FSU/TA== + dependencies: + "@matterlabs/hardhat-zksync-solc" "^1.0.5" + chai "^4.3.6" + chalk "4.1.2" + fs-extra "^11.2.0" + glob "^10.3.10" + lodash "^4.17.21" + sinon "^17.0.1" + sinon-chai "^3.7.0" + ts-morph "^21.0.1" + +"@matterlabs/hardhat-zksync-deploy@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-deploy/-/hardhat-zksync-deploy-1.3.0.tgz#5c2b723318ddf6c4d3929ec225401864ff54557a" + integrity sha512-4UHOgOwIBC4JA3W8DE9GHqbAuBhCPAjtM+Oew1aiYYGkIsPUAMYsH35+4I2FzJsYyE6mD6ATmoS/HfZweQHTlQ== + dependencies: + "@matterlabs/hardhat-zksync-solc" "^1.0.4" + chai "^4.3.6" + chalk "4.1.2" + fs-extra "^11.2.0" + glob "^10.3.10" + lodash "^4.17.21" + sinon "^17.0.1" + sinon-chai "^3.7.0" + ts-morph "^21.0.1" + +"@matterlabs/hardhat-zksync-node@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-node/-/hardhat-zksync-node-0.1.0.tgz#de9b6b277727457ee981208030f33d06537db5d7" + integrity sha512-P3QZkcajplkQZg4Mj7soAlvH7JZObn853GtsD6NRCcjnwn3Id2yV1B5Iokg/BACRAwXCSLHRTn4nc2B/xkyqfg== + dependencies: + "@matterlabs/hardhat-zksync-solc" "^1.1.4" + axios "^1.4.0" + chai "^4.3.6" + chalk "4.1.2" + fs-extra "^11.1.1" + proxyquire "^2.1.3" + sinon "^17.0.1" + sinon-chai "^3.7.0" + undici "^5.14.0" + +"@matterlabs/hardhat-zksync-solc@^1.0.4", "@matterlabs/hardhat-zksync-solc@^1.0.5", "@matterlabs/hardhat-zksync-solc@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-1.1.4.tgz#04a2fad6fb6b6944c64ad969080ee65b9af3f617" + integrity sha512-4/usbogh9neewR2/v8Dn2OzqVblZMUuT/iH2MyPZgPRZYQlL4SlZtMvokU9UQjZT6iSoaKCbbdWESHDHSzfUjA== + dependencies: + "@nomiclabs/hardhat-docker" "^2.0.0" + chai "^4.3.6" + chalk "4.1.2" + debug "^4.3.4" + dockerode "^4.0.2" + fs-extra "^11.1.1" + proper-lockfile "^4.1.2" + semver "^7.5.1" + sinon "^17.0.1" + sinon-chai "^3.7.0" + undici "^5.14.0" + +"@matterlabs/hardhat-zksync-upgradable@^0.4.0": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-upgradable/-/hardhat-zksync-upgradable-0.4.1.tgz#b5e7833da9921516b52bee8b897d4f3e78a72075" + integrity sha512-GCz+R7Nj41N70N6v8/0J3oCkaOtjuOOOxedMdOoKow6Zaoe+/vGIDfAqhVOZaJn2sIVdJpSizjaoHEYDNe/HMQ== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@matterlabs/hardhat-zksync-deploy" "^0.9.0" + "@matterlabs/hardhat-zksync-solc" "^1.1.4" + "@openzeppelin/upgrades-core" "1.27.0" + chalk "4.1.2" + compare-versions "^6.0.0" + dockerode "^3.3.4" + ethereumjs-util "^7.1.5" + ethers "~5.7.2" + fs-extra "^11.1.1" + hardhat "^2.14.0" + proper-lockfile "^4.1.1" + solidity-ast "npm:solidity-ast@0.4.45" + zksync-ethers "^5.0.0" + +"@matterlabs/hardhat-zksync-verify@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-verify/-/hardhat-zksync-verify-0.6.0.tgz#8923a124d7d2a84fea9a037b654a733bec07836e" + integrity sha512-LndlCZLgAd7r2zB/VJCuLOhVNpcWFbk3UOqv5sVOtO+XdV0A74tiLaxlyGNILm9lsbP+cPnGArDq9KI0yV4FEw== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@ethersproject/address" "5.7.0" + "@matterlabs/hardhat-zksync-solc" "^1.1.4" + "@nomicfoundation/hardhat-verify" "^2.0.0" + "@openzeppelin/contracts" "^4.9.2" + axios "^1.6.2" + cbor "^8.1.0" + chai "^4.3.6" + chalk "4.1.2" + debug "^4.1.1" + hardhat "^2.14.0" + sinon "^17.0.1" + sinon-chai "^3.7.0" + zksync-ethers "^5.0.0" + +"@matterlabs/hardhat-zksync@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync/-/hardhat-zksync-0.1.0.tgz#83fb8562f8be144615ec90a0f2dff33956e2ee09" + integrity sha512-LyiTnh7of0TSzXqSBC2cp8pjwPlVcetBCmAFWFiEXEfwAPSiQ0Bb2O/z5g999PcoTjm1NhHa6tT6YpZCtcPCPA== + dependencies: + "@matterlabs/hardhat-zksync-deploy" "^0.9.0" + "@matterlabs/hardhat-zksync-node" "^0.1.0" + "@matterlabs/hardhat-zksync-solc" "^1.1.4" + "@matterlabs/hardhat-zksync-upgradable" "^0.4.0" + "@matterlabs/hardhat-zksync-verify" "^0.6.0" + "@matterlabs/zksync-contracts" "^0.6.1" + "@nomicfoundation/hardhat-verify" "^2.0.0" + "@nomiclabs/hardhat-ethers" "^2.2.1" + "@openzeppelin/contracts" "^4.9.2" + "@openzeppelin/contracts-upgradeable" "^4.9.2" + "@openzeppelin/upgrades-core" "^1.27.0" + chai "^4.3.7" + ethers "^5.7.2" + hardhat "^2.14.0" + sinon "^17.0.1" + sinon-chai "^3.7.0" + zksync-ethers "^5.0.0" + +"@matterlabs/zksync-contracts@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@matterlabs/zksync-contracts/-/zksync-contracts-0.6.1.tgz#39f061959d5890fd0043a2f1ae710f764b172230" + integrity sha512-+hucLw4DhGmTmQlXOTEtpboYCaOm/X2VJcWmnW4abNcOgQXEHX+mTxQrxEfPjIZT0ZE6z5FTUrOK9+RgUZwBMQ== + +"@metamask/eth-sig-util@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" + integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^6.2.1" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + +"@noble/curves@1.3.0", "@noble/curves@~1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e" + integrity sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA== + dependencies: + "@noble/hashes" "1.3.3" + +"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" + integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== + +"@noble/hashes@1.3.3", "@noble/hashes@~1.3.2": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== + +"@noble/hashes@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + +"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -507,21 +704,424 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@openzeppelin/contracts-upgradeable@^4.4.2", "@openzeppelin/contracts-upgradeable@^4.9.6": +"@nomicfoundation/edr-darwin-arm64@0.3.7": + version "0.3.7" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.3.7.tgz#c204edc79643624dbd431b489b254778817d8244" + integrity sha512-6tK9Lv/lSfyBvpEQ4nsTfgxyDT1y1Uv/x8Wa+aB+E8qGo3ToexQ1BMVjxJk6PChXCDOWxB3B4KhqaZFjdhl3Ow== + +"@nomicfoundation/edr-darwin-x64@0.3.7": + version "0.3.7" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.3.7.tgz#c3b394445084270cc5250d6c1869b0574e7ef810" + integrity sha512-1RrQ/1JPwxrYO69e0tglFv5H+ggour5Ii3bb727+yBpBShrxtOTQ7fZyfxA5h62LCN+0Z9wYOPeQ7XFcVurMaQ== + +"@nomicfoundation/edr-linux-arm64-gnu@0.3.7": + version "0.3.7" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.3.7.tgz#6d65545a44d1323bb7ab08c3306947165d2071de" + integrity sha512-ds/CKlBoVXIihjhflhgPn13EdKWed6r5bgvMs/YwRqT5wldQAQJZWAfA2+nYm0Yi2gMGh1RUpBcfkyl4pq7G+g== + +"@nomicfoundation/edr-linux-arm64-musl@0.3.7": + version "0.3.7" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.3.7.tgz#5368534bceac1a8c18b1be6b908caca5d39b0c03" + integrity sha512-e29udiRaPujhLkM3+R6ju7QISrcyOqpcaxb2FsDWBkuD7H8uU9JPZEyyUIpEp5uIY0Jh1eEJPKZKIXQmQAEAuw== + +"@nomicfoundation/edr-linux-x64-gnu@0.3.7": + version "0.3.7" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.3.7.tgz#42349bf5941dbb54a5719942924c6e4e8cde348e" + integrity sha512-/xkjmTyv+bbJ4akBCW0qzFKxPOV4AqLOmqurov+s9umHb16oOv72osSa3SdzJED2gHDaKmpMITT4crxbar4Axg== + +"@nomicfoundation/edr-linux-x64-musl@0.3.7": + version "0.3.7" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.3.7.tgz#e6babe11c9a8012f1284e6e48c3551861f2a7cd4" + integrity sha512-QwBP9xlmsbf/ldZDGLcE4QiAb8Zt46E/+WLpxHBATFhGa7MrpJh6Zse+h2VlrT/SYLPbh2cpHgSmoSlqVxWG9g== + +"@nomicfoundation/edr-win32-x64-msvc@0.3.7": + version "0.3.7" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.3.7.tgz#1504b98f305f03be153b0220a546985660de9dc6" + integrity sha512-j/80DEnkxrF2ewdbk/gQ2EOPvgF0XSsg8D0o4+6cKhUVAW6XwtWKzIphNL6dyD2YaWEPgIrNvqiJK/aln0ww4Q== + +"@nomicfoundation/edr@^0.3.5": + version "0.3.7" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.3.7.tgz#9c75edf1fcf601617905b2c89acf103f4786d017" + integrity sha512-v2JFWnFKRsnOa6PDUrD+sr8amcdhxnG/YbL7LzmgRGU1odWEyOF4/EwNeUajQr4ZNKVWrYnJ6XjydXtUge5OBQ== + optionalDependencies: + "@nomicfoundation/edr-darwin-arm64" "0.3.7" + "@nomicfoundation/edr-darwin-x64" "0.3.7" + "@nomicfoundation/edr-linux-arm64-gnu" "0.3.7" + "@nomicfoundation/edr-linux-arm64-musl" "0.3.7" + "@nomicfoundation/edr-linux-x64-gnu" "0.3.7" + "@nomicfoundation/edr-linux-x64-musl" "0.3.7" + "@nomicfoundation/edr-win32-x64-msvc" "0.3.7" + +"@nomicfoundation/ethereumjs-common@4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz#9901f513af2d4802da87c66d6f255b510bef5acb" + integrity sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg== + dependencies: + "@nomicfoundation/ethereumjs-util" "9.0.4" + +"@nomicfoundation/ethereumjs-rlp@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz#66c95256fc3c909f6fb18f6a586475fc9762fa30" + integrity sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw== + +"@nomicfoundation/ethereumjs-tx@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz#b0ceb58c98cc34367d40a30d255d6315b2f456da" + integrity sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw== + dependencies: + "@nomicfoundation/ethereumjs-common" "4.0.4" + "@nomicfoundation/ethereumjs-rlp" "5.0.4" + "@nomicfoundation/ethereumjs-util" "9.0.4" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-util@9.0.4": + version "9.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz#84c5274e82018b154244c877b76bc049a4ed7b38" + integrity sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q== + dependencies: + "@nomicfoundation/ethereumjs-rlp" "5.0.4" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/hardhat-chai-matchers@^2.0.0": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-2.0.6.tgz#ef88be3bd666adf29c06ac7882e96c8dbaaa32ba" + integrity sha512-Te1Uyo9oJcTCF0Jy9dztaLpshmlpjLf2yPtWXlXuLjMt3RRSmJLm/+rKVTW6gfadAEs12U/it6D0ZRnnRGiICQ== + dependencies: + "@types/chai-as-promised" "^7.1.3" + chai-as-promised "^7.1.1" + deep-eql "^4.0.1" + ordinal "^1.0.3" + +"@nomicfoundation/hardhat-ethers@^3.0.0": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.5.tgz#0422c2123dec7c42e7fb2be8e1691f1d9708db56" + integrity sha512-RNFe8OtbZK6Ila9kIlHp0+S80/0Bu/3p41HUpaRIoHLm6X3WekTd83vob3rE54Duufu1edCiBDxspBzi2rxHHw== + dependencies: + debug "^4.1.1" + lodash.isequal "^4.5.0" + +"@nomicfoundation/hardhat-foundry@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-foundry/-/hardhat-foundry-1.1.1.tgz#db72b1f33f9cfaecc27e67f69ad436f8710162d6" + integrity sha512-cXGCBHAiXas9Pg9MhMOpBVQCkWRYoRFG7GJJAph+sdQsfd22iRs5U5Vs9XmpGEQd1yEvYISQZMeE68Nxj65iUQ== + dependencies: + chalk "^2.4.2" + +"@nomicfoundation/hardhat-network-helpers@^1.0.0": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.10.tgz#c61042ceb104fdd6c10017859fdef6529c1d6585" + integrity sha512-R35/BMBlx7tWN5V6d/8/19QCwEmIdbnA4ZrsuXgvs8i2qFx5i7h6mH5pBS4Pwi4WigLH+upl6faYusrNPuzMrQ== + dependencies: + ethereumjs-util "^7.1.4" + +"@nomicfoundation/hardhat-toolbox@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-4.0.0.tgz#eb1f619218dd1414fa161dfec92d3e5e53a2f407" + integrity sha512-jhcWHp0aHaL0aDYj8IJl80v4SZXWMS1A2XxXa1CA6pBiFfJKuZinCkO6wb+POAt0LIfXB3gA3AgdcOccrcwBwA== + +"@nomicfoundation/hardhat-verify@^2.0.0": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.6.tgz#02623c431244c92a852c524008239fc616e1c658" + integrity sha512-oKUI5fl8QC8jysE2LUBHE6rObzEmccJcc4b43Ov7LFMlCBZJE27qoqGIsg/++wX7L8Jdga+bkejPxl8NvsecpQ== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@ethersproject/address" "^5.0.2" + cbor "^8.1.0" + chalk "^2.4.2" + debug "^4.1.1" + lodash.clonedeep "^4.5.0" + semver "^6.3.0" + table "^6.8.0" + undici "^5.14.0" + +"@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.1.tgz#4c858096b1c17fe58a474fe81b46815f93645c15" + integrity sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w== + +"@nomicfoundation/solidity-analyzer-darwin-x64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.1.tgz#6e25ccdf6e2d22389c35553b64fe6f3fdaec432c" + integrity sha512-XhQG4BaJE6cIbjAVtzGOGbK3sn1BO9W29uhk9J8y8fZF1DYz0Doj8QDMfpMu+A6TjPDs61lbsmeYodIDnfveSA== + +"@nomicfoundation/solidity-analyzer-freebsd-x64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.1.tgz#0a224ea50317139caeebcdedd435c28a039d169c" + integrity sha512-GHF1VKRdHW3G8CndkwdaeLkVBi5A9u2jwtlS7SLhBc8b5U/GcoL39Q+1CSO3hYqePNP+eV5YI7Zgm0ea6kMHoA== + +"@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.1.tgz#dfa085d9ffab9efb2e7b383aed3f557f7687ac2b" + integrity sha512-g4Cv2fO37ZsUENQ2vwPnZc2zRenHyAxHcyBjKcjaSmmkKrFr64yvzeNO8S3GBFCo90rfochLs99wFVGT/0owpg== + +"@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.1.tgz#c9e06b5d513dd3ab02a7ac069c160051675889a4" + integrity sha512-WJ3CE5Oek25OGE3WwzK7oaopY8xMw9Lhb0mlYuJl/maZVo+WtP36XoQTb7bW/i8aAdHW5Z+BqrHMux23pvxG3w== + +"@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.1.tgz#8d328d16839e52571f72f2998c81e46bf320f893" + integrity sha512-5WN7leSr5fkUBBjE4f3wKENUy9HQStu7HmWqbtknfXkkil+eNWiBV275IOlpXku7v3uLsXTOKpnnGHJYI2qsdA== + +"@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.1.tgz#9b49d0634b5976bb5ed1604a1e1b736f390959bb" + integrity sha512-KdYMkJOq0SYPQMmErv/63CwGwMm5XHenEna9X9aB8mQmhDBrYrlAOSsIPgFCUSL0hjxE3xHP65/EPXR/InD2+w== + +"@nomicfoundation/solidity-analyzer-win32-arm64-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.1.tgz#e2867af7264ebbcc3131ef837878955dd6a3676f" + integrity sha512-VFZASBfl4qiBYwW5xeY20exWhmv6ww9sWu/krWSesv3q5hA0o1JuzmPHR4LPN6SUZj5vcqci0O6JOL8BPw+APg== + +"@nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.1.tgz#0685f78608dd516c8cdfb4896ed451317e559585" + integrity sha512-JnFkYuyCSA70j6Si6cS1A9Gh1aHTEb8kOTBApp/c7NRTFGNMH8eaInKlyuuiIbvYFhlXW4LicqyYuWNNq9hkpQ== + +"@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.1.tgz#c9a44f7108646f083b82e851486e0f6aeb785836" + integrity sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw== + +"@nomicfoundation/solidity-analyzer@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.1.tgz#f5f4d36d3f66752f59a57e7208cd856f3ddf6f2d" + integrity sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg== + optionalDependencies: + "@nomicfoundation/solidity-analyzer-darwin-arm64" "0.1.1" + "@nomicfoundation/solidity-analyzer-darwin-x64" "0.1.1" + "@nomicfoundation/solidity-analyzer-freebsd-x64" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-arm64-musl" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-x64-gnu" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-x64-musl" "0.1.1" + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc" "0.1.1" + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc" "0.1.1" + "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.1" + +"@nomiclabs/hardhat-docker@^2.0.0": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-docker/-/hardhat-docker-2.0.2.tgz#ae964be17951275a55859ff7358e9e7c77448846" + integrity sha512-XgGEpRT3wlA1VslyB57zyAHV+oll8KnV1TjwnxxC1tpAL04/lbdwpdO5KxInVN8irMSepqFpsiSkqlcnvbE7Ng== + dependencies: + dockerode "^2.5.8" + fs-extra "^7.0.1" + node-fetch "^2.6.0" + +"@nomiclabs/hardhat-ethers@^2.2.1": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.3.tgz#b41053e360c31a32c2640c9a45ee981a7e603fe0" + integrity sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg== + +"@nomiclabs/hardhat-etherscan@^3.1.7": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.8.tgz#3c12ee90b3733e0775e05111146ef9418d4f5a38" + integrity sha512-v5F6IzQhrsjHh6kQz4uNrym49brK9K5bYCq2zQZ729RYRaifI9hHbtmK+KkIVevfhut7huQFEQ77JLRMAzWYjQ== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@ethersproject/address" "^5.0.2" + cbor "^8.1.0" + chalk "^2.4.2" + debug "^4.1.1" + fs-extra "^7.0.1" + lodash "^4.17.11" + semver "^6.3.0" + table "^6.8.0" + undici "^5.14.0" + +"@openzeppelin/contracts-upgradeable@^4.4.2", "@openzeppelin/contracts-upgradeable@^4.9.2", "@openzeppelin/contracts-upgradeable@^4.9.6": version "4.9.6" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.6.tgz#38b21708a719da647de4bb0e4802ee235a0d24df" integrity sha512-m4iHazOsOCv1DgM7eD7GupTJ+NFVujRZt1wzddDPSVGpWdKq1SKkla5htKG7+IS4d2XOCtzkUNwRZ7Vq5aEUMA== -"@openzeppelin/contracts@^4.4.2", "@openzeppelin/contracts@^4.9.6": +"@openzeppelin/contracts@^4.4.2", "@openzeppelin/contracts@^4.9.2", "@openzeppelin/contracts@^4.9.6": version "4.9.6" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.6.tgz#2a880a24eb19b4f8b25adc2a5095f2aa27f39677" integrity sha512-xSmezSupL+y9VkHZJGDoCBpmnB2ogM13ccaYDWqJTfS3dbuHkgjuwDFUmaFauBCboQMGB/S5UqUl2y54X99BmA== +"@openzeppelin/upgrades-core@1.27.0": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.27.0.tgz#43f05c3e45b21bdc583488aa42297fbb0d065d17" + integrity sha512-FBIuFPKiRNMhW09HS8jkmV5DueGfxO2wp/kmCa0m0SMDyX4ROumgy/4Ao0/yH8/JZZPDiH1q3EnTRn+B7TGYgg== + dependencies: + cbor "^8.0.0" + chalk "^4.1.0" + compare-versions "^5.0.0" + debug "^4.1.1" + ethereumjs-util "^7.0.3" + minimist "^1.2.7" + proper-lockfile "^4.1.1" + solidity-ast "^0.4.15" + +"@openzeppelin/upgrades-core@^1.27.0": + version "1.33.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.33.1.tgz#2e129ce1ab7bd07d07e98822ca8bb8de1d3b008e" + integrity sha512-YRxIRhTY1b+j7+NUUu8Uuem5ugxKexEMVd8dBRWNgWeoN1gS1OCrhgUg0ytL+54vzQ+SGWZDfNnzjVuI1Cj1Zw== + dependencies: + cbor "^9.0.0" + chalk "^4.1.0" + compare-versions "^6.0.0" + debug "^4.1.1" + ethereumjs-util "^7.0.3" + minimist "^1.2.7" + proper-lockfile "^4.1.1" + solidity-ast "^0.4.51" + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@scure/base@~1.1.0", "@scure/base@~1.1.4": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d" + integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g== + +"@scure/bip32@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.5.tgz#d2ccae16dcc2e75bc1d75f5ef3c66a338d1ba300" + integrity sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw== + dependencies: + "@noble/hashes" "~1.2.0" + "@noble/secp256k1" "~1.7.0" + "@scure/base" "~1.1.0" + +"@scure/bip32@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.3.tgz#a9624991dc8767087c57999a5d79488f48eae6c8" + integrity sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ== + dependencies: + "@noble/curves" "~1.3.0" + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.4" + +"@scure/bip39@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" + integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== + dependencies: + "@noble/hashes" "~1.2.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.2.tgz#f3426813f4ced11a47489cbcf7294aa963966527" + integrity sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA== + dependencies: + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.4" + +"@sentry/core@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" + integrity sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/minimal" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/hub@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100" + integrity sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ== + dependencies: + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/minimal@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.30.0.tgz#ce3d3a6a273428e0084adcb800bc12e72d34637b" + integrity sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/types" "5.30.0" + tslib "^1.9.3" + +"@sentry/node@^5.18.1": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.30.0.tgz#4ca479e799b1021285d7fe12ac0858951c11cd48" + integrity sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg== + dependencies: + "@sentry/core" "5.30.0" + "@sentry/hub" "5.30.0" + "@sentry/tracing" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + +"@sentry/tracing@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f" + integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/minimal" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/types@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402" + integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw== + +"@sentry/utils@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980" + integrity sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww== + dependencies: + "@sentry/types" "5.30.0" + tslib "^1.9.3" + +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== + dependencies: + type-detect "4.0.8" + +"@sinonjs/commons@^3.0.0", "@sinonjs/commons@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^11.2.2": + version "11.2.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz#50063cc3574f4a27bd8453180a04171c85cc9699" + integrity sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw== + dependencies: + "@sinonjs/commons" "^3.0.0" + +"@sinonjs/samsam@^8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-8.0.0.tgz#0d488c91efb3fa1442e26abea81759dfc8b5ac60" + integrity sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew== + dependencies: + "@sinonjs/commons" "^2.0.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/text-encoding@^0.7.2": + version "0.7.2" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz#5981a8db18b56ba38ef0efb7d995b12aa7b51918" + integrity sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ== + +"@solidity-parser/parser@^0.14.0": + version "0.14.5" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.5.tgz#87bc3cc7b068e08195c219c91cd8ddff5ef1a804" + integrity sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg== + dependencies: + antlr4ts "^0.5.0-alpha.4" + "@solidity-parser/parser@^0.16.0": version "0.16.2" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.16.2.tgz#42cb1e3d88b3e8029b0c9befff00b634cd92d2fa" @@ -534,6 +1134,11 @@ resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.17.0.tgz#52a2fcc97ff609f72011014e4c5b485ec52243ef" integrity sha512-Nko8R0/kUo391jsEHHxrGM07QFdnPGvlmox4rmH0kNiNAashItAilhy4Mv4pK5gQmW5f4sXAF58fwJbmlkGcVw== +"@solidity-parser/parser@^0.18.0": + version "0.18.0" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.18.0.tgz#8e77a02a09ecce957255a2f48c9a7178ec191908" + integrity sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA== + "@thirdweb-dev/dynamic-contracts@^1.2.4": version "1.2.5" resolved "https://registry.yarnpkg.com/@thirdweb-dev/dynamic-contracts/-/dynamic-contracts-1.2.5.tgz#f9735c0d46198e7bf2f98c277f0a9a79c54da1e8" @@ -548,6 +1153,16 @@ buffer-reverse "^1.0.1" treeify "^1.1.0" +"@ts-morph/common@~0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.22.0.tgz#8951d451622a26472fbc3a227d6c3a90e687a683" + integrity sha512-HqNBuV/oIlMKdkLshXd1zKBqNQCsuPEsgQOkfFQ/eUKjRlwndXW1AjN9LVkBEIukm00gGXSRmfkl0Wv5VXLnlw== + dependencies: + fast-glob "^3.3.2" + minimatch "^9.0.3" + mkdirp "^3.0.1" + path-browserify "^1.0.1" + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -576,6 +1191,61 @@ lodash "^4.17.15" ts-essentials "^7.0.1" +"@typechain/ethers-v6@^0.5.0": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz#42fe214a19a8b687086c93189b301e2b878797ea" + integrity sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA== + dependencies: + lodash "^4.17.15" + ts-essentials "^7.0.1" + +"@typechain/hardhat@^9.0.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@typechain/hardhat/-/hardhat-9.1.0.tgz#6985015f01dfb37ef2ca8a29c742d05890351ddc" + integrity sha512-mtaUlzLlkqTlfPwB3FORdejqBskSnh+Jl8AIJGjXNAQfRQ4ofHADPl1+oU7Z3pAJzmZbUXII8MhOLQltcHgKnA== + dependencies: + fs-extra "^9.1.0" + +"@types/bn.js@^4.11.3": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0": + version "5.1.5" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" + integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== + dependencies: + "@types/node" "*" + +"@types/chai-as-promised@^7.1.3": + version "7.1.8" + resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz#f2b3d82d53c59626b5d6bbc087667ccb4b677fe9" + integrity sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw== + dependencies: + "@types/chai" "*" + +"@types/chai@*", "@types/chai@^4.2.0": + version "4.3.16" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.16.tgz#b1572967f0b8b60bf3f87fe1d854a5604ea70c82" + integrity sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ== + +"@types/concat-stream@^1.6.0": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@types/concat-stream/-/concat-stream-1.6.1.tgz#24bcfc101ecf68e886aaedce60dfd74b632a1b74" + integrity sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA== + dependencies: + "@types/node" "*" + +"@types/form-data@0.0.33": + version "0.0.33" + resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-0.0.33.tgz#c9ac85b2a5fd18435b8c85d9ecb50e6d6c893ff8" + integrity sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw== + dependencies: + "@types/node" "*" + "@types/fs-extra@^9.0.13": version "9.0.13" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" @@ -583,11 +1253,29 @@ dependencies: "@types/node" "*" +"@types/glob@^7.1.1": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/lru-cache@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" + integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== + +"@types/minimatch@*": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + "@types/mocha@^9.1.1": version "9.1.1" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" @@ -600,16 +1288,45 @@ dependencies: undici-types "~5.26.4" +"@types/node@^10.0.3": + version "10.17.60" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" + integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== + "@types/node@^17.0.45": version "17.0.45" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== +"@types/node@^8.0.0": + version "8.10.66" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" + integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw== + +"@types/pbkdf2@^3.0.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.2.tgz#2dc43808e9985a2c69ff02e2d2027bd4fe33e8dc" + integrity sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew== + dependencies: + "@types/node" "*" + "@types/prettier@^2.1.1": version "2.7.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== +"@types/qs@^6.2.31": + version "6.9.15" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce" + integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== + +"@types/secp256k1@^4.0.1": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.6.tgz#d60ba2349a51c2cbc5e816dcd831a42029d376bf" + integrity sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ== + dependencies: + "@types/node" "*" + "@types/semver@^7.3.12": version "7.5.6" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" @@ -709,6 +1426,24 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +JSONStream@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea" + integrity sha512-mn0KSip7N4e0UDPZHnqDsHECo5uGQrixQKnAskOM1BIB8hd7QKbd6il8IPRPudPHOeHiECoCFqhyMaRO9+nWyA== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +abbrev@1.0.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + integrity sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q== + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -724,11 +1459,31 @@ acorn@^8.4.1, acorn@^8.9.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== +adm-zip@^0.4.16: + version "0.4.16" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" + integrity sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg== + aes-js@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv@^6.12.4, ajv@^6.12.6: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -749,15 +1504,39 @@ ajv@^8.0.1: require-from-string "^2.0.2" uri-js "^4.2.2" +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg== + +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-escapes@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== ansi-regex@^6.0.1: version "6.0.1" @@ -811,6 +1590,13 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -826,11 +1612,67 @@ array-back@^4.0.1, array-back@^4.0.2: resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +array-uniq@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== + +array.prototype.findlast@^1.2.2: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" + integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + +asap@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +asn1@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + ast-parents@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" @@ -841,16 +1683,61 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +async@1.x: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +axios@^1.4.0, axios@^1.5.1, axios@^1.6.2: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base-x@^3.0.2: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + dependencies: + safe-buffer "^5.0.1" + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +bcrypt-pbkdf@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + dependencies: + tweetnacl "^0.14.3" + bech32@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" @@ -861,16 +1748,57 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bn.js@^4.11.9: +bl@^1.0.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" + integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +blakejs@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" + integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA== + +bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.2.0, bn.js@^5.2.1: +bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== +boxen@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -903,11 +1831,75 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== +browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + buffer-reverse@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60" integrity sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg== +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -916,6 +1908,11 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +buildcheck@~0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238" + integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== + bundle-require@^3.0.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bundle-require/-/bundle-require-3.1.2.tgz#1374a7bdcb8b330a7ccc862ccbf7c137cc43ad27" @@ -923,21 +1920,84 @@ bundle-require@^3.0.2: dependencies: load-tsconfig "^0.2.0" +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + cac@^6.7.12: version "6.7.14" resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camelcase@^6.0.0: +camelcase@^6.0.0, camelcase@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== +caseless@^0.12.0, caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== + +cbor@^8.0.0, cbor@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" + integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== + dependencies: + nofilter "^3.1.0" + +cbor@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-9.0.2.tgz#536b4f2d544411e70ec2b19a2453f10f83cd9fdb" + integrity sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ== + dependencies: + nofilter "^3.1.0" + +chai-as-promised@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" + integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA== + dependencies: + check-error "^1.0.2" + +chai@^4.2.0, chai@^4.3.6, chai@^4.3.7: + version "4.4.1" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" + integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" + pathval "^1.1.1" + type-detect "^4.0.8" + +chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -947,13 +2007,17 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== +"charenc@>= 0.0.1": + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== + +check-error@^1.0.2, check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" + get-func-name "^2.0.2" chokidar@3.5.3, chokidar@^3.5.1: version "3.5.3" @@ -970,6 +2034,68 @@ chokidar@3.5.3, chokidar@^3.5.1: optionalDependencies: fsevents "~2.3.2" +chokidar@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^1.0.1, chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + +cli-table3@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" + integrity sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw== + dependencies: + object-assign "^4.1.0" + string-width "^2.1.1" + optionalDependencies: + colors "^1.1.2" + +cli-table3@^0.6.0: + version "0.6.5" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -979,6 +2105,11 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +code-block-writer@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-12.0.0.tgz#4dd58946eb4234105aff7f0035977b2afdc2a770" + integrity sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -1003,6 +2134,23 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colors@1.4.0, colors@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +combined-stream@^1.0.6, combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + command-line-args@^5.1.1: version "5.2.1" resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" @@ -1023,6 +2171,11 @@ command-line-usage@^6.1.0: table-layout "^1.0.2" typical "^5.2.0" +commander@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" + integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== + commander@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" @@ -1033,11 +2186,41 @@ commander@^4.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== +compare-versions@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.3.tgz#a9b34fea217472650ef4a2651d905f42c28ebfd7" + integrity sha512-4UZlZP8Z99MGEY+Ovg/uJxJuvoXuN4M6B3hKaiackiHrgzQFEe3diJi1mf1PNHbFujM7FvLrK2bpgIaImbtZ1A== + +compare-versions@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-6.1.0.tgz#3f2131e3ae93577df111dba133e6db876ffe127a" + integrity sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +concat-stream@^1.6.0, concat-stream@^1.6.2, concat-stream@~1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +cookie@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + cosmiconfig@^8.0.0: version "8.3.6" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" @@ -1048,6 +2231,37 @@ cosmiconfig@^8.0.0: parse-json "^5.2.0" path-type "^4.0.0" +cpu-features@~0.0.9: + version "0.0.10" + resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.10.tgz#9aae536db2710c7254d7ed67cb3cbc7d29ad79c5" + integrity sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA== + dependencies: + buildcheck "~0.0.6" + nan "^2.19.0" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -1062,6 +2276,50 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +"crypt@>= 0.0.1": + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== + +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +death@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/death/-/death-1.1.0.tgz#01aa9c401edd92750514470b8266390c66c67318" + integrity sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w== + +debug@4, debug@4.3.4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debug@4.3.3: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" @@ -1069,28 +2327,63 @@ debug@4.3.3: dependencies: ms "2.1.2" -debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== +debug@^3.2.6: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: - ms "2.1.2" + ms "^2.1.1" decamelize@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +deep-eql@^4.0.1, deep-eql@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== + dependencies: + type-detect "^4.0.0" + deep-extend@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-is@^0.1.3: +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + diff@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -1101,6 +2394,18 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +diff@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" + integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== + +difflib@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/difflib/-/difflib-0.2.4.tgz#b5e30361a6db023176d562892db85940a718f47e" + integrity sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w== + dependencies: + heap ">= 0.2.0" + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -1108,6 +2413,63 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +docker-modem@^1.0.8: + version "1.0.9" + resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-1.0.9.tgz#a1f13e50e6afb6cf3431b2d5e7aac589db6aaba8" + integrity sha512-lVjqCSCIAUDZPAZIeyM125HXfNvOmYYInciphNrLrylUtKyW66meAjSPXWchKVzoIYZx69TPnAepVSSkeawoIw== + dependencies: + JSONStream "1.3.2" + debug "^3.2.6" + readable-stream "~1.0.26-4" + split-ca "^1.0.0" + +docker-modem@^3.0.0: + version "3.0.8" + resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-3.0.8.tgz#ef62c8bdff6e8a7d12f0160988c295ea8705e77a" + integrity sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ== + dependencies: + debug "^4.1.1" + readable-stream "^3.5.0" + split-ca "^1.0.1" + ssh2 "^1.11.0" + +docker-modem@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-5.0.3.tgz#50c06f11285289f58112b5c4c4d89824541c41d0" + integrity sha512-89zhop5YVhcPEt5FpUFGr3cDyceGhq/F9J+ZndQ4KfqNvfbJpPMfgeixFgUj5OjCYAboElqODxY5Z1EBsSa6sg== + dependencies: + debug "^4.1.1" + readable-stream "^3.5.0" + split-ca "^1.0.1" + ssh2 "^1.15.0" + +dockerode@^2.5.8: + version "2.5.8" + resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-2.5.8.tgz#1b661e36e1e4f860e25f56e0deabe9f87f1d0acc" + integrity sha512-+7iOUYBeDTScmOmQqpUYQaE7F4vvIt6+gIZNHWhqAQEI887tiPFB9OvXI/HzQYqfUNvukMK+9myLW63oTJPZpw== + dependencies: + concat-stream "~1.6.2" + docker-modem "^1.0.8" + tar-fs "~1.16.3" + +dockerode@^3.3.4: + version "3.3.5" + resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-3.3.5.tgz#7ae3f40f2bec53ae5e9a741ce655fff459745629" + integrity sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA== + dependencies: + "@balena/dockerignore" "^1.0.2" + docker-modem "^3.0.0" + tar-fs "~2.0.1" + +dockerode@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-4.0.2.tgz#dedc8529a1db3ac46d186f5912389899bc309f7d" + integrity sha512-9wM1BVpVMFr2Pw3eJNXrYYt6DT9k0xMcsSCjtPvyQ+xa1iPg/Mo3T/gUcwI0B2cczqCeCYRPF8yFYDwtFXT0+w== + dependencies: + "@balena/dockerignore" "^1.0.2" + docker-modem "^5.0.3" + tar-fs "~2.0.1" + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -1138,16 +2500,44 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +elliptic@^6.5.2, elliptic@^6.5.4: + version "6.5.5" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" + integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" emoji-regex@^9.2.2: version "9.2.2" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enquirer@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" + integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== + dependencies: + ansi-colors "^4.1.1" + strip-ansi "^6.0.1" + +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + erc721a-upgradeable@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/erc721a-upgradeable/-/erc721a-upgradeable-3.3.0.tgz#c7b481668694756120868261fe98ab3a245a06b4" @@ -1169,6 +2559,102 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + +es-shim-unscopables@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + esbuild-android-64@0.14.54: version "0.14.54" resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be" @@ -1311,6 +2797,18 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +escodegen@1.8.x: + version "1.8.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + integrity sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A== + dependencies: + esprima "^2.7.1" + estraverse "^1.9.1" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.2.0" + eslint-config-prettier@^8.10.0: version "8.10.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" @@ -1390,6 +2888,16 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" +esprima@2.7.x, esprima@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + integrity sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + esquery@^1.4.2: version "1.5.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" @@ -1404,6 +2912,11 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" +estraverse@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + integrity sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA== + estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" @@ -1419,7 +2932,106 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -ethers@^5.7.2: +eth-gas-reporter@^0.2.25: + version "0.2.27" + resolved "https://registry.yarnpkg.com/eth-gas-reporter/-/eth-gas-reporter-0.2.27.tgz#928de8548a674ed64c7ba0bf5795e63079150d4e" + integrity sha512-femhvoAM7wL0GcI8ozTdxfuBtBFJ9qsyIAsmKVjlWAHUbdnnXHt+lKzz/kmldM5lA9jLuNHGwuIxorNpLbR1Zw== + dependencies: + "@solidity-parser/parser" "^0.14.0" + axios "^1.5.1" + cli-table3 "^0.5.0" + colors "1.4.0" + ethereum-cryptography "^1.0.3" + ethers "^5.7.2" + fs-readdir-recursive "^1.1.0" + lodash "^4.17.14" + markdown-table "^1.1.3" + mocha "^10.2.0" + req-cwd "^2.0.0" + sha1 "^1.1.1" + sync-request "^6.0.0" + +ethereum-bloom-filters@^1.0.6: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.1.0.tgz#b3fc1eb789509ee30db0bf99a2988ccacb8d0397" + integrity sha512-J1gDRkLpuGNvWYzWslBQR9cDV4nd4kfvVTE/Wy4Kkm4yb3EYRSlyi0eB/inTsSTTVyA0+HyzHgbr95Fn/Z1fSw== + dependencies: + "@noble/hashes" "^1.4.0" + +ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-cryptography@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz#5ccfa183e85fdaf9f9b299a79430c044268c9b3a" + integrity sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw== + dependencies: + "@noble/hashes" "1.2.0" + "@noble/secp256k1" "1.7.1" + "@scure/bip32" "1.1.5" + "@scure/bip39" "1.1.1" + +ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz#1352270ed3b339fe25af5ceeadcf1b9c8e30768a" + integrity sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA== + dependencies: + "@noble/curves" "1.3.0" + "@noble/hashes" "1.3.3" + "@scure/bip32" "1.3.3" + "@scure/bip39" "1.2.2" + +ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^7.0.3, ethereumjs-util@^7.1.4, ethereumjs-util@^7.1.5: + version "7.1.5" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" + integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + +ethers@^5.7.2, ethers@~5.7.0, ethers@~5.7.2: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -1455,6 +3067,30 @@ ethers@^5.7.2: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw== + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -1480,7 +3116,7 @@ fast-diff@^1.1.2, fast-diff@^1.2.0: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== -fast-glob@^3.2.9: +fast-glob@^3.0.3, fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -1496,7 +3132,7 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== @@ -1515,6 +3151,14 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +fill-keys@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" + integrity sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA== + dependencies: + is-object "~1.0.1" + merge-descriptors "~1.0.0" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -1537,6 +3181,13 @@ find-up@5.0.0, find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== + dependencies: + locate-path "^2.0.0" + flat-cache@^3.0.4: version "3.2.0" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" @@ -1556,6 +3207,18 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== +follow-redirects@^1.12.1, follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + foreground-child@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" @@ -1564,6 +3227,50 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" +form-data@^2.2.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4" + integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fp-ts@1.19.3: + version "1.19.3" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.3.tgz#261a60d1088fbff01f91256f91d21d0caaaaa96f" + integrity sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg== + +fp-ts@^1.0.0: + version "1.19.5" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a" + integrity sha512-wDNqTimnzs8QqpldiId9OavWK2NptormjXnRJTQecNjzwfyp6P/8s/zG8e4h3ja3oqkKaY72UlTjQYt/1yXf9A== + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + fs-extra@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" @@ -1573,7 +3280,16 @@ fs-extra@^10.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^7.0.0: +fs-extra@^11.1.1, fs-extra@^11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^7.0.0, fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== @@ -1582,6 +3298,30 @@ fs-extra@^7.0.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-readdir-recursive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" + integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1592,16 +3332,74 @@ fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-func-name@^2.0.1, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-port@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + integrity sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg== + get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + +ghost-testrpc@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz#c4de9557b1d1ae7b2d20bbe474a91378ca90ce92" + integrity sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ== + dependencies: + chalk "^2.4.2" + node-emoji "^1.10.0" + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -1640,6 +3438,17 @@ glob@7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" +glob@8.1.0, glob@^8.0.3: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + glob@^10.3.10: version "10.3.10" resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" @@ -1651,7 +3460,18 @@ glob@^10.3.10: minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-scurry "^1.10.1" -glob@^7.1.3: +glob@^5.0.15: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA== + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.1.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -1663,16 +3483,21 @@ glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.3: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" + integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A== dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" + integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg== + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" globals@^13.19.0: version "13.24.0" @@ -1681,10 +3506,32 @@ globals@^13.19.0: dependencies: type-fest "^0.20.2" -globby@^11.0.3, globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== +globalthis@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +globby@^10.0.1: + version "10.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.2.tgz#277593e745acaa4646c3ab411289ec47a0392543" + integrity sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.0.0" + +globby@^11.0.3, globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" @@ -1693,7 +3540,14 @@ globby@^11.0.3, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -1708,6 +3562,94 @@ growl@1.10.5: resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== +handlebars@^4.0.1: + version "4.7.8" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.2" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +hardhat-contract-sizer@2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hardhat-contract-sizer/-/hardhat-contract-sizer-2.5.0.tgz#ae0ef708efbc433a129f655827478741cba84606" + integrity sha512-579Bm3QjrGyInL4RuPFPV/2jLDekw+fGmeLQ85GeiBciIKPHVS3ZYuZJDrp7E9J6A4Czk+QVCRA9YPT2Svn7lQ== + dependencies: + chalk "^4.0.0" + cli-table3 "^0.6.0" + +hardhat-gas-reporter@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.10.tgz#ebe5bda5334b5def312747580cd923c2b09aef1b" + integrity sha512-02N4+So/fZrzJ88ci54GqwVA3Zrf0C9duuTyGt0CFRIh/CdNwbnTgkXkRfojOMLBQ+6t+lBIkgbsOtqMvNwikA== + dependencies: + array-uniq "1.0.3" + eth-gas-reporter "^0.2.25" + sha1 "^1.1.1" + +hardhat@^2.14.0, hardhat@^2.19.1: + version "2.22.3" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.3.tgz#50605daca6b29862397e446c42ec14c89430bec3" + integrity sha512-k8JV2ECWNchD6ahkg2BR5wKVxY0OiKot7fuxiIpRK0frRqyOljcR2vKwgWSLw6YIeDcNNA4xybj7Og7NSxr2hA== + dependencies: + "@ethersproject/abi" "^5.1.2" + "@metamask/eth-sig-util" "^4.0.0" + "@nomicfoundation/edr" "^0.3.5" + "@nomicfoundation/ethereumjs-common" "4.0.4" + "@nomicfoundation/ethereumjs-tx" "5.0.4" + "@nomicfoundation/ethereumjs-util" "9.0.4" + "@nomicfoundation/solidity-analyzer" "^0.1.0" + "@sentry/node" "^5.18.1" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "^5.1.0" + adm-zip "^0.4.16" + aggregate-error "^3.0.0" + ansi-escapes "^4.3.0" + boxen "^5.1.2" + chalk "^2.4.2" + chokidar "^3.4.0" + ci-info "^2.0.0" + debug "^4.1.1" + enquirer "^2.3.0" + env-paths "^2.2.0" + ethereum-cryptography "^1.0.3" + ethereumjs-abi "^0.6.8" + find-up "^2.1.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + glob "7.2.0" + immutable "^4.0.0-rc.12" + io-ts "1.10.4" + keccak "^3.0.2" + lodash "^4.17.11" + mnemonist "^0.38.0" + mocha "^10.0.0" + p-map "^4.0.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + solc "0.7.3" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + tsort "0.0.1" + undici "^5.14.0" + uuid "^8.3.2" + ws "^7.4.6" + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -1718,7 +3660,40 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== @@ -1726,11 +3701,23 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +"heap@>= 0.2.0": + version "0.2.7" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" + integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== + hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -1740,21 +3727,74 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +http-basic@^8.1.1: + version "8.1.3" + resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf" + integrity sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw== + dependencies: + caseless "^0.12.0" + concat-stream "^1.6.2" + http-response-object "^3.0.1" + parse-cache-control "^1.0.1" + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-response-object@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-3.0.2.tgz#7f435bb210454e4360d074ef1f989d5ea8aa9810" + integrity sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA== + dependencies: + "@types/node" "^10.0.3" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -ieee754@^1.2.1: +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +ignore@^5.1.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + ignore@^5.2.0, ignore@^5.2.4: version "5.3.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== +immutable@^4.0.0-rc.12: + version "4.3.5" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.5.tgz#f8b436e66d59f99760dc577f5c99a4fd2a5cc5a0" + integrity sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw== + import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -1768,6 +3808,11 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -1776,16 +3821,57 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@^2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@^1.3.5: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +io-ts@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" + integrity sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g== + dependencies: + fp-ts "^1.0.0" + +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -1793,11 +3879,50 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -1810,11 +3935,33 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-object@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" @@ -1825,16 +3972,74 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -1864,6 +4069,14 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-yaml@3.x: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -1896,6 +4109,13 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw== + optionalDependencies: + graceful-fs "^4.1.6" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -1912,6 +4132,21 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +jsonschema@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" + integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== + +just-extend@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-6.2.0.tgz#b816abfb3d67ee860482e7401564672558163947" + integrity sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw== + keccak256@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/keccak256/-/keccak256-1.0.6.tgz#dd32fb771558fed51ce4e45a035ae7515573da58" @@ -1921,7 +4156,7 @@ keccak256@^1.0.6: buffer "^6.0.3" keccak "^3.0.2" -keccak@^3.0.2: +keccak@^3.0.0, keccak@^3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== @@ -1937,6 +4172,18 @@ keyv@^4.5.3: dependencies: json-buffer "3.0.1" +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw== + optionalDependencies: + graceful-fs "^4.1.9" + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -1945,6 +4192,14 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + lilconfig@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" @@ -1960,6 +4215,14 @@ load-tsconfig@^0.2.0: resolved "https://registry.yarnpkg.com/load-tsconfig/-/load-tsconfig-0.2.5.tgz#453b8cd8961bfb912dea77eb6c168fe8cca3d3a1" integrity sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg== +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -1972,6 +4235,21 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -1987,7 +4265,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== -lodash@^4.17.15, lodash@^4.17.21: +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -2000,6 +4278,13 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +loupe@^2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== + dependencies: + get-func-name "^2.0.1" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -2012,21 +4297,55 @@ lru-cache@^6.0.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.1.0.tgz#2098d41c2dc56500e6c88584aa656c84de7d0484" integrity sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag== +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ== + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +markdown-table@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" + integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== + +merge-descriptors@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0, merge2@^1.4.1: +merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +micro-ftch@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f" + integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg== + micromatch@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -2035,6 +4354,18 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -2050,6 +4381,13 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== +"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + minimatch@4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" @@ -2057,12 +4395,12 @@ minimatch@4.2.1: dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== dependencies: - brace-expansion "^1.1.7" + brace-expansion "^2.0.1" minimatch@^5.0.1: version "5.1.6" @@ -2078,16 +4416,78 @@ minimatch@^9.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.3: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + "minipass@^5.0.0 || ^6.0.2 || ^7.0.0": version "7.0.4" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mkdirp@0.5.x, mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mkdirp@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" + integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== + +mnemonist@^0.38.0: + version "0.38.5" + resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.5.tgz#4adc7f4200491237fe0fa689ac0b86539685cade" + integrity sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg== + dependencies: + obliterator "^2.0.0" + +mocha@^10.0.0, mocha@^10.2.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.4.0.tgz#ed03db96ee9cfc6d20c56f8e2af07b961dbae261" + integrity sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "8.1.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + mocha@^9.2.2: version "9.2.2" resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" @@ -2118,12 +4518,17 @@ mocha@^9.2.2: yargs-parser "20.2.4" yargs-unparser "2.0.0" +module-not-found-error@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0" + integrity sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3: +ms@2.1.3, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -2137,6 +4542,11 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" +nan@^2.18.0, nan@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.19.0.tgz#bb58122ad55a6c5bc973303908d5b16cfdd5a8c0" + integrity sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw== + nanoid@3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" @@ -2152,16 +4562,58 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +nise@^5.1.9: + version "5.1.9" + resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.9.tgz#0cb73b5e4499d738231a473cd89bd8afbb618139" + integrity sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww== + dependencies: + "@sinonjs/commons" "^3.0.0" + "@sinonjs/fake-timers" "^11.2.2" + "@sinonjs/text-encoding" "^0.7.2" + just-extend "^6.2.0" + path-to-regexp "^6.2.1" + node-addon-api@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== +node-emoji@^1.10.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c" + integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A== + dependencies: + lodash "^4.17.21" + +node-fetch@^2.6.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-gyp-build@^4.2.0: version "4.8.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== +nofilter@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" + integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== + +nopt@3.x: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg== + dependencies: + abbrev "1" + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -2174,12 +4626,45 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -object-assign@^4.0.1: +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig== + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -once@^1.3.0: +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +obliterator@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" + integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== + +once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -2193,6 +4678,18 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -2205,6 +4702,23 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" +ordinal@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ordinal/-/ordinal-1.0.3.tgz#1a3c7726a61728112f50944ad7c35c06ae3a0d4d" + integrity sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ== + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -2212,6 +4726,13 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== + dependencies: + p-limit "^1.1.0" + p-locate@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" @@ -2219,6 +4740,18 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -2226,6 +4759,11 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-cache-control@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e" + integrity sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg== + parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" @@ -2236,6 +4774,16 @@ parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -2251,6 +4799,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-parse@^1.0.6, path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + path-scurry@^1.10.1: version "1.10.1" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" @@ -2259,16 +4812,42 @@ path-scurry@^1.10.1: lru-cache "^9.1.1 || ^10.0.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-to-regexp@^6.2.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.2.tgz#324377a83e5049cbecadc5554d6a63a9a4866b36" + integrity sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +pbkdf2@^3.0.17: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + pirates@^4.0.1: version "4.0.6" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" @@ -2279,6 +4858,11 @@ pluralize@^8.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + postcss-load-config@^3.0.1: version "3.1.4" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" @@ -2292,6 +4876,11 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + prettier-linter-helpers@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" @@ -2313,11 +4902,69 @@ prettier@^2.3.1, prettier@^2.8.3, prettier@^2.8.8: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +promise@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" + integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg== + dependencies: + asap "~2.0.6" + +proper-lockfile@^4.1.1, proper-lockfile@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +proxyquire@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-2.1.3.tgz#2049a7eefa10a9a953346a18e54aab2b4268df39" + integrity sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg== + dependencies: + fill-keys "^1.0.2" + module-not-found-error "^1.0.1" + resolve "^1.11.1" + +pump@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" + integrity sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +qs@^6.4.0: + version "6.12.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.1.tgz#39422111ca7cbdb70425541cba20c7d7b216599a" + integrity sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ== + dependencies: + side-channel "^1.0.6" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -2330,7 +4977,30 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -readable-stream@^3.6.0: +raw-body@^2.4.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.5: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -2339,6 +5009,16 @@ readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@~1.0.26-4: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -2346,21 +5026,64 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== + dependencies: + resolve "^1.1.6" + +recursive-readdir@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372" + integrity sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA== + dependencies: + minimatch "^3.0.5" + reduce-flatten@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== +regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +req-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/req-cwd/-/req-cwd-2.0.0.tgz#d4082b4d44598036640fb73ddea01ed53db49ebc" + integrity sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ== + dependencies: + req-from "^2.0.0" + +req-from@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/req-from/-/req-from-2.0.0.tgz#d74188e47f93796f4aa71df6ee35ae689f3e0e70" + integrity sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA== + dependencies: + resolve-from "^3.0.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-from-string@^2.0.2: +require-from-string@^2.0.0, require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -2371,11 +5094,44 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +resolve@1.1.x: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg== + +resolve@1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + +resolve@^1.1.6, resolve@^1.11.1: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rimraf@^2.2.8: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -2383,6 +5139,21 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.2.3, rlp@^2.2.4: + version "2.2.7" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" + integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== + dependencies: + bn.js "^5.2.0" + rollup@^2.74.1: version "2.79.1" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" @@ -2397,16 +5168,101 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -scrypt-js@3.0.1: +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sc-istanbul@^0.4.5: + version "0.4.6" + resolved "https://registry.yarnpkg.com/sc-istanbul/-/sc-istanbul-0.4.6.tgz#cf6784355ff2076f92d70d59047d71c13703e839" + integrity sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g== + dependencies: + abbrev "1.0.x" + async "1.x" + escodegen "1.8.x" + esprima "2.7.x" + glob "^5.0.15" + handlebars "^4.0.1" + js-yaml "3.x" + mkdirp "0.5.x" + nopt "3.x" + once "1.x" + resolve "1.1.x" + supports-color "^3.1.0" + which "^1.1.1" + wordwrap "^1.0.0" + +scrypt-js@3.0.1, scrypt-js@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== +seaport-core@^1.6.6: + version "1.6.6" + resolved "https://registry.yarnpkg.com/seaport-core/-/seaport-core-1.6.6.tgz#c599ffbfaccab8e031bd702a67ed5fc48d256802" + integrity sha512-BNOg9EizHcLXN27UMKPB+2ewwcRnZkuNV8lRawKORnlpHI7SlCXhLkvCT+SHL/s5W8sJbnq2ef/SCvNB0r+KKA== + dependencies: + seaport-types "1.6.3" + +seaport-types@1.6.3, seaport-types@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/seaport-types/-/seaport-types-1.6.3.tgz#b9993864517d4f9ecccc6b6daf6ef3f52a114e58" + integrity sha512-Rm9dTTEUKmXqMgc5TiRtfX/sFOX6SjKkT9l/spTdRknplYh5tmJ0fMJzbE60pCzV1/Izq0cCua6uvWszo6zOAQ== + +secp256k1@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" + integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== + dependencies: + elliptic "^6.5.4" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semver@^5.5.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.4, semver@^7.5.1: + version "7.6.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.1.tgz#60bfe090bf907a25aa8119a72b9f90ef7ca281b2" + integrity sha512-f/vbBsu+fOiYt+lmwZV0rVwJScl46HppnOA1ZvIuBWKOTlllpyJ3bfVax76/OrhCH38dyxoDIA8K7uB963IYgA== + semver@^7.3.7, semver@^7.5.2, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" @@ -2421,6 +5277,54 @@ serialize-javascript@6.0.0: dependencies: randombytes "^2.1.0" +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +sha1@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/sha1/-/sha1-1.1.1.tgz#addaa7a93168f393f19eb2b15091618e2700f848" + integrity sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA== + dependencies: + charenc ">= 0.0.1" + crypt ">= 0.0.1" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -2433,7 +5337,26 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -signal-exit@^3.0.3: +shelljs@^0.8.3: + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +side-channel@^1.0.4, side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -2443,6 +5366,23 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +sinon-chai@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.7.0.tgz#cfb7dec1c50990ed18c153f1840721cf13139783" + integrity sha512-mf5NURdUaSdnatJx3uhoBOrY9dtL19fiOtAdT1Azxg3+lNJFiuN0uzaU3xX1LeAfL17kHQhTAJgpsfhbMJMY2g== + +sinon@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-17.0.2.tgz#470894bcc2d24b01bad539722ea46da949892405" + integrity sha512-uihLiaB9FhzesElPDFZA7hDcNABzsVHwr3YfmM9sBllVwab3l0ltGlRV1XhpNfIacNDLGD1QRZNLs5nU5+hTuA== + dependencies: + "@sinonjs/commons" "^3.0.1" + "@sinonjs/fake-timers" "^11.2.2" + "@sinonjs/samsam" "^8.0.0" + diff "^5.2.0" + nise "^5.1.9" + supports-color "^7" + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -2462,6 +5402,21 @@ solady@0.0.180: resolved "https://registry.yarnpkg.com/solady/-/solady-0.0.180.tgz#d806c84a0bf8b6e3d85a8fb0978980de086ff59e" integrity sha512-9QVCyMph+wk78Aq/GxtDAQg7dvNoVWx2dS2Zwf11XlwFKDZ+YJG2lrQsK9NEIth9NOebwjBXAYk4itdwOOE4aw== +solc@0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" + integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA== + dependencies: + command-exists "^1.2.8" + commander "3.0.2" + follow-redirects "^1.12.1" + fs-extra "^0.30.0" + js-sha3 "0.8.0" + memorystream "^0.3.1" + require-from-string "^2.0.0" + semver "^5.5.0" + tmp "0.0.33" + solhint-plugin-prettier@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz#e3b22800ba435cd640a9eca805a7f8bc3e3e6a6b" @@ -2494,11 +5449,56 @@ solhint@^3.6.2: optionalDependencies: prettier "^2.8.3" +solidity-ast@^0.4.15, solidity-ast@^0.4.51: + version "0.4.56" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.56.tgz#94fe296f12e8de1a3bed319bc06db8d05a113d7a" + integrity sha512-HgmsA/Gfklm/M8GFbCX/J1qkVH0spXHgALCNZ8fA8x5X+MFdn/8CP2gr5OVyXjXw6RZTPC/Sxl2RUDQOXyNMeA== + dependencies: + array.prototype.findlast "^1.2.2" + +"solidity-ast@npm:solidity-ast@0.4.45": + version "0.4.45" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.45.tgz#37c1c17bd79123106fc69d94b4a8e9237ae8c625" + integrity sha512-N6uqfaDulVZqjpjru+KvMLjV89M3hesyr/1/t8nkjohRagFSDmDxZvb9viKV98pdwpMzs61Nt2JAApgh0fkL0g== + solidity-comments-extractor@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.8.tgz#f6e148ab0c49f30c1abcbecb8b8df01ed8e879f8" integrity sha512-htM7Vn6LhHreR+EglVMd2s+sZhcXAirB1Zlyrv5zBuTxieCvjfnRpd7iZk75m/u6NOlEyQ94C6TWbBn2cY7w8g== +solidity-coverage@^0.8.1: + version "0.8.12" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.12.tgz#c4fa2f64eff8ada7a1387b235d6b5b0e6c6985ed" + integrity sha512-8cOB1PtjnjFRqOgwFiD8DaUsYJtVJ6+YdXQtSZDrLGf8cdhhh8xzTtGzVTGeBf15kTv0v7lYPJlV/az7zLEPJw== + dependencies: + "@ethersproject/abi" "^5.0.9" + "@solidity-parser/parser" "^0.18.0" + chalk "^2.4.2" + death "^1.1.0" + difflib "^0.2.4" + fs-extra "^8.1.0" + ghost-testrpc "^0.0.2" + global-modules "^2.0.0" + globby "^10.0.1" + jsonschema "^1.2.4" + lodash "^4.17.21" + mocha "^10.2.0" + node-emoji "^1.10.0" + pify "^4.0.1" + recursive-readdir "^2.2.2" + sc-istanbul "^0.4.5" + semver "^7.3.4" + shelljs "^0.8.3" + web3-utils "^1.3.6" + +source-map-support@^0.5.13: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map@0.8.0-beta.0: version "0.8.0-beta.0" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" @@ -2506,12 +5506,57 @@ source-map@0.8.0-beta.0: dependencies: whatwg-url "^7.0.0" +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + integrity sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA== + dependencies: + amdefine ">=0.0.4" + +split-ca@^1.0.0, split-ca@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" + integrity sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +ssh2@^1.11.0, ssh2@^1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.15.0.tgz#2f998455036a7f89e0df5847efb5421748d9871b" + integrity sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw== + dependencies: + asn1 "^0.2.6" + bcrypt-pbkdf "^1.0.2" + optionalDependencies: + cpu-features "~0.0.9" + nan "^2.18.0" + +stacktrace-parser@^0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + string-format@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -2520,6 +5565,14 @@ string-format@^2.0.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -2529,6 +5582,34 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -2536,6 +5617,18 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -2543,6 +5636,13 @@ string_decoder@^1.1.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow== + dependencies: + ansi-regex "^3.0.0" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -2555,6 +5655,13 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A== + dependencies: + is-hex-prefixed "1.0.0" + strip-json-comments@3.1.1, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -2580,6 +5687,13 @@ supports-color@8.1.1: dependencies: has-flag "^4.0.0" +supports-color@^3.1.0: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + integrity sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A== + dependencies: + has-flag "^1.0.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -2587,13 +5701,34 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.1.0: +supports-color@^7, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +sync-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-6.1.0.tgz#e96217565b5e50bbffe179868ba75532fb597e68" + integrity sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw== + dependencies: + http-response-object "^3.0.1" + sync-rpc "^1.2.1" + then-request "^6.0.0" + +sync-rpc@^1.2.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7" + integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw== + dependencies: + get-port "^3.1.0" + table-layout@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" @@ -2604,6 +5739,17 @@ table-layout@^1.0.2: typical "^5.2.0" wordwrapjs "^4.0.0" +table@^6.8.0: + version "6.8.2" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58" + integrity sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + table@^6.8.1: version "6.8.1" resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" @@ -2615,11 +5761,72 @@ table@^6.8.1: string-width "^4.2.3" strip-ansi "^6.0.1" +tar-fs@~1.16.3: + version "1.16.3" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" + integrity sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw== + dependencies: + chownr "^1.0.1" + mkdirp "^0.5.1" + pump "^1.0.0" + tar-stream "^1.1.2" + +tar-fs@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2" + integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.0.0" + +tar-stream@^1.1.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" + integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== + dependencies: + bl "^1.0.0" + buffer-alloc "^1.2.0" + end-of-stream "^1.0.0" + fs-constants "^1.0.0" + readable-stream "^2.3.0" + to-buffer "^1.1.1" + xtend "^4.0.0" + +tar-stream@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +then-request@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/then-request/-/then-request-6.0.2.tgz#ec18dd8b5ca43aaee5cb92f7e4c1630e950d4f0c" + integrity sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA== + dependencies: + "@types/concat-stream" "^1.6.0" + "@types/form-data" "0.0.33" + "@types/node" "^8.0.0" + "@types/qs" "^6.2.31" + caseless "~0.12.0" + concat-stream "^1.6.0" + form-data "^2.2.0" + http-basic "^8.1.1" + http-response-object "^3.0.1" + promise "^8.0.0" + qs "^6.4.0" + thenify-all@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" @@ -2634,6 +5841,23 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +"through@>=2.2.7 <3": + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tmp@0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-buffer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" + integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -2641,6 +5865,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + tr46@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" @@ -2648,6 +5877,11 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -2678,6 +5912,14 @@ ts-interface-checker@^0.1.9: resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== +ts-morph@^21.0.1: + version "21.0.1" + resolved "https://registry.yarnpkg.com/ts-morph/-/ts-morph-21.0.1.tgz#712302a0f6e9dbf1aa8d9cf33a4386c4b18c2006" + integrity sha512-dbDtVdEAncKctzrVZ+Nr7kHpHkv+0JDJb2MjjpBaj8bFeCkePU9rHfMklmhuLFnpeq/EJZk2IhStY6NzqgjOkg== + dependencies: + "@ts-morph/common" "~0.22.0" + code-block-writer "^12.0.0" + ts-node@^10.9.1: version "10.9.2" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" @@ -2697,7 +5939,7 @@ ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tslib@^1.8.1: +tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -2707,6 +5949,11 @@ tslib@^2.6.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tsort@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" + integrity sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw== + tsup@^5.12.9: version "5.12.9" resolved "https://registry.yarnpkg.com/tsup/-/tsup-5.12.9.tgz#8cdd9b4bc6493317cb92edf5f3476920dddcdb18" @@ -2734,6 +5981,21 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tweetnacl-util@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== + +tweetnacl@^0.14.3: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== + +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -2741,11 +6003,33 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + typechain@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.3.2.tgz#1090dd8d9c57b6ef2aed3640a516bdbf01b00d73" @@ -2762,6 +6046,55 @@ typechain@^8.3.2: ts-command-line-args "^2.2.0" ts-essentials "^7.0.1" +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + typescript@^4.9.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" @@ -2782,11 +6115,33 @@ typical@^5.2.0: resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== +uglify-js@^3.1.4: + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici@^5.14.0: + version "5.28.4" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + dependencies: + "@fastify/busboy" "^2.0.0" + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -2797,6 +6152,11 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== +unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -2804,21 +6164,58 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -util-deprecate@^1.0.1: +utf8@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== +web3-utils@^1.3.6: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.4.tgz#0daee7d6841641655d8b3726baf33b08eda1cbec" + integrity sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A== + dependencies: + "@ethereumjs/util" "^8.1.0" + bn.js "^5.2.1" + ethereum-bloom-filters "^1.0.6" + ethereum-cryptography "^2.1.2" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + whatwg-url@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" @@ -2828,6 +6225,28 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.14, which-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + which@2.0.2, which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -2835,6 +6254,30 @@ which@2.0.2, which@^2.0.1: dependencies: isexe "^2.0.0" +which@^1.1.1, which@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +word-wrap@~1.2.3: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + wordwrapjs@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" @@ -2848,6 +6291,11 @@ workerpool@6.2.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -2876,6 +6324,16 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +ws@^7.4.6: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -2933,3 +6391,10 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zksync-ethers@^5.0.0, zksync-ethers@^5.7.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/zksync-ethers/-/zksync-ethers-5.7.0.tgz#edf465eb564ed60d6a1cc3de5978b8bd8481c230" + integrity sha512-X99c5APICTlRzyXXjfwkEjRzOPp3Jwo62+z2DVGaZbe+b9Apbizcd2UGV4NGomoAR2GXPbeiSqi1cf3Hbo3cQw== + dependencies: + ethers "~5.7.0"