diff --git a/.gas-snapshot b/.gas-snapshot index ea293f87c..59fb47f8b 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,1007 +1,61 @@ -AccountBenchmarkTest:test_benchmark_createAccount_directWithFactory() (gas: 151905) -AccountBenchmarkTest:test_benchmark_createAccount_withUserOp() (gas: 304504) -AccountBenchmarkTest:test_benchmark_dummyTx_withAccount() (gas: 381471) -AccountBenchmarkTest:test_benchmark_dummyTx_withEOA() (gas: 27707) -AccountTest:test_revert_executeTransaction_nonSigner_viaDirectCall() (gas: 481195) -AccountTest:test_revert_executeTransaction_nonSigner_viaEntrypoint() (gas: 299925) -AccountTest:test_state_accountReceivesNativeTokens() (gas: 261513) -AccountTest:test_state_addAndWithdrawDeposit() (gas: 340374) -AccountTest:test_state_createAccount_viaEntrypoint() (gas: 253919) -AccountTest:test_state_createAccount_viaFactory() (gas: 161112) -AccountTest:test_state_executeBatchTransaction() (gas: 294528) -AccountTest:test_state_executeBatchTransaction_viaEntrypoint() (gas: 335783) -AccountTest:test_state_executeTransaction() (gas: 285859) -AccountTest:test_state_executeTransaction_viaAccountSigner() (gas: 540854) -AccountTest:test_state_executeTransaction_viaEntrypoint() (gas: 323701) -AccountTest:test_state_receiveERC1155NFT() (gas: 292047) -AccountTest:test_state_receiveERC721NFT() (gas: 331225) -AccountTest:test_state_transferOutsNativeTokens() (gas: 338149) -AirdropERC1155BenchmarkTest:test_benchmark_airdrop_five() (gas: 287722) -AirdropERC1155BenchmarkTest:test_benchmark_airdrop_one() (gas: 93206) -AirdropERC1155BenchmarkTest:test_benchmark_airdrop_two() (gas: 141422) -AirdropERC1155GasTest:test_safeTransferFrom_toContract() (gas: 47202) -AirdropERC1155GasTest:test_safeTransferFrom_toContract_gasOverride() (gas: 50671) -AirdropERC1155GasTest:test_safeTransferFrom_toEOA() (gas: 47756) -AirdropERC1155GasTest:test_safeTransferFrom_toEOA_gasOverride() (gas: 50583) -AirdropERC1155Test:test_revert_airdrop_notApproved() (gas: 10894694) -AirdropERC1155Test:test_revert_airdrop_notOwner() (gas: 10928533) -AirdropERC1155Test:test_revert_processPayments_notApproved() (gas: 118339369) -AirdropERC1155Test:test_revert_processPayments_notOwner() (gas: 10923853) -AirdropERC1155Test:test_state_airdrop() (gas: 48184183) -AirdropERC1155Test:test_state_cancelPayments() (gas: 159005765) -AirdropERC1155Test:test_state_cancelPayments_addMore() (gas: 174827682) -AirdropERC1155Test:test_state_processPayments_full() (gas: 164096567) -AirdropERC1155Test:test_state_processPayments_partial() (gas: 154498350) -AirdropERC20AuditTest:test_process_payments_with_non_compliant_token() (gas: 130063205) -AirdropERC20BenchmarkTest:test_benchmark_airdrop_five_ERC20() (gas: 240783) -AirdropERC20BenchmarkTest:test_benchmark_airdrop_one_ERC20() (gas: 89304) -AirdropERC20BenchmarkTest:test_benchmark_airdrop_two_ERC20() (gas: 127223) -AirdropERC20GasTest:test_transferNativeToken_toContract() (gas: 12383) -AirdropERC20GasTest:test_transferNativeToken_toContract_gasOverride() (gas: 15470) -AirdropERC20GasTest:test_transferNativeToken_toEOA() (gas: 39697) -AirdropERC20GasTest:test_transferNativeToken_toEOA_gasOverride() (gas: 43269) -AirdropERC20Test:test_revert_airdrop_notApproved() (gas: 8728055) -AirdropERC20Test:test_revert_airdrop_notOwner() (gas: 8763441) -AirdropERC20Test:test_revert_processPayments_incorrectNativeTokenAmt() (gas: 101771743) -AirdropERC20Test:test_revert_processPayments_notAdmin() (gas: 8758783) -AirdropERC20Test:test_revert_processPayments_notApproved() (gas: 98292857) -AirdropERC20Test:test_state_airdrop() (gas: 42600531) -AirdropERC20Test:test_state_cancelPayments() (gas: 133657183) -AirdropERC20Test:test_state_cancelPayments_addMore() (gas: 147262237) -AirdropERC20Test:test_state_cancelPayments_nativeToken() (gas: 141265693) -AirdropERC20Test:test_state_processPayments_full() (gas: 138780074) -AirdropERC20Test:test_state_processPayments_nativeToken_full() (gas: 148016847) -AirdropERC20Test:test_state_processPayments_nativeToken_partial() (gas: 137511536) -AirdropERC20Test:test_state_processPayments_partial() (gas: 129936965) -AirdropERC721AuditTest:test_NewRecipientsAreEmptyPreventingAirdrops() (gas: 357588) -AirdropERC721BenchmarkTest:test_benchmark_airdrop_five_ERC721() (gas: 295666) -AirdropERC721BenchmarkTest:test_benchmark_airdrop_one_ERC721() (gas: 96712) -AirdropERC721BenchmarkTest:test_benchmark_airdrop_two_ERC721() (gas: 146829) -AirdropERC721GasTest:test_safeTransferFrom_toContract() (gas: 49740) -AirdropERC721GasTest:test_safeTransferFrom_toContract_gasOverride() (gas: 53212) -AirdropERC721GasTest:test_safeTransferFrom_toEOA() (gas: 50749) -AirdropERC721GasTest:test_safeTransferFrom_toEOA_gasOverride() (gas: 54000) -AirdropERC721Test:test_revert_airdrop_notApproved() (gas: 8736763) -AirdropERC721Test:test_revert_airdrop_notOwner() (gas: 8763421) -AirdropERC721Test:test_revert_processPayments_notApproved() (gas: 98020506) -AirdropERC721Test:test_revert_processPayments_notOwner() (gas: 98738024) -AirdropERC721Test:test_state_airdrop() (gas: 53602782) -AirdropERC721Test:test_state_cancelPayments() (gas: 140931283) -AirdropERC721Test:test_state_cancelPayments_addMore() (gas: 154420153) -AirdropERC721Test:test_state_processPayments_full() (gas: 149630654) -AirdropERC721Test:test_state_processPayments_partial() (gas: 138522797) -AirdropGriefingTest:test_GriefingERC1155_Exceeds_30M_Gas() (gas: 393312) -AirdropGriefingTest:test_GriefingERC20_Exceeds_30M_Gas() (gas: 402461) -AirdropGriefingTest:test_GriefingERC721_Exceeds_30M_Gas() (gas: 423807) -BaseERC20BaseTest:test_revert_burn_NotEnoughBalance() (gas: 64106) -BaseERC20BaseTest:test_revert_mint_MintToZeroAddress() (gas: 13983) -BaseERC20BaseTest:test_revert_mint_MintingZeroTokens() (gas: 16047) -BaseERC20BaseTest:test_revert_mint_NotAuthorized() (gas: 13582) -BaseERC20BaseTest:test_revert_permit_ExpiredDeadline() (gas: 101958) -BaseERC20BaseTest:test_revert_permit_IncorrectKey() (gas: 124092) -BaseERC20BaseTest:test_revert_permit_UsedNonce() (gas: 157607) -BaseERC20BaseTest:test_state_burn() (gas: 56179) -BaseERC20BaseTest:test_state_mint() (gas: 66369) -BaseERC20BaseTest:test_state_permit() (gas: 152810) -BaseERC20DropTest:test_revert_burn_NotEnoughBalance() (gas: 13690) -BaseERC20DropTest:test_revert_permit_ExpiredDeadline() (gas: 49734) -BaseERC20DropTest:test_revert_permit_IncorrectKey() (gas: 71855) -BaseERC20DropTest:test_revert_permit_UsedNonce() (gas: 105471) -BaseERC20DropTest:test_state_burn() (gas: 150220) -BaseERC20DropTest:test_state_claim_NonZeroPrice_ERC20() (gas: 301599) -BaseERC20DropTest:test_state_claim_NonZeroPrice_NativeToken() (gas: 268059) -BaseERC20DropTest:test_state_claim_ZeroPrice() (gas: 183567) -BaseERC20DropTest:test_state_permit() (gas: 100816) -BaseERC20DropVoteTest:test_revert_burn_NotEnoughBalance() (gas: 13779) -BaseERC20DropVoteTest:test_revert_delegateBySig_InvalidNonce() (gas: 73554) -BaseERC20DropVoteTest:test_revert_delegateBySig_SignatureExpired() (gas: 49055) -BaseERC20DropVoteTest:test_revert_permit_ExpiredDeadline() (gas: 49381) -BaseERC20DropVoteTest:test_revert_permit_IncorrectKey() (gas: 71502) -BaseERC20DropVoteTest:test_revert_permit_UsedNonce() (gas: 104465) -BaseERC20DropVoteTest:test_state_burn() (gas: 201562) -BaseERC20DropVoteTest:test_state_claim_NonZeroPrice_ERC20() (gas: 370259) -BaseERC20DropVoteTest:test_state_claim_NonZeroPrice_NativeToken() (gas: 317126) -BaseERC20DropVoteTest:test_state_claim_ZeroPrice() (gas: 232634) -BaseERC20DropVoteTest:test_state_delegateBySig() (gas: 101010) -BaseERC20DropVoteTest:test_state_permit() (gas: 100012) -BaseERC20SignatureMintTest:test_revert_mintWithSignature_MintingZeroTokens() (gas: 47656) -BaseERC20SignatureMintTest:test_revert_mintWithSignature_MustSendTotalPrice() (gas: 123971) -BaseERC20SignatureMintTest:test_revert_mintWithSignature_QuantityTooLow() (gas: 104515) -BaseERC20SignatureMintTest:test_state_mintWithSignature_NonZeroPrice_ERC20() (gas: 260706) -BaseERC20SignatureMintTest:test_state_mintWithSignature_NonZeroPrice_NativeToken() (gas: 226660) -BaseERC20SignatureMintTest:test_state_mintWithSignature_ZeroPrice() (gas: 125075) -BaseERC20SignatureMintVoteTest:test_revert_mintWithSignature_MintingZeroTokens() (gas: 47436) -BaseERC20SignatureMintVoteTest:test_revert_mintWithSignature_MustSendTotalPrice() (gas: 123965) -BaseERC20SignatureMintVoteTest:test_revert_mintWithSignature_QuantityTooLow() (gas: 104509) -BaseERC20SignatureMintVoteTest:test_state_mintWithSignature_NonZeroPrice_ERC20() (gas: 309999) -BaseERC20SignatureMintVoteTest:test_state_mintWithSignature_NonZeroPrice_NativeToken() (gas: 275953) -BaseERC20SignatureMintVoteTest:test_state_mintWithSignature_ZeroPrice() (gas: 174368) -BaseERC20VoteTest:test_revert_burn_NotEnoughBalance() (gas: 113905) -BaseERC20VoteTest:test_revert_delegateBySig_InvalidNonce() (gas: 74060) -BaseERC20VoteTest:test_revert_delegateBySig_SignatureExpired() (gas: 49583) -BaseERC20VoteTest:test_revert_mint_MintToZeroAddress() (gas: 14092) -BaseERC20VoteTest:test_revert_mint_MintingZeroTokens() (gas: 16091) -BaseERC20VoteTest:test_revert_mint_NotAuthorized() (gas: 13626) -BaseERC20VoteTest:test_revert_permit_ExpiredDeadline() (gas: 149969) -BaseERC20VoteTest:test_revert_permit_IncorrectKey() (gas: 174176) -BaseERC20VoteTest:test_revert_permit_UsedNonce() (gas: 207919) -BaseERC20VoteTest:test_state_burn() (gas: 98294) -BaseERC20VoteTest:test_state_delegateBySig() (gas: 101491) -BaseERC20VoteTest:test_state_mint() (gas: 116344) -BaseERC20VoteTest:test_state_permit() (gas: 203169) -BaseERC721BaseTest:test_isApprovedOrOwner() (gas: 151185) -BaseERC721BaseTest:test_revert_batchMintTo_MintToZeroAddress() (gas: 84360) -BaseERC721BaseTest:test_revert_batchMintTo_NotAuthorized() (gas: 12779) -BaseERC721BaseTest:test_revert_burn_NotOwnerNorApproved() (gas: 117187) -BaseERC721BaseTest:test_revert_mintTo_MintToZeroAddress() (gas: 39746) -BaseERC721BaseTest:test_revert_mintTo_NotAuthorized() (gas: 12184) -BaseERC721BaseTest:test_state_batchMintTo() (gas: 3743211) -BaseERC721BaseTest:test_state_burn_Approved() (gas: 185664) -BaseERC721BaseTest:test_state_burn_Owner() (gas: 154612) -BaseERC721BaseTest:test_state_mintTo() (gas: 122324) -BaseERC721DelayedRevealTest:test_revert_lazyMint_URIForNonExistentId() (gas: 229035) -BaseERC721DelayedRevealTest:test_revert_reveal_NotAuthorized() (gas: 870763) -BaseERC721DelayedRevealTest:test_state_lazyMint_noEncryptedURI() (gas: 1060632) -BaseERC721DelayedRevealTest:test_state_lazyMint_withEncryptedURI() (gas: 869895) -BaseERC721DelayedRevealTest:test_state_reveal() (gas: 1733654) -BaseERC721DropTest:test_revert_claim_NotEnoughMintedTokens() (gas: 181147) -BaseERC721DropTest:test_state_claim_NonZeroPrice_ERC20() (gas: 598324) -BaseERC721DropTest:test_state_claim_NonZeroPrice_NativeToken() (gas: 547818) -BaseERC721DropTest:test_state_claim_ZeroPrice() (gas: 463666) -BaseERC721LazyMintTest:test_revert_claim_NotEnoughTokens() (gas: 21149) -BaseERC721LazyMintTest:test_state_claim() (gas: 162641) -BaseERC721MultiwrapTest:test_state_wrap() (gas: 458881) -BaseERC721SignatureMintTest:test_revert_mintWithSignature_MustSendTotalPrice() (gas: 134935) -BaseERC721SignatureMintTest:test_revert_mintWithSignature_QuantityNotOne() (gas: 56098) -BaseERC721SignatureMintTest:test_state_mintWithSignature_NonZeroPrice_ERC20() (gas: 346717) -BaseERC721SignatureMintTest:test_state_mintWithSignature_NonZeroPrice_NativeToken() (gas: 336017) -BaseERC721SignatureMintTest:test_state_mintWithSignature_ZeroPrice() (gas: 238782) -BreitwieserTheBidder:test_rob_as_bidder() (gas: 964694) -BreitwieserTheCreator:test_rob_as_creator() (gas: 620861) -ContractPublisherTest:test_publish() (gas: 353909) -ContractPublisherTest:test_publish_multiple_versions() (gas: 521719) -ContractPublisherTest:test_publish_revert_registryPaused() (gas: 33806) -ContractPublisherTest:test_publish_revert_unapprovedCaller() (gas: 21345) -ContractPublisherTest:test_read_from_linked_publisher() (gas: 376397) -ContractPublisherTest:test_unpublish() (gas: 288973) -ContractPublisherTest:test_unpublish_revert_registryPaused() (gas: 360570) -ContractPublisherTest:test_unpublish_revert_unapprovedCaller() (gas: 349459) -CreatePackBenchmarkTest:test_benchmark_createPack() (gas: 1172889) -DefaultExtensionSetTest:test_revert_addExtensionsWithSameFunctionSelectors() (gas: 3432951) -DefaultExtensionSetTest:test_revert_emptyExtensionImplementation() (gas: 133699) -DefaultExtensionSetTest:test_revert_fnSelectorSignatureMismatch() (gas: 2100540) -DefaultExtensionSetTest:test_revert_sameExtensionName() (gas: 3318938) -DefaultExtensionSetTest:test_revert_setExtension_nonDeployerCaller() (gas: 27721) -DefaultExtensionSetTest:test_state_setExtension() (gas: 1030414) -DropERC1155Test:test_claimCondition_startIdAndCount() (gas: 341404) -DropERC1155Test:test_claimCondition_startPhase() (gas: 359208) -DropERC1155Test:test_claimCondition_with_startTimestamp() (gas: 363371) -DropERC1155Test:test_claim_transferRole() (gas: 359826) -DropERC1155Test:test_event_lazyMint_TokensLazyMinted() (gas: 131799) -DropERC1155Test:test_fuzz_claim_merkleProof(uint256) (runs: 256, μ: 413462, ~: 413917) -DropERC1155Test:test_fuzz_claim_noAllowlist(uint256) (runs: 256, μ: 315849, ~: 315849) -DropERC1155Test:test_fuzz_lazyMint_batchMintAndNextTokenIdToMint(uint256) (runs: 256, μ: 203642, ~: 203642) -DropERC1155Test:test_fuzz_lazyMint_noEncryptedURI(uint256) (runs: 256, μ: 180746, ~: 165587) -DropERC1155Test:test_member_count_incremented_properly_when_role_granted() (gas: 96418) -DropERC1155Test:test_revert_claimCondition_exceedMaxClaimableSupply() (gas: 347312) -DropERC1155Test:test_revert_claimCondition_notEnoughMintedTokens() (gas: 232839) -DropERC1155Test:test_revert_grant_role_to_account_with_role() (gas: 92802) -DropERC1155Test:test_revert_lazyMint_MINTER_ROLE() (gas: 30282) -DropERC1155Test:test_revert_lazyMint_URIForNonLazyMintedToken() (gas: 129178) -DropERC1155Test:test_revert_nonHolder_renounceRole() (gas: 73721) -DropERC1155Test:test_revert_revokeRoleForNonHolder() (gas: 80455) -DropERC1155Test:test_state_claimCondition_resetEligibility() (gas: 433592) -DropERC1155Test:test_state_claim_allowlisted_DefaultQuantitySomePrice() (gas: 558870) -DropERC1155Test:test_state_claim_allowlisted_SetQuantityDefaultPrice() (gas: 586208) -DropERC1155Test:test_state_claim_allowlisted_SetQuantityPrice() (gas: 572283) -DropERC1155Test:test_state_claim_allowlisted_SetQuantityZeroPrice() (gas: 423542) -DropERC1155Test:test_state_getRoleMember_transferRole() (gas: 517290) -DropERC1155Test:test_state_grant_transferRole() (gas: 98223) -DropERC1155Test:test_state_lazyMint_noEncryptedURI() (gas: 1022904) -DropERC20Test:test_claimCondition_startIdAndCount() (gas: 340475) -DropERC20Test:test_claimCondition_startPhase() (gas: 356355) -DropERC20Test:test_claimCondition_with_startTimestamp() (gas: 294651) -DropERC20Test:test_claim_transferRole() (gas: 290466) -DropERC20Test:test_fuzz_claim_merkleProof(uint256) (runs: 256, μ: 344107, ~: 344646) -DropERC20Test:test_fuzz_claim_noAllowlist(uint256) (runs: 256, μ: 200919, ~: 200919) -DropERC20Test:test_member_count_incremented_properly_when_role_granted() (gas: 96307) -DropERC20Test:test_revert_claimCondition_exceedMaxClaimableSupply() (gas: 278717) -DropERC20Test:test_revert_grant_role_to_account_with_role() (gas: 92537) -DropERC20Test:test_revert_nonHolder_renounceRole() (gas: 73599) -DropERC20Test:test_revert_revokeRoleForNonHolder() (gas: 80421) -DropERC20Test:test_state_claimCondition_resetEligibility() (gas: 364367) -DropERC20Test:test_state_claim_allowlisted_DefaultQuantitySomePrice() (gas: 499636) -DropERC20Test:test_state_claim_allowlisted_SetQuantityDefaultPrice() (gas: 530268) -DropERC20Test:test_state_claim_allowlisted_SetQuantityPrice() (gas: 525320) -DropERC20Test:test_state_claim_allowlisted_SetQuantityZeroPrice() (gas: 357272) -DropERC20Test:test_state_getRoleMember_transferRole() (gas: 515420) -DropERC20Test:test_state_grant_transferRole() (gas: 97993) -DropERC721Test:testFail_reveal_incorrectKey() (gas: 217099) -DropERC721Test:test_claimCondition_startIdAndCount() (gas: 338838) -DropERC721Test:test_claimCondition_startPhase() (gas: 353210) -DropERC721Test:test_claimCondition_with_startTimestamp() (gas: 385212) -DropERC721Test:test_claim_transferRole() (gas: 379128) -DropERC721Test:test_delayedReveal_withNewLazyMintedEmptyBatch() (gas: 282928) -DropERC721Test:test_event_lazyMint_TokensLazyMinted() (gas: 132406) -DropERC721Test:test_event_reveal_TokenURIRevealed() (gas: 192024) -DropERC721Test:test_fuzz_claim_merkleProof(uint256) (runs: 256, μ: 887759, ~: 884485) -DropERC721Test:test_fuzz_claim_noAllowlist(uint256) (runs: 256, μ: 320086, ~: 320086) -DropERC721Test:test_fuzz_lazyMint_batchMintAndNextTokenIdToMint(uint256) (runs: 256, μ: 203852, ~: 203852) -DropERC721Test:test_fuzz_lazyMint_noEncryptedURI(uint256) (runs: 256, μ: 185522, ~: 168852) -DropERC721Test:test_fuzz_lazyMint_withEncryptedURI(uint256) (runs: 256, μ: 244173, ~: 244173) -DropERC721Test:test_member_count_incremented_properly_when_role_granted() (gas: 95979) -DropERC721Test:test_revert_claimCondition_exceedMaxClaimableSupply() (gas: 564309) -DropERC721Test:test_revert_claimCondition_notEnoughMintedTokens() (gas: 228044) -DropERC721Test:test_revert_delayedReveal_alreadyRevealed() (gas: 193322) -DropERC721Test:test_revert_grant_role_to_account_with_role() (gas: 92870) -DropERC721Test:test_revert_lazyMint_MINTER_ROLE() (gas: 30773) -DropERC721Test:test_revert_lazyMint_URIForNonLazyMintedToken() (gas: 129731) -DropERC721Test:test_revert_nonHolder_renounceRole() (gas: 73777) -DropERC721Test:test_revert_reveal_MINTER_ROLE() (gas: 241696) -DropERC721Test:test_revert_reveal_revealingNonExistentBatch() (gas: 312272) -DropERC721Test:test_revert_revokeRoleForNonHolder() (gas: 80246) -DropERC721Test:test_state_claimCondition_resetEligibility() (gas: 867671) -DropERC721Test:test_state_claim_allowlisted_DefaultQuantitySomePrice() (gas: 595294) -DropERC721Test:test_state_claim_allowlisted_SetQuantityDefaultPrice() (gas: 800632) -DropERC721Test:test_state_claim_allowlisted_SetQuantityPrice() (gas: 788134) -DropERC721Test:test_state_claim_allowlisted_SetQuantityZeroPrice() (gas: 640226) -DropERC721Test:test_state_getRoleMember_transferRole() (gas: 514719) -DropERC721Test:test_state_grant_transferRole() (gas: 97832) -DropERC721Test:test_state_lazyMint_noEncryptedURI() (gas: 1153017) -DropERC721Test:test_state_lazyMint_withEncryptedURI() (gas: 945838) -DropERC721Test:test_state_reveal() (gas: 1749231) -DynamicAccountTest:test_revert_executeTransaction_nonSigner_viaDirectCall() (gas: 482293) -DynamicAccountTest:test_revert_executeTransaction_nonSigner_viaEntrypoint() (gas: 289922) -DynamicAccountTest:test_scenario_changeExtensionForFunction() (gas: 824007) -DynamicAccountTest:test_state_accountReceivesNativeTokens() (gas: 251653) -DynamicAccountTest:test_state_addAndWithdrawDeposit() (gas: 330071) -DynamicAccountTest:test_state_createAccount_viaEntrypoint() (gas: 244037) -DynamicAccountTest:test_state_createAccount_viaFactory() (gas: 140199) -DynamicAccountTest:test_state_executeBatchTransaction() (gas: 293269) -DynamicAccountTest:test_state_executeBatchTransaction_viaEntrypoint() (gas: 334429) -DynamicAccountTest:test_state_executeTransaction() (gas: 278349) -DynamicAccountTest:test_state_executeTransaction_viaAccountSigner() (gas: 541794) -DynamicAccountTest:test_state_executeTransaction_viaEntrypoint() (gas: 316132) -DynamicAccountTest:test_state_receiveERC1155NFT() (gas: 290563) -DynamicAccountTest:test_state_receiveERC721NFT() (gas: 321365) -DynamicAccountTest:test_state_transferOutsNativeTokens() (gas: 330513) -ERC1155BaseTest:test_revert_batchMintTo_invalidId() (gas: 24111) -ERC1155BaseTest:test_revert_batchMintTo_lengthMismatch() (gas: 24305) -ERC1155BaseTest:test_revert_batchMintTo_mintingZeroTokens() (gas: 22759) -ERC1155BaseTest:test_revert_batchMintTo_unauthorizedCaller() (gas: 21971) -ERC1155BaseTest:test_revert_burnBatch_lengthMismatch() (gas: 270092) -ERC1155BaseTest:test_revert_burnBatch_notEnoughTokensOwned() (gas: 261277) -ERC1155BaseTest:test_revert_burnBatch_unapprovedCaller() (gas: 271572) -ERC1155BaseTest:test_revert_burn_notEnoughTokensOwned() (gas: 119072) -ERC1155BaseTest:test_revert_burn_unapprovedCaller() (gas: 121304) -ERC1155BaseTest:test_revert_mintTo_invalidId() (gas: 20115) -ERC1155BaseTest:test_revert_mintTo_unauthorizedCaller() (gas: 15005) -ERC1155BaseTest:test_state_batchMintTo_existingNFTs() (gas: 403845) -ERC1155BaseTest:test_state_batchMintTo_newAndExistingNFTs() (gas: 511590) -ERC1155BaseTest:test_state_batchMintTo_newNFTs() (gas: 291799) -ERC1155BaseTest:test_state_burn() (gas: 96432) -ERC1155BaseTest:test_state_burnBatch() (gas: 227630) -ERC1155BaseTest:test_state_mintTo_existingNFTs() (gas: 151497) -ERC1155BaseTest:test_state_mintTo_newNFTs() (gas: 120638) -ERC1155DelayedRevealTest:test_state_reveal() (gas: 187362) -ERC1155DelayedRevealTest:test_state_reveal_additionalBatch() (gas: 299024) -ERC1155DropTest:test_revert_claim_exceedsMaxSupply() (gas: 314239) -ERC1155DropTest:test_revert_claim_insufficientPrice() (gas: 356386) -ERC1155DropTest:test_revert_claim_invalidCurrency() (gas: 314100) -ERC1155DropTest:test_revert_claim_invalidPrice() (gas: 313956) -ERC1155DropTest:test_revert_claim_invalidQtyProof() (gas: 403526) -ERC1155DropTest:test_revert_claim_invalidQuantity() (gas: 314497) -ERC1155DropTest:test_revert_setClaimConditions_supplyClaimedAlready() (gas: 361303) -ERC1155DropTest:test_revert_setClaimConditions_unauthorizedCaller() (gas: 134239) -ERC1155DropTest:test_state_claim() (gas: 366526) -ERC1155DropTest:test_state_claim_withAllowlist() (gas: 504827) -ERC1155DropTest:test_state_claim_withPrice() (gas: 445970) -ERC1155DropTest:test_state_setClaimConditions() (gas: 248810) -ERC1155DropTest:test_state_setClaimConditions_resetEligibility() (gas: 376156) -ERC1155LazyMintTest:test_revert_mintTo_invalidId() (gas: 19564) -ERC1155LazyMintTest:test_state_claim() (gas: 81953) -ERC1155SignatureMintTest:test_revert_mintWithSignature_invalidId() (gas: 216041) -ERC1155SignatureMintTest:test_revert_mintWithSignature_mintingZeroTokens() (gas: 212793) -ERC1155SignatureMintTest:test_revert_mintWithSignature_withPrice_incorrectPrice() (gas: 314496) -ERC1155SignatureMintTest:test_state_mintWithSignature_existingNFTs() (gas: 441826) -ERC1155SignatureMintTest:test_state_mintWithSignature_newNFTs() (gas: 401408) -ERC1155SignatureMintTest:test_state_mintWithSignature_withPrice() (gas: 454908) -EditionStakeEthRewardTest:test_Macro_EditionDirectSafeTransferLocksToken() (gas: 53454) -EditionStakeEthRewardTest:test_revert_claimRewards_noRewards() (gas: 318196) -EditionStakeEthRewardTest:test_revert_setRewardsPerUnitTime_notAuthorized() (gas: 16268) -EditionStakeEthRewardTest:test_revert_setTimeUnit_notAuthorized() (gas: 16421) -EditionStakeEthRewardTest:test_revert_stake_notBalanceOrApproved() (gas: 106401) -EditionStakeEthRewardTest:test_revert_stake_stakingZeroTokens() (gas: 19266) -EditionStakeEthRewardTest:test_revert_withdraw_withdrawingMoreThanStaked() (gas: 633520) -EditionStakeEthRewardTest:test_revert_withdraw_withdrawingZeroTokens() (gas: 21373) -EditionStakeEthRewardTest:test_revert_zeroTimeUnit_adminLockTokens() (gas: 224188) -EditionStakeEthRewardTest:test_state_claimRewards_defaults_differentTokens() (gas: 575810) -EditionStakeEthRewardTest:test_state_claimRewards_defaults_sameToken() (gas: 485273) -EditionStakeEthRewardTest:test_state_setRewardsPerUnitTime_bothTokens() (gas: 943061) -EditionStakeEthRewardTest:test_state_setRewardsPerUnitTime_token0() (gas: 851038) -EditionStakeEthRewardTest:test_state_setTimeUnit_bothTokens() (gas: 942263) -EditionStakeEthRewardTest:test_state_setTimeUnit_token0() (gas: 850377) -EditionStakeEthRewardTest:test_state_stake_defaults_differentTokens() (gas: 460069) -EditionStakeEthRewardTest:test_state_stake_defaults_sameToken() (gas: 369181) -EditionStakeEthRewardTest:test_state_withdraw_differentTokens() (gas: 541174) -EditionStakeEthRewardTest:test_state_withdraw_sameToken() (gas: 447590) -EditionStakeTest:test_Macro_EditionDirectSafeTransferLocksToken() (gas: 53454) -EditionStakeTest:test_revert_claimRewards_noRewards() (gas: 304700) -EditionStakeTest:test_revert_setRewardsPerUnitTime_notAuthorized() (gas: 16268) -EditionStakeTest:test_revert_setTimeUnit_notAuthorized() (gas: 16421) -EditionStakeTest:test_revert_stake_notBalanceOrApproved() (gas: 106401) -EditionStakeTest:test_revert_stake_stakingZeroTokens() (gas: 19266) -EditionStakeTest:test_revert_withdraw_withdrawingMoreThanStaked() (gas: 633520) -EditionStakeTest:test_revert_withdraw_withdrawingZeroTokens() (gas: 21373) -EditionStakeTest:test_revert_zeroTimeUnit_adminLockTokens() (gas: 224188) -EditionStakeTest:test_state_claimRewards_defaults_differentTokens() (gas: 547414) -EditionStakeTest:test_state_claimRewards_defaults_sameToken() (gas: 456877) -EditionStakeTest:test_state_setRewardsPerUnitTime_bothTokens() (gas: 943061) -EditionStakeTest:test_state_setRewardsPerUnitTime_token0() (gas: 851038) -EditionStakeTest:test_state_setTimeUnit_bothTokens() (gas: 942263) -EditionStakeTest:test_state_setTimeUnit_token0() (gas: 850377) -EditionStakeTest:test_state_stake_defaults_differentTokens() (gas: 460069) -EditionStakeTest:test_state_stake_defaults_sameToken() (gas: 369181) -EditionStakeTest:test_state_withdraw_differentTokens() (gas: 541174) -EditionStakeTest:test_state_withdraw_sameToken() (gas: 447590) -ExtensionBatchMintMetadata:test_state_batchMintMetadata() (gas: 83302) -ExtensionBatchMintMetadata:test_state_setBaseURI() (gas: 85983) -ExtensionContractMetadataTest:test_event_setContractURI() (gas: 60227) -ExtensionContractMetadataTest:test_revert_setContractURI() (gas: 10997) -ExtensionContractMetadataTest:test_state_setContractURI() (gas: 57648) -ExtensionDelayedReveal:test_revert_getRevealURI_IncorrectKey() (gas: 130490) -ExtensionDelayedReveal:test_revert_getRevealURI_NothingToReveal() (gas: 102113) -ExtensionDelayedReveal:test_state_getRevealURI() (gas: 128570) -ExtensionDelayedReveal:test_state_setEncryptedData() (gas: 123858) -ExtensionDropSinglePhase1155:test_claimCondition_resetEligibility_quantityLimitPerWallet() (gas: 221867) -ExtensionDropSinglePhase1155:test_event_claim() (gas: 175829) -ExtensionDropSinglePhase1155:test_event_setClaimConditions() (gas: 125433) -ExtensionDropSinglePhase1155:test_fuzz_claim_noAllowlist(uint256) (runs: 256, μ: 150230, ~: 150230) -ExtensionDropSinglePhase1155:test_revert_claimCondition_exceedMaxClaimableSupply() (gas: 182320) -ExtensionDropSinglePhase1155:test_state_claimCondition_uniqueConditionId() (gas: 183437) -ExtensionDropSinglePhase:test_event_claim() (gas: 150579) -ExtensionDropSinglePhase:test_event_setClaimConditions() (gas: 103102) -ExtensionDropSinglePhase:test_fuzz_claim_merkleProof(uint256) (runs: 256, μ: 208680, ~: 209346) -ExtensionDropSinglePhase:test_fuzz_claim_noAllowlist(uint256) (runs: 256, μ: 142190, ~: 142190) -ExtensionDropSinglePhase:test_revert_claimCondition_exceedMaxClaimableSupply() (gas: 155209) -ExtensionLazyMint:test_event_lazyMint() (gas: 106851) -ExtensionLazyMint:test_state_lazyMint() (gas: 174500) -ExtensionLazyMint:test_state_lazyMint_NotAuthorized() (gas: 11407) -ExtensionLazyMint:test_state_lazyMint_ZeroAmount() (gas: 32140) -ExtensionOwnableTest:test_event_setOwner() (gas: 34863) -ExtensionOwnableTest:test_revert_setOwner() (gas: 10571) -ExtensionOwnableTest:test_state_setOwner() (gas: 31277) -ExtensionPermissions:test_event_grantRole() (gas: 42337) -ExtensionPermissions:test_event_revokeRole() (gas: 33042) -ExtensionPermissions:test_event_roleAdminChanged() (gas: 35380) -ExtensionPermissions:test_modifier_onlyRole() (gas: 71310) -ExtensionPermissions:test_revert_grantRole_grantToHolder() (gas: 41857) -ExtensionPermissions:test_revert_grantRole_missingRole() (gas: 73649) -ExtensionPermissions:test_revert_renounceRole_missingRole() (gas: 73364) -ExtensionPermissions:test_revert_renounceRole_renounceForOthers() (gas: 43109) -ExtensionPermissions:test_revert_revokeRole_missingRole() (gas: 169777) -ExtensionPermissions:test_state_grantRole() (gas: 121756) -ExtensionPermissions:test_state_renounceRole() (gas: 33069) -ExtensionPermissions:test_state_revokeRole() (gas: 33089) -ExtensionPermissions:test_state_setRoleAdmin() (gas: 34595) -ExtensionPermissionsEnumerable:test_state_grantRole() (gas: 225840) -ExtensionPermissionsEnumerable:test_state_revokeRole() (gas: 312434) -ExtensionPlatformFee:test_event_platformFeeInfo() (gas: 56724) -ExtensionPlatformFee:test_revert_setPlatformFeeInfo_ExceedsMaxBps() (gas: 31761) -ExtensionPlatformFee:test_revert_setPlatformFeeInfo_NotAuthorized() (gas: 10857) -ExtensionPlatformFee:test_state_setPlatformFeeInfo() (gas: 53399) -ExtensionPrimarySale:test_event_setPrimarySaleRecipient() (gas: 34040) -ExtensionPrimarySale:test_revert_setPrimarySaleRecipient_NotAuthorized() (gas: 10557) -ExtensionPrimarySale:test_state_setPrimarySaleRecipient() (gas: 31055) -ExtensionRegistryTest:test_revert_addExtension_emptyExtensionImplementation() (gas: 133995) -ExtensionRegistryTest:test_revert_addExtension_fnSelectorSignatureMismatch() (gas: 2100691) -ExtensionRegistryTest:test_revert_addExtension_sameExtensionName() (gas: 3319813) -ExtensionRegistryTest:test_revert_addExtension_unauthorizedCaller() (gas: 57908) -ExtensionRegistryTest:test_revert_addExtensionsWithSameFunctionSelectors() (gas: 3433892) -ExtensionRegistryTest:test_revert_removeExtension_extensionDoesNotExist() (gas: 17210) -ExtensionRegistryTest:test_revert_removeExtension_unauthorizedCaller() (gas: 2390986) -ExtensionRegistryTest:test_revert_updateExtension_extensionDoesNotExist() (gas: 1967399) -ExtensionRegistryTest:test_revert_updateExtension_fnSelectorSignatureMismatch() (gas: 4084008) -ExtensionRegistryTest:test_revert_updateExtension_notUpdatingImplementation() (gas: 2252102) -ExtensionRegistryTest:test_revert_updateExtension_unauthorizedCaller() (gas: 4221664) -ExtensionRegistryTest:test_state_addExtension() (gas: 1032508) -ExtensionRegistryTest:test_state_removeExtension() (gas: 4280618) -ExtensionRegistryTest:test_state_updateExtension_allNewFunctions() (gas: 4260519) -ExtensionRegistryTest:test_state_updateExtension_someNewFunctions() (gas: 4397981) -ExtensionRoyaltyTest:test_event_defaultRoyalty() (gas: 56878) -ExtensionRoyaltyTest:test_event_royaltyForToken() (gas: 80163) -ExtensionRoyaltyTest:test_revert_setDefaultRoyaltyInfo_ExceedsMaxBps() (gas: 32012) -ExtensionRoyaltyTest:test_revert_setRoyaltyInfoForToken_ExceedsMaxBps() (gas: 32069) -ExtensionRoyaltyTest:test_revert_setRoyaltyInfo_NotAuthorized() (gas: 10872) -ExtensionRoyaltyTest:test_state_setDefaultRoyaltyInfo() (gas: 59864) -ExtensionRoyaltyTest:test_state_setRoyaltyInfoForToken() (gas: 76828) -ExtensionSignatureMintERC1155:test_revert_mintWithSignature_InvalidReq() (gas: 108160) -ExtensionSignatureMintERC1155:test_revert_mintWithSignature_NotAuthorized() (gas: 44036) -ExtensionSignatureMintERC1155:test_revert_mintWithSignature_RequestExpired() (gas: 76714) -ExtensionSignatureMintERC1155:test_state_mintWithSignature() (gas: 96920) -ExtensionSignatureMintERC20:test_revert_mintWithSignature_InvalidReq() (gas: 96996) -ExtensionSignatureMintERC20:test_revert_mintWithSignature_NotAuthorized() (gas: 35020) -ExtensionSignatureMintERC20:test_revert_mintWithSignature_RequestExpired() (gas: 67121) -ExtensionSignatureMintERC20:test_state_mintWithSignature() (gas: 87327) -ExtensionSignatureMintERC721:test_revert_mintWithSignature_InvalidReq() (gas: 105730) -ExtensionSignatureMintERC721:test_revert_mintWithSignature_NotAuthorized() (gas: 41817) -ExtensionSignatureMintERC721:test_revert_mintWithSignature_RequestExpired() (gas: 74511) -ExtensionSignatureMintERC721:test_state_mintWithSignature() (gas: 94728) -ExtensionTokenBundle:test_revert_createBundle_emptyBundle() (gas: 8910) -ExtensionTokenBundle:test_revert_createBundle_existingBundleId() (gas: 211575) -ExtensionTokenBundle:test_revert_createBundle_tokenTypeMismatch() (gas: 1053809) -ExtensionTokenBundle:test_revert_updateBundle_emptyBundle() (gas: 209621) -ExtensionTokenBundle:test_revert_updateTokenInBundle_indexDNE() (gas: 212217) -ExtensionTokenBundle:test_state_addTokenInBundle() (gas: 292608) -ExtensionTokenBundle:test_state_createBundle() (gas: 218016) -ExtensionTokenBundle:test_state_updateBundle() (gas: 320404) -ExtensionTokenBundle:test_state_updateTokenInBundle() (gas: 235193) -ExtensionTokenStore:test_balances_releaseTokens() (gas: 362264) -ExtensionTokenStore:test_balances_storeTokens() (gas: 346405) -ForwarderChainlessDomainTest:test_state_forwarderChainlessDomain() (gas: 81756) -ForwarderTest:test_state_forwarder() (gas: 81617) -IssueC2_MarketplaceDirectListingsTest:test_state_buyFromListing_after_update() (gas: 538057) -IssueC3_MarketplaceEnglishAuctionsTest:test_state_collectAuctionTokens_afterAuctionPayout() (gas: 588074) -Macro_EditionStakeTest:testEdition_adminLockTokens() (gas: 483849) -Macro_EditionStakeTest:testEdition_demostrate_adminRewardsLock() (gas: 580875) -Macro_TokenStakeTest:testToken_adminLockTokens() (gas: 292846) -Macro_TokenStakeTest:testToken_demostrate_adminRewardsLock() (gas: 503329) -Macro_TokenStake_Rewards18_Staking6_Test:test_Macro_reward18_staking6() (gas: 222813) -Macro_TokenStake_Rewards6_Staking18_Test:test_Macro_reward6_staking18() (gas: 223319) -Macro_TokenStake_Tax:testToken_demonstrate_inaccurate_amount() (gas: 231438) -ManagedAccountTest:test_revert_executeTransaction_nonSigner_viaDirectCall() (gas: 484833) -ManagedAccountTest:test_revert_executeTransaction_nonSigner_viaEntrypoint() (gas: 289147) -ManagedAccountTest:test_scenario_changeExtensionForFunction() (gas: 828640) -ManagedAccountTest:test_state_accountReceivesNativeTokens() (gas: 251230) -ManagedAccountTest:test_state_addAndWithdrawDeposit() (gas: 329010) -ManagedAccountTest:test_state_createAccount_viaEntrypoint() (gas: 243328) -ManagedAccountTest:test_state_createAccount_viaFactory() (gas: 138282) -ManagedAccountTest:test_state_executeBatchTransaction() (gas: 294135) -ManagedAccountTest:test_state_executeBatchTransaction_viaEntrypoint() (gas: 334946) -ManagedAccountTest:test_state_executeTransaction() (gas: 279200) -ManagedAccountTest:test_state_executeTransaction_viaAccountSigner() (gas: 544268) -ManagedAccountTest:test_state_executeTransaction_viaEntrypoint() (gas: 316634) -ManagedAccountTest:test_state_receiveERC1155NFT() (gas: 291414) -ManagedAccountTest:test_state_receiveERC721NFT() (gas: 320942) -ManagedAccountTest:test_state_transferOutsNativeTokens() (gas: 331338) -MapTest:test_state_getAllFunctionsOfPlugin() (gas: 4290863) -MapTest:test_state_getAllPlugins() (gas: 838217) -MapTest:test_state_getPluginForFunction() (gas: 409580) -MarketplaceDirectListingsTest:test_revert_approveBuyerForListing_listingNotReserved() (gas: 420579) -MarketplaceDirectListingsTest:test_revert_approveBuyerForListing_notListingCreator() (gas: 378766) -MarketplaceDirectListingsTest:test_revert_approveCurrencyForListing_notListingCreator() (gas: 365076) -MarketplaceDirectListingsTest:test_revert_approveCurrencyForListing_reApprovingMainCurrency() (gas: 376243) -MarketplaceDirectListingsTest:test_revert_buyFromListing_buyerBalanceLessThanPrice() (gas: 517551) -MarketplaceDirectListingsTest:test_revert_buyFromListing_buyingMoreQuantityThanListed() (gas: 509425) -MarketplaceDirectListingsTest:test_revert_buyFromListing_buyingZeroQuantity() (gas: 449612) -MarketplaceDirectListingsTest:test_revert_buyFromListing_invalidCurrency() (gas: 524598) -MarketplaceDirectListingsTest:test_revert_buyFromListing_nativeToken_incorrectValueSent() (gas: 474543) -MarketplaceDirectListingsTest:test_revert_buyFromListing_notApprovedMarketplaceToTransferPrice() (gas: 499074) -MarketplaceDirectListingsTest:test_revert_buyFromListing_unexpectedTotalPrice() (gas: 474485) -MarketplaceDirectListingsTest:test_revert_cancelListing_nonExistentListing() (gas: 382453) -MarketplaceDirectListingsTest:test_revert_cancelListing_notListingCreator() (gas: 376400) -MarketplaceDirectListingsTest:test_revert_createListing_invalidEndTimestamp() (gas: 175926) -MarketplaceDirectListingsTest:test_revert_createListing_invalidStartTimestamp() (gas: 176645) -MarketplaceDirectListingsTest:test_revert_createListing_listingInvalidQuantity() (gas: 177263) -MarketplaceDirectListingsTest:test_revert_createListing_listingNonERC721OrERC1155Token() (gas: 158728) -MarketplaceDirectListingsTest:test_revert_createListing_listingZeroQuantity() (gas: 177222) -MarketplaceDirectListingsTest:test_revert_createListing_noAssetRoleWhenRestrictionsActive() (gas: 167324) -MarketplaceDirectListingsTest:test_revert_createListing_noListerRoleWhenRestrictionsActive() (gas: 160771) -MarketplaceDirectListingsTest:test_revert_createListing_notApprovedMarketplaceToTransferToken() (gas: 164955) -MarketplaceDirectListingsTest:test_revert_createListing_notOwnerOfListedToken() (gas: 181748) -MarketplaceDirectListingsTest:test_revert_updateListing_invalidEndTimestamp() (gas: 401691) -MarketplaceDirectListingsTest:test_revert_updateListing_invalidStartTimestamp() (gas: 402426) -MarketplaceDirectListingsTest:test_revert_updateListing_listingInvalidQuantity() (gas: 403962) -MarketplaceDirectListingsTest:test_revert_updateListing_listingNonERC721OrERC1155Token() (gas: 489291) -MarketplaceDirectListingsTest:test_revert_updateListing_listingZeroQuantity() (gas: 404318) -MarketplaceDirectListingsTest:test_revert_updateListing_noAssetRoleWhenRestrictionsActive() (gas: 411622) -MarketplaceDirectListingsTest:test_revert_updateListing_notApprovedMarketplaceToTransferToken() (gas: 394297) -MarketplaceDirectListingsTest:test_revert_updateListing_notListingCreator() (gas: 397706) -MarketplaceDirectListingsTest:test_revert_updateListing_notOwnerOfListedToken() (gas: 461711) -MarketplaceDirectListingsTest:test_state_approveBuyerForListing() (gas: 408721) -MarketplaceDirectListingsTest:test_state_approveCurrencyForListing() (gas: 405574) -MarketplaceDirectListingsTest:test_state_approvedCurrencies() (gas: 473990) -MarketplaceDirectListingsTest:test_state_buyFromListing() (gas: 520307) -MarketplaceDirectListingsTest:test_state_buyFromListing_nativeToken() (gas: 515181) -MarketplaceDirectListingsTest:test_state_cancelListing() (gas: 385192) -MarketplaceDirectListingsTest:test_state_createListing() (gas: 373581) -MarketplaceDirectListingsTest:test_state_initial() (gas: 24285) -MarketplaceDirectListingsTest:test_state_updateListing() (gas: 441381) -MarketplaceEnglishAuctionsTest:test_revert_bidInAuction_inactiveAuction() (gas: 514519) -MarketplaceEnglishAuctionsTest:test_revert_bidInAuction_notApprovedMarketplaceToTransferToken() (gas: 521255) -MarketplaceEnglishAuctionsTest:test_revert_bidInAuction_notNewWinningBid_firstBid() (gas: 501293) -MarketplaceEnglishAuctionsTest:test_revert_bidInAuction_notNewWinningBid_secondBid() (gas: 619950) -MarketplaceEnglishAuctionsTest:test_revert_bidInAuction_notOwnerOfBidTokens() (gas: 503747) -MarketplaceEnglishAuctionsTest:test_revert_cancelAuction_bidsAlreadyMade() (gas: 551400) -MarketplaceEnglishAuctionsTest:test_revert_collectAuctionPayout_auctionNotExpired() (gas: 576534) -MarketplaceEnglishAuctionsTest:test_revert_collectAuctionPayout_noBidsInAuction() (gas: 441604) -MarketplaceEnglishAuctionsTest:test_revert_collectAuctionTokens_auctionNotExpired() (gas: 568088) -MarketplaceEnglishAuctionsTest:test_revert_createAuction_auctioningZeroQuantity() (gas: 176955) -MarketplaceEnglishAuctionsTest:test_revert_createAuction_invalidAssetContract() (gas: 158927) -MarketplaceEnglishAuctionsTest:test_revert_createAuction_invalidBidAmounts() (gas: 177163) -MarketplaceEnglishAuctionsTest:test_revert_createAuction_invalidEndTimestamp() (gas: 177279) -MarketplaceEnglishAuctionsTest:test_revert_createAuction_invalidQuantity() (gas: 176996) -MarketplaceEnglishAuctionsTest:test_revert_createAuction_invalidStartTimestamp() (gas: 177482) -MarketplaceEnglishAuctionsTest:test_revert_createAuction_noAssetRoleWhenRestrictionsActive() (gas: 167529) -MarketplaceEnglishAuctionsTest:test_revert_createAuction_noBidOrTimeBuffer() (gas: 238997) -MarketplaceEnglishAuctionsTest:test_revert_createAuction_noListerRoleWhenRestrictionsActive() (gas: 161224) -MarketplaceEnglishAuctionsTest:test_revert_createAuction_notApprovedMarketplaceToTransferToken() (gas: 346064) -MarketplaceEnglishAuctionsTest:test_revert_createAuction_notOwnerOfAuctionedToken() (gas: 365276) -MarketplaceEnglishAuctionsTest:test_revert_isAuctionExpired() (gas: 392075) -MarketplaceEnglishAuctionsTest:test_revert_isNewWinningBid() (gas: 548606) -MarketplaceEnglishAuctionsTest:test_state_bidInAuction_buyoutBid() (gas: 671699) -MarketplaceEnglishAuctionsTest:test_state_bidInAuction_firstBid() (gas: 553804) -MarketplaceEnglishAuctionsTest:test_state_bidInAuction_nativeToken() (gas: 547436) -MarketplaceEnglishAuctionsTest:test_state_bidInAuction_secondBid() (gas: 632367) -MarketplaceEnglishAuctionsTest:test_state_cancelAuction() (gas: 441097) -MarketplaceEnglishAuctionsTest:test_state_collectAuctionPayout_afterAuctionEnds() (gas: 612793) -MarketplaceEnglishAuctionsTest:test_state_collectAuctionPayout_buyoutBid() (gas: 628265) -MarketplaceEnglishAuctionsTest:test_state_collectAuctionTokens() (gas: 609521) -MarketplaceEnglishAuctionsTest:test_state_createAuction() (gas: 404448) -MarketplaceEnglishAuctionsTest:test_state_getAllAuctions() (gas: 1609131) -MarketplaceEnglishAuctionsTest:test_state_getAllValidAuctions() (gas: 1899237) -MarketplaceEnglishAuctionsTest:test_state_initial() (gas: 24109) -MarketplaceEnglishAuctionsTest:test_state_isAuctionExpired() (gas: 402646) -MarketplaceEnglishAuctionsTest:test_state_isNewWinningBid() (gas: 562076) -MarketplaceOffersTest:test_revert_acceptOffer_notApprovedMarketplaceToTransferOfferedTokens() (gas: 420747) -MarketplaceOffersTest:test_revert_acceptOffer_notApprovedMarketplaceToTransferPrice() (gas: 418239) -MarketplaceOffersTest:test_revert_acceptOffer_notOwnedRequiredTokens() (gas: 439372) -MarketplaceOffersTest:test_revert_acceptOffer_offerorBalanceLessThanPrice() (gas: 397154) -MarketplaceOffersTest:test_revert_cancelOffer_callerNotOfferor() (gas: 330014) -MarketplaceOffersTest:test_revert_createListing_noAssetRoleWhenRestrictionsActive() (gas: 134626) -MarketplaceOffersTest:test_revert_makeOffer_invalidAssetContract() (gas: 226005) -MarketplaceOffersTest:test_revert_makeOffer_invalidExpirationTimestamp() (gas: 147930) -MarketplaceOffersTest:test_revert_makeOffer_invalidQuantity() (gas: 147592) -MarketplaceOffersTest:test_revert_makeOffer_notApprovedMarketplaceToTransferTokens() (gas: 128523) -MarketplaceOffersTest:test_revert_makeOffer_notOwnerOfOfferedTokens() (gas: 102767) -MarketplaceOffersTest:test_revert_makeOffer_wantedZeroTokens() (gas: 147241) -MarketplaceOffersTest:test_state_acceptOffer() (gas: 480946) -MarketplaceOffersTest:test_state_cancelOffer() (gas: 357974) -MarketplaceOffersTest:test_state_getAllOffers() (gas: 1240932) -MarketplaceOffersTest:test_state_getAllValidOffers() (gas: 1544879) -MarketplaceOffersTest:test_state_initial() (gas: 24142) -MarketplaceOffersTest:test_state_makeOffer() (gas: 336831) -MarketplaceTest:test_acceptOffer_expiration() (gas: 569752) -MarketplaceTest:test_acceptOffer_whenListingAcceptsNativeToken() (gas: 538615) -MarketplaceTest:test_createListing_auctionListing() (gas: 342542) -MarketplaceTest:test_createListing_auctionListing_ZeroBuyoutAmount() (gas: 327851) -MarketplaceTest:test_createListing_startTime_future() (gas: 342673) -MarketplaceTest:test_createListing_startTime_now() (gas: 343024) -MarketplaceTest:test_createListing_startTime_past() (gas: 146795) -MarketplaceTest:test_createListing_startTime_pastWithBuffer() (gas: 342595) -MarketplaceTest:test_offer_bidAuctionNativeToken() (gas: 586716) -MarketplaceTest:test_revert_offer_bidZeroAmount() (gas: 348014) -MarketplaceTest:test_updateListing_startTimeAndEndTime() (gas: 356090) -MarketplaceTest:test_updateListing_startTime_future() (gas: 351134) -MarketplaceTest:test_updateListing_startTime_past() (gas: 347725) -MinimalFactoryTest:test_gas_minimal() (gas: 692832) -MinimalFactoryTest:test_gas_notMinimalFactory() (gas: 651841) -MinimalFactoryTest:test_gas_twProxy() (gas: 793009) -MinimalFactoryTest:test_verify_deployedProxy() (gas: 698484) -MultiwrapTest:test_balances_unwrap() (gas: 502520) -MultiwrapTest:test_balances_wrap() (gas: 500732) -MultiwrapTest:test_balances_wrap_nativeTokens_multipleInstances() (gas: 353529) -MultiwrapTest:test_event_unwrap_TokensUnwrapped() (gas: 488478) -MultiwrapTest:test_event_wrap_TokensWrapped() (gas: 486434) -MultiwrapTest:test_fuzz_state_unwrap(uint256) (runs: 256, μ: 47555979, ~: 52727530) -MultiwrapTest:test_fuzz_state_wrap(uint256) (runs: 256, μ: 49552706, ~: 53288762) -MultiwrapTest:test_revert_nonHolder_renounceRole() (gas: 73666) -MultiwrapTest:test_revert_revokeRoleForNonHolder() (gas: 80378) -MultiwrapTest:test_revert_unwrap_access_UNWRAP_ROLE() (gas: 563223) -MultiwrapTest:test_revert_unwrap_invalidTokenId() (gas: 488469) -MultiwrapTest:test_revert_unwrap_notOwner() (gas: 509015) -MultiwrapTest:test_revert_unwrap_unapprovedCaller() (gas: 493717) -MultiwrapTest:test_revert_wrap_access_ASSET_ROLE() (gas: 129365) -MultiwrapTest:test_revert_wrap_access_MINTER_ROLE() (gas: 113560) -MultiwrapTest:test_revert_wrap_nativeTokens_insufficientValue() (gas: 149863) -MultiwrapTest:test_revert_wrap_nativeTokens_insufficientValueProvided_multipleInstances() (gas: 204295) -MultiwrapTest:test_revert_wrap_noTokensToWrap() (gas: 54481) -MultiwrapTest:test_revert_wrap_notApprovedTransfer_ERC1155() (gas: 375475) -MultiwrapTest:test_revert_wrap_notApprovedTransfer_ERC20() (gas: 289704) -MultiwrapTest:test_revert_wrap_notApprovedTransfer_ERC721() (gas: 332828) -MultiwrapTest:test_revert_wrap_notOwner_ERC1155() (gas: 408114) -MultiwrapTest:test_revert_wrap_notOwner_ERC20() (gas: 319786) -MultiwrapTest:test_revert_wrap_notOwner_ERC721() (gas: 363610) -MultiwrapTest:test_revert_wrap_reentrancy() (gas: 2226850) -MultiwrapTest:test_state_unwrap() (gas: 495078) -MultiwrapTest:test_state_unwrap_approvedCaller() (gas: 517792) -MultiwrapTest:test_state_unwrap_nativeTokens() (gas: 295184) -MultiwrapTest:test_state_wrap() (gas: 498458) -MultiwrapTest:test_state_wrap_nativeTokens() (gas: 311767) -MultiwrapTest:test_state_wrap_withAssetRoleRestriction() (gas: 735045) -NFTStakeEthRewardTest:test_Macro_NFTDirectSafeTransferLocksToken() (gas: 63193) -NFTStakeEthRewardTest:test_revert_claimRewards_noRewards() (gas: 494354) -NFTStakeEthRewardTest:test_revert_largeRewardsPerUnitTime_adminRewardsLock() (gas: 847163) -NFTStakeEthRewardTest:test_revert_setRewardsPerUnitTime_notAuthorized() (gas: 15903) -NFTStakeEthRewardTest:test_revert_setTimeUnit_notAuthorized() (gas: 16265) -NFTStakeEthRewardTest:test_revert_stake_notStaker() (gas: 104914) -NFTStakeEthRewardTest:test_revert_stake_stakingZeroTokens() (gas: 19253) -NFTStakeEthRewardTest:test_revert_withdraw_notStaker() (gas: 377026) -NFTStakeEthRewardTest:test_revert_withdraw_withdrawingMoreThanStaked() (gas: 269826) -NFTStakeEthRewardTest:test_revert_withdraw_withdrawingZeroTokens() (gas: 21476) -NFTStakeEthRewardTest:test_revert_zeroTimeUnit_adminLockTokens() (gas: 414812) -NFTStakeEthRewardTest:test_state_claimRewards() (gas: 544731) -NFTStakeEthRewardTest:test_state_setRewardsPerUnitTime() (gas: 702713) -NFTStakeEthRewardTest:test_state_setTimeUnit() (gas: 704200) -NFTStakeEthRewardTest:test_state_stake() (gas: 809055) -NFTStakeEthRewardTest:test_state_withdraw() (gas: 772279) -NFTStakeTest:test_Macro_NFTDirectSafeTransferLocksToken() (gas: 63193) -NFTStakeTest:test_revert_claimRewards_noRewards() (gas: 480833) -NFTStakeTest:test_revert_largeRewardsPerUnitTime_adminRewardsLock() (gas: 847163) -NFTStakeTest:test_revert_setRewardsPerUnitTime_notAuthorized() (gas: 15903) -NFTStakeTest:test_revert_setTimeUnit_notAuthorized() (gas: 16265) -NFTStakeTest:test_revert_stake_notStaker() (gas: 104914) -NFTStakeTest:test_revert_stake_stakingZeroTokens() (gas: 19253) -NFTStakeTest:test_revert_withdraw_notStaker() (gas: 377026) -NFTStakeTest:test_revert_withdraw_withdrawingMoreThanStaked() (gas: 269826) -NFTStakeTest:test_revert_withdraw_withdrawingZeroTokens() (gas: 21476) -NFTStakeTest:test_revert_zeroTimeUnit_adminLockTokens() (gas: 414812) -NFTStakeTest:test_state_claimRewards() (gas: 531101) -NFTStakeTest:test_state_setRewardsPerUnitTime() (gas: 702713) -NFTStakeTest:test_state_setTimeUnit() (gas: 704200) -NFTStakeTest:test_state_stake() (gas: 809055) -NFTStakeTest:test_state_withdraw() (gas: 772279) -OpenEditionERC721Test:test_claimCondition_startIdAndCount() (gas: 343270) -OpenEditionERC721Test:test_claimCondition_startPhase() (gas: 362104) -OpenEditionERC721Test:test_claimCondition_with_startTimestamp() (gas: 364603) -OpenEditionERC721Test:test_claim_transferRole() (gas: 363163) -OpenEditionERC721Test:test_event_setSharedMetadata_SharedMetadataUpdated() (gas: 135347) -OpenEditionERC721Test:test_fuzz_claim_merkleProof(uint256) (runs: 256, μ: 851408, ~: 810433) -OpenEditionERC721Test:test_fuzz_claim_noAllowlist(uint256) (runs: 256, μ: 310676, ~: 310676) -OpenEditionERC721Test:test_member_count_incremented_properly_when_role_granted() (gas: 99120) -OpenEditionERC721Test:test_revert_claimCondition_exceedMaxClaimableSupply() (gas: 547744) -OpenEditionERC721Test:test_revert_grant_role_to_account_with_role() (gas: 95070) -OpenEditionERC721Test:test_revert_nonHolder_renounceRole() (gas: 75845) -OpenEditionERC721Test:test_revert_revokeRoleForNonHolder() (gas: 82645) -OpenEditionERC721Test:test_revert_setSharedMetadata_MINTER_ROLE() (gas: 30241) -OpenEditionERC721Test:test_state_claimCondition_resetEligibility() (gas: 851135) -OpenEditionERC721Test:test_state_claim_allowlisted_DefaultQuantitySomePrice() (gas: 577500) -OpenEditionERC721Test:test_state_claim_allowlisted_SetQuantityDefaultPrice() (gas: 787193) -OpenEditionERC721Test:test_state_claim_allowlisted_SetQuantityPrice() (gas: 770362) -OpenEditionERC721Test:test_state_claim_allowlisted_SetQuantityZeroPrice() (gas: 624551) -OpenEditionERC721Test:test_state_getRoleMember_transferRole() (gas: 526550) -OpenEditionERC721Test:test_state_grant_transferRole() (gas: 100993) -OpenEditionERC721Test:test_state_sharedMetadata() (gas: 690296) -OpenPackBenchmarkTest:test_benchmark_openPack() (gas: 123789) -OpenPackLargeInputsTest:test_fuzz_failing_state_openPack() (gas: 9757122250) -PackTest:test_balances_addPackContents() (gas: 1720270) -PackTest:test_balances_createPack() (gas: 1470897) -PackTest:test_balances_openPack() (gas: 1678554) -PackTest:test_checkForwarders() (gas: 18810) -PackTest:test_event_createPack_PackCreated() (gas: 1434260) -PackTest:test_event_openPack_PackOpened() (gas: 1486756) -PackTest:test_fuzz_state_createPack(uint256,uint128) (runs: 256, μ: 1747808, ~: 3635) -PackTest:test_interface() (gas: 4007) -PackTest:test_revert_addPackContents_CantUpdateAnymore() (gas: 1470715) -PackTest:test_revert_addPackContents_NotMinterRole() (gas: 1517940) -PackTest:test_revert_addPackContents_NotRecipient() (gas: 1458360) -PackTest:test_revert_addPackContents_PackNonExistent() (gas: 140174) -PackTest:test_revert_addPackContents_RandomAccountGrief() (gas: 1710706) -PackTest:test_revert_createPack_access_ASSET_ROLE() (gas: 221356) -PackTest:test_revert_createPack_access_MINTER_ROLE() (gas: 181562) -PackTest:test_revert_createPack_invalidRewardUnits() (gas: 117521) -PackTest:test_revert_createPack_invalidTokenType() (gas: 131315) -PackTest:test_revert_createPack_nativeTokens_insufficientValue() (gas: 1497106) -PackTest:test_revert_createPack_noTokensToPack() (gas: 49697) -PackTest:test_revert_createPack_notApprovedTransfer_ERC1155() (gas: 1149670) -PackTest:test_revert_createPack_notApprovedTransfer_ERC20() (gas: 1189828) -PackTest:test_revert_createPack_notApprovedTransfer_ERC721() (gas: 1107585) -PackTest:test_revert_createPack_notOwner_ERC1155() (gas: 1182598) -PackTest:test_revert_createPack_notOwner_ERC20() (gas: 1288454) -PackTest:test_revert_createPack_notOwner_ERC721() (gas: 1142573) -PackTest:test_revert_createPack_reentrancy() (gas: 2486988) -PackTest:test_revert_createPack_zeroTotalAmount() (gas: 79047) -PackTest:test_revert_openPack_invalidPackId() (gas: 1437999) -PackTest:test_revert_openPack_notEOA() (gas: 1436601) -PackTest:test_revert_openPack_openBeforeStart() (gas: 1439161) -PackTest:test_revert_openPack_openMoreThanOwned() (gas: 1437122) -PackTest:test_state_addPackContents() (gas: 1772940) -PackTest:test_state_createPack() (gas: 1479689) -PackTest:test_state_createPack_nativeTokens() (gas: 1699686) -PackTest:test_state_createPack_withAssetRoleRestriction() (gas: 1743612) -PackTest:test_state_openPack() (gas: 1664893) -PackTest:test_supportsInterface() (gas: 13465) -PackVRFDirectBenchmarkTest:test_benchmark_fulfillRandomness() (gas: 75675) -PackVRFDirectTest:test_balances_createPack() (gas: 1437179) -PackVRFDirectTest:test_balances_openPack() (gas: 1692107) -PackVRFDirectTest:test_event_createPack_PackCreated() (gas: 1400145) -PackVRFDirectTest:test_event_openPack() (gas: 1508129) -PackVRFDirectTest:test_fuzz_state_createPack(uint256,uint128) (runs: 256, μ: 1737457, ~: 3723) -PackVRFDirectTest:test_interface() (gas: 3985) -PackVRFDirectTest:test_revert_createPack_access_MINTER_ROLE() (gas: 176232) -PackVRFDirectTest:test_revert_createPack_invalidRewardUnits() (gas: 112923) -PackVRFDirectTest:test_revert_createPack_invalidTokenType() (gas: 124223) -PackVRFDirectTest:test_revert_createPack_nativeTokens_insufficientValue() (gas: 1489626) -PackVRFDirectTest:test_revert_createPack_noTokensToPack() (gas: 45099) -PackVRFDirectTest:test_revert_createPack_notApprovedTransfer_ERC1155() (gas: 1142600) -PackVRFDirectTest:test_revert_createPack_notApprovedTransfer_ERC20() (gas: 1182740) -PackVRFDirectTest:test_revert_createPack_notApprovedTransfer_ERC721() (gas: 1100118) -PackVRFDirectTest:test_revert_createPack_notOwner_ERC1155() (gas: 1175438) -PackVRFDirectTest:test_revert_createPack_notOwner_ERC20() (gas: 1281304) -PackVRFDirectTest:test_revert_createPack_notOwner_ERC721() (gas: 1135481) -PackVRFDirectTest:test_revert_createPack_reentrancy() (gas: 2427602) -PackVRFDirectTest:test_revert_createPack_zeroTotalAmount() (gas: 70065) -PackVRFDirectTest:test_revert_openPackAndClaimRewards_ReqInFlight() (gas: 1542247) -PackVRFDirectTest:test_revert_openPack_ReqInFlight() (gas: 1522111) -PackVRFDirectTest:test_revert_openPack_invalidPackId() (gas: 1406038) -PackVRFDirectTest:test_revert_openPack_notEOA() (gas: 1402578) -PackVRFDirectTest:test_revert_openPack_openBeforeStart() (gas: 1405296) -PackVRFDirectTest:test_revert_openPack_openMoreThanOwned() (gas: 1405201) -PackVRFDirectTest:test_state_createPack() (gas: 1445574) -PackVRFDirectTest:test_state_createPack_nativeTokens() (gas: 1665549) -PackVRFDirectTest:test_state_openPack() (gas: 1678035) -PackVRFDirectTest:test_state_openPackAndClaimRewards() (gas: 1614632) -PackVRFDirectTest:test_state_openPackAndClaimRewards_lowGasFailsafe() (gas: 1646170) -PackVRFDirectTest:test_supportsInterface() (gas: 13509) -RouterImmutableTest:test_revert_callWithRouter() (gas: 16706) -RouterImmutableTest:test_state_callWithRouter() (gas: 45357) -RouterTest:test_revert_addPlugin_defaultExists() (gas: 17813) -RouterTest:test_revert_addPlugin_pluginAlreadyExists() (gas: 226279) -RouterTest:test_revert_addPlugin_selectorSignatureMismatch() (gas: 84842) -RouterTest:test_revert_removePlugin_pluginDNE() (gas: 11325) -RouterTest:test_revert_updatePlugin_functionDNE() (gas: 20258) -RouterTest:test_revert_updatePlugin_selectorSignatureMismatch() (gas: 21013) -RouterTest:test_state_addPlugin() (gas: 312374) -RouterTest:test_state_getAllFunctionsOfPlugin() (gas: 444458) -RouterTest:test_state_getPluginForFunction() (gas: 412081) -RouterTest:test_state_removePlugin() (gas: 181994) -RouterTest:test_state_updatePlugin() (gas: 324661) -SignatureDropBenchmarkTest:test_benchmark_claim() (gas: 165870) -SignatureDropBenchmarkTest:test_benchmark_mintWithSignature() (gas: 211745) -SignatureDropTest:testFail_combination_signatureAndClaim() (gas: 1563024) -SignatureDropTest:testFail_reentrancy_claim() (gas: 1404948) -SignatureDropTest:testFail_reentrancy_mintWithSignature() (gas: 1473980) -SignatureDropTest:testFail_reveal_incorrectKey() (gas: 217825) -SignatureDropTest:test_balances_mintWithSignature() (gas: 323061) -SignatureDropTest:test_claimCondition_with_startTimestamp() (gas: 361148) -SignatureDropTest:test_claim_transferRole() (gas: 348109) -SignatureDropTest:test_delayedReveal_withNewLazyMintedEmptyBatch() (gas: 284088) -SignatureDropTest:test_event_lazyMint_TokensLazyMinted() (gas: 132692) -SignatureDropTest:test_event_reveal_TokenURIRevealed() (gas: 192639) -SignatureDropTest:test_fuzz_claim_merkleProof(uint256) (runs: 256, μ: 845197, ~: 814232) -SignatureDropTest:test_fuzz_claim_noAllowlist(uint256) (runs: 256, μ: 256471, ~: 256471) -SignatureDropTest:test_fuzz_lazyMint_batchMintAndNextTokenIdToMint(uint256) (runs: 256, μ: 204468, ~: 204468) -SignatureDropTest:test_fuzz_lazyMint_noEncryptedURI(uint256) (runs: 256, μ: 185292, ~: 170236) -SignatureDropTest:test_fuzz_lazyMint_withEncryptedURI(uint256) (runs: 256, μ: 245535, ~: 245535) -SignatureDropTest:test_fuzz_mintWithSignature(uint128,uint128) (runs: 256, μ: 219080, ~: 427300) -SignatureDropTest:test_member_count_incremented_properly_when_role_granted() (gas: 96419) -SignatureDropTest:test_revert_claimCondition_exceedMaxClaimableSupply() (gas: 537689) -SignatureDropTest:test_revert_claimCondition_notEnoughMintedTokens() (gas: 205284) -SignatureDropTest:test_revert_delayedReveal_alreadyRevealed() (gas: 194412) -SignatureDropTest:test_revert_grant_role_to_account_with_role() (gas: 92760) -SignatureDropTest:test_revert_lazyMint_MINTER_ROLE() (gas: 214770) -SignatureDropTest:test_revert_lazyMint_URIForNonLazyMintedToken() (gas: 130214) -SignatureDropTest:test_revert_mintWithSignature_notEnoughMintedTokens() (gas: 144004) -SignatureDropTest:test_revert_mintWithSignature_notSentAmountRequired() (gas: 189152) -SignatureDropTest:test_revert_mintWithSignature_unapprovedSigner() (gas: 273157) -SignatureDropTest:test_revert_mintWithSignature_zeroQuantity() (gas: 158539) -SignatureDropTest:test_revert_nonHolder_renounceRole() (gas: 73667) -SignatureDropTest:test_revert_reveal_MINTER_ROLE() (gas: 242469) -SignatureDropTest:test_revert_reveal_revealingNonExistentBatch() (gas: 313502) -SignatureDropTest:test_revert_revokeRoleForNonHolder() (gas: 80445) -SignatureDropTest:test_state_claimCondition_resetEligibility() (gas: 418418) -SignatureDropTest:test_state_getRoleMember_transferRole() (gas: 514945) -SignatureDropTest:test_state_grant_transferRole() (gas: 97957) -SignatureDropTest:test_state_lazyMint_noEncryptedURI() (gas: 1176282) -SignatureDropTest:test_state_lazyMint_withEncryptedURI() (gas: 898851) -SignatureDropTest:test_state_mintWithSignature() (gas: 440798) -SignatureDropTest:test_state_mintWithSignature_UpdateRoyaltyAndSaleInfo() (gas: 610488) -SignatureDropTest:test_state_reveal() (gas: 1793797) -StakingExtensionTest:test_revert_claimRewards_noRewards() (gas: 455701) -StakingExtensionTest:test_revert_setRewardsPerUnitTime_notAuthorized() (gas: 10020) -StakingExtensionTest:test_revert_setTimeUnit_notAuthorized() (gas: 10504) -StakingExtensionTest:test_revert_stake_notStaker() (gas: 97844) -StakingExtensionTest:test_revert_stake_stakingZeroTokens() (gas: 16710) -StakingExtensionTest:test_revert_withdraw_notStaker() (gas: 345708) -StakingExtensionTest:test_revert_withdraw_withdrawingMoreThanStaked() (gas: 241338) -StakingExtensionTest:test_revert_withdraw_withdrawingZeroTokens() (gas: 16838) -StakingExtensionTest:test_state_claimRewards() (gas: 492106) -StakingExtensionTest:test_state_setRewardsPerUnitTime() (gas: 664782) -StakingExtensionTest:test_state_setTimeUnit() (gas: 666403) -StakingExtensionTest:test_state_stake() (gas: 771073) -StakingExtensionTest:test_state_withdraw() (gas: 481871) -TWFactoryTest:testNoNonAdmin(address,address) (runs: 256, μ: 14608, ~: 14608) -TWFactoryTest:test_addImplementation() (gas: 96619) -TWFactoryTest:test_addImplementation_directV2() (gas: 285166) -TWFactoryTest:test_addImplementation_emit_ImplementationAdded() (gas: 91616) -TWFactoryTest:test_addImplementation_newImpl() (gas: 339924) -TWFactoryTest:test_addImplementation_revert_invalidCaller() (gas: 16714) -TWFactoryTest:test_approveImplementation() (gas: 50687) -TWFactoryTest:test_approveImplementation_emit_ImplementationApproved() (gas: 41357) -TWFactoryTest:test_approveImplementation_revert_invalidCaller() (gas: 15984) -TWFactoryTest:test_deployProxy() (gas: 237023) -TWFactoryTest:test_deployProxyByImplementation(bytes32) (runs: 256, μ: 190991, ~: 190991) -TWFactoryTest:test_deployProxyByImplementation_revert_invalidImpl() (gas: 16131) -TWFactoryTest:test_deployProxyDeterministic(bytes32) (runs: 256, μ: 238227, ~: 238227) -TWFactoryTest:test_deployProxyDeterministic_revert_invalidImpl(bytes32) (runs: 256, μ: 23849, ~: 23849) -TWFactoryTest:test_deployProxy_revert_invalidImpl() (gas: 29694) -TWFactoryTest:test_deployProxy_sameBlock() (gas: 355169) -TWFactoryTest:test_initialState() (gas: 11145) -TWMultichainRegistryTest:test_addFromFactory() (gas: 138669225) -TWMultichainRegistryTest:test_addFromSelf() (gas: 138866643) -TWMultichainRegistryTest:test_add_emit_Added() (gas: 172695) -TWMultichainRegistryTest:test_interfaceId() (gas: 3005) -TWMultichainRegistryTest:test_removeFromFactory() (gas: 130636550) -TWMultichainRegistryTest:test_removeFromSelf() (gas: 129359757) -TWMultichainRegistryTest:test_remove_emit_Deleted() (gas: 121444494) -TWMultichainRegistryTest:test_remove_revert_incorrectChainId() (gas: 121500707) -TWMultichainRegistryTest:test_remove_revert_invalidCaller() (gas: 121498120) -TWMultichainRegistryTest:test_remove_revert_noModulesToRemove() (gas: 125863594) -TWProxyBenchmark:testBenchmark_deployDrop1155() (gas: 719803) -TWProxyBenchmark:testBenchmark_deployDrop721() (gas: 719370) -TWProxyBenchmark:testBenchmark_deployToken1155() (gas: 822657) -TWProxyBenchmark:testBenchmark_deployToken721() (gas: 818275) -TWRegistryTest:test_addFromFactory() (gas: 143795) -TWRegistryTest:test_addFromSelf() (gas: 89032) -TWRegistryTest:test_add_emit_Added() (gas: 88317) -TWRegistryTest:test_removeFromFactory() (gas: 70711) -TWRegistryTest:test_removeFromSelf() (gas: 72572) -TWRegistryTest:test_remove_emit_Deleted() (gas: 72635) -TWRegistryTest:test_remove_revert_invalidCaller() (gas: 91275) -TWRegistryTest:test_remove_revert_noModulesToRemove() (gas: 101049) -TWRouterTest:test_revert_addExtension_extensionAlreadyExists() (gas: 1075669) -TWRouterTest:test_revert_addExtension_extensionDNE() (gas: 659649) -TWRouterTest:test_revert_removeExtension_extensionDNE() (gas: 16875) -TWRouterTest:test_revert_updateExtension_extensionDNE_inRegistry() (gas: 1125292) -TWRouterTest:test_revert_updateExtension_extensionDNE_inRouter() (gas: 671610) -TWRouterTest:test_revert_updateExtension_reAddingExtension() (gas: 1075784) -TWRouterTest:test_state_addExtension() (gas: 1081653) -TWRouterTest:test_state_initialState() (gas: 539306) -TWRouterTest:test_state_removeExtension() (gas: 868082) -TWRouterTest:test_state_updateExtension() (gas: 1334464) -TieredDropBechmarkTest:test_banchmark_getTokensInTier() (gas: 57274027) -TieredDropBechmarkTest:test_banchmark_getTokensInTier_hundred() (gas: 579481) -TieredDropBechmarkTest:test_banchmark_getTokensInTier_ten() (gas: 73309) -TieredDropTest:test_revert_claimWithSignature_insufficientTokensInTiers() (gas: 1153588) -TieredDropTest:test_revert_claimWithSignature_invalidEncoding() (gas: 915117) -TieredDropTest:test_revert_claimWithSignature_mintingZeroQuantity() (gas: 1094104) -TieredDropTest:test_revert_claimWithSignature_notEnoughLazyMintedTokens() (gas: 1116792) -TieredDropTest:test_revert_lazyMintWithTier_mintingZeroAmount() (gas: 38678) -TieredDropTest:test_revert_lazyMintWithTier_notMinterRole() (gas: 57929) -TieredDropTest:test_state_claimWithSignature() (gas: 1827983) -TieredDropTest:test_state_claimWithSignature_IssueH1() (gas: 2005682) -TieredDropTest:test_state_claimWithSignature_IssueH1_2() (gas: 2501031) -TieredDropTest:test_state_getTierForToken() (gas: 1642825) -TieredDropTest:test_state_getTokensInTier() (gas: 1523824) -TieredDropTest:test_state_getTokensInTierLen() (gas: 2376848) -TieredDropTest:test_state_revealWithScrambleOffset() (gas: 2741023) -TokenERC1155Test:test_event_defaultRoyalty() (gas: 32193) -TokenERC1155Test:test_event_mintTo() (gas: 126570) -TokenERC1155Test:test_event_mintWithSignature() (gas: 250659) -TokenERC1155Test:test_event_platformFeeInfo() (gas: 31929) -TokenERC1155Test:test_event_royaltyForToken() (gas: 66855) -TokenERC1155Test:test_event_setOwner() (gas: 107352) -TokenERC1155Test:test_event_setPrimarySaleRecipient() (gas: 26264) -TokenERC1155Test:test_revert_PlatformFeeGreaterThanPrice() (gas: 358228) -TokenERC1155Test:test_revert_burn_NotOwnerNorApproved() (gas: 132061) -TokenERC1155Test:test_revert_mintTo_NotAuthorized() (gas: 78757) -TokenERC1155Test:test_revert_mintWithSignature_InvalidSignature() (gas: 86252) -TokenERC1155Test:test_revert_mintWithSignature_InvalidTokenId() (gas: 109933) -TokenERC1155Test:test_revert_mintWithSignature_MsgValueNotZero() (gas: 307277) -TokenERC1155Test:test_revert_mintWithSignature_MustSendTotalPrice() (gas: 298046) -TokenERC1155Test:test_revert_mintWithSignature_RecipientUndefined() (gas: 84569) -TokenERC1155Test:test_revert_mintWithSignature_RequestExpired() (gas: 80407) -TokenERC1155Test:test_revert_mintWithSignature_ZeroQuantity() (gas: 84516) -TokenERC1155Test:test_revert_setContractURI() (gas: 76641) -TokenERC1155Test:test_revert_setDefaultRoyaltyInfo_ExceedsRoyaltyBps() (gas: 19414) -TokenERC1155Test:test_revert_setDefaultRoyaltyInfo_NotAuthorized() (gas: 77043) -TokenERC1155Test:test_revert_setOwner_NotModuleAdmin() (gas: 21177) -TokenERC1155Test:test_revert_setPlatformFeeInfo_ExceedsMaxBps() (gas: 19118) -TokenERC1155Test:test_revert_setPlatformFeeInfo_NotAuthorized() (gas: 142873) -TokenERC1155Test:test_revert_setPrimarySaleRecipient_NotAuthorized() (gas: 76817) -TokenERC1155Test:test_revert_setRoyaltyInfoForToken_ExceedsRoyaltyBps() (gas: 19030) -TokenERC1155Test:test_revert_setRoyaltyInfo_NotAuthorized() (gas: 76892) -TokenERC1155Test:test_state_PlatformFee_Flat_ERC20() (gas: 461017) -TokenERC1155Test:test_state_PlatformFee_NativeToken() (gas: 454305) -TokenERC1155Test:test_state_burn_TokenOperator() (gas: 136292) -TokenERC1155Test:test_state_burn_TokenOwner() (gas: 109536) -TokenERC1155Test:test_state_mintTo() (gas: 132179) -TokenERC1155Test:test_state_mintWithSignature_ExistingTokenId() (gas: 341150) -TokenERC1155Test:test_state_mintWithSignature_NewTokenId() (gas: 250066) -TokenERC1155Test:test_state_mintWithSignature_NonZeroPrice_ERC20() (gas: 408002) -TokenERC1155Test:test_state_mintWithSignature_NonZeroPrice_NativeToken() (gas: 400764) -TokenERC1155Test:test_state_setContractURI() (gas: 29841) -TokenERC1155Test:test_state_setDefaultRoyaltyInfo() (gas: 38448) -TokenERC1155Test:test_state_setFlatPlatformFee() (gas: 49065) -TokenERC1155Test:test_state_setOwner() (gas: 107527) -TokenERC1155Test:test_state_setPlatformFeeInfo() (gas: 31893) -TokenERC1155Test:test_state_setPlatformFeeType() (gas: 74554) -TokenERC1155Test:test_state_setPrimarySaleRecipient() (gas: 25909) -TokenERC1155Test:test_state_setRoyaltyInfoForToken() (gas: 66741) -TokenERC20Test:test_event_mintTo() (gas: 122728) -TokenERC20Test:test_event_mintWithSignature() (gas: 188445) -TokenERC20Test:test_event_platformFeeInfo() (gas: 31788) -TokenERC20Test:test_event_setPrimarySaleRecipient() (gas: 26551) -TokenERC20Test:test_revert_mintTo_NotAuthorized() (gas: 18999) -TokenERC20Test:test_revert_mintWithSignature_InvalidSignature() (gas: 75206) -TokenERC20Test:test_revert_mintWithSignature_MsgValueNotZero() (gas: 149757) -TokenERC20Test:test_revert_mintWithSignature_MustSendTotalPrice() (gas: 140692) -TokenERC20Test:test_revert_mintWithSignature_RecipientUndefined() (gas: 75909) -TokenERC20Test:test_revert_mintWithSignature_RequestExpired() (gas: 69033) -TokenERC20Test:test_revert_mintWithSignature_ZeroQuantity() (gas: 73064) -TokenERC20Test:test_revert_setContractURI_NotAuthorized() (gas: 76762) -TokenERC20Test:test_revert_setPlatformFeeInfo_ExceedsMaxBps() (gas: 18942) -TokenERC20Test:test_revert_setPlatformFeeInfo_NotAuthorized() (gas: 76719) -TokenERC20Test:test_revert_setPrimarySaleRecipient_NotAuthorized() (gas: 76872) -TokenERC20Test:test_state_mintTo() (gas: 126549) -TokenERC20Test:test_state_mintWithSignature_NonZeroPrice_ERC20() (gas: 314729) -TokenERC20Test:test_state_mintWithSignature_NonZeroPrice_NativeToken() (gas: 303402) -TokenERC20Test:test_state_mintWithSignature_ZeroPrice() (gas: 186989) -TokenERC20Test:test_state_setContractURI() (gas: 30017) -TokenERC20Test:test_state_setPlatformFeeInfo() (gas: 31700) -TokenERC20Test:test_state_setPrimarySaleRecipient() (gas: 25797) -TokenERC721Test:test_event_defaultRoyalty() (gas: 31664) -TokenERC721Test:test_event_mintTo() (gas: 155355) -TokenERC721Test:test_event_mintWithSignature() (gas: 272179) -TokenERC721Test:test_event_platformFeeInfo() (gas: 31885) -TokenERC721Test:test_event_royaltyForToken() (gas: 66943) -TokenERC721Test:test_event_setOwner() (gas: 107462) -TokenERC721Test:test_event_setPrimarySaleRecipient() (gas: 26242) -TokenERC721Test:test_revert_burn_NotOwnerNorApproved() (gas: 163289) -TokenERC721Test:test_revert_mintTo_NotAuthorized() (gas: 78412) -TokenERC721Test:test_revert_mintTo_emptyURI() (gas: 43104) -TokenERC721Test:test_revert_mintWithSignature_InvalidSignature() (gas: 81879) -TokenERC721Test:test_revert_mintWithSignature_MsgValueNotZero() (gas: 330275) -TokenERC721Test:test_revert_mintWithSignature_MustSendTotalPrice() (gas: 318352) -TokenERC721Test:test_revert_mintWithSignature_RecipientUndefined() (gas: 79768) -TokenERC721Test:test_revert_mintWithSignature_RequestExpired() (gas: 75628) -TokenERC721Test:test_revert_setContractURI() (gas: 76883) -TokenERC721Test:test_revert_setDefaultRoyaltyInfo_ExceedsRoyaltyBps() (gas: 18951) -TokenERC721Test:test_revert_setDefaultRoyaltyInfo_NotAuthorized() (gas: 76690) -TokenERC721Test:test_revert_setOwner_NotModuleAdmin() (gas: 21111) -TokenERC721Test:test_revert_setPlatformFeeInfo_ExceedsMaxBps() (gas: 19074) -TokenERC721Test:test_revert_setPlatformFeeInfo_NotAuthorized() (gas: 76796) -TokenERC721Test:test_revert_setPrimarySaleRecipient_NotAuthorized() (gas: 76530) -TokenERC721Test:test_revert_setRoyaltyInfoForToken_ExceedsRoyaltyBps() (gas: 19118) -TokenERC721Test:test_revert_setRoyaltyInfo_NotAuthorized() (gas: 77068) -TokenERC721Test:test_state_burn_TokenOperator() (gas: 167383) -TokenERC721Test:test_state_burn_TokenOwner() (gas: 140704) -TokenERC721Test:test_state_mintTo() (gas: 165345) -TokenERC721Test:test_state_mintWithSignature_NonZeroPrice_ERC20() (gas: 405328) -TokenERC721Test:test_state_mintWithSignature_NonZeroPrice_NativeToken() (gas: 391092) -TokenERC721Test:test_state_mintWithSignature_ZeroPrice() (gas: 276330) -TokenERC721Test:test_state_setContractURI() (gas: 30083) -TokenERC721Test:test_state_setDefaultRoyaltyInfo() (gas: 38139) -TokenERC721Test:test_state_setOwner() (gas: 107284) -TokenERC721Test:test_state_setPlatformFeeInfo() (gas: 31937) -TokenERC721Test:test_state_setPrimarySaleRecipient() (gas: 25468) -TokenERC721Test:test_state_setRoyaltyInfoForToken() (gas: 66895) -TokenStakeEthRewardTest:test_revert_claimRewards_noRewards() (gas: 281976) -TokenStakeEthRewardTest:test_revert_setRewardRatio_divideByZero() (gas: 31378) -TokenStakeEthRewardTest:test_revert_setRewardRatio_notAuthorized() (gas: 16130) -TokenStakeEthRewardTest:test_revert_setTimeUnit_notAuthorized() (gas: 16177) -TokenStakeEthRewardTest:test_revert_stake_stakingZeroTokens() (gas: 18862) -TokenStakeEthRewardTest:test_revert_withdraw_withdrawingMoreThanStaked() (gas: 369796) -TokenStakeEthRewardTest:test_revert_withdraw_withdrawingZeroTokens() (gas: 21041) -TokenStakeEthRewardTest:test_revert_zeroTimeUnit_adminLockTokens() (gas: 191294) -TokenStakeEthRewardTest:test_state_claimRewards() (gas: 460583) -TokenStakeEthRewardTest:test_state_setRewardRatio() (gas: 484807) -TokenStakeEthRewardTest:test_state_setTimeUnit() (gas: 485410) -TokenStakeEthRewardTest:test_state_stake() (gas: 345036) -TokenStakeEthRewardTest:test_state_withdraw() (gas: 424073) -TokenStakeEthStakeTest:test_revert_claimRewards_noRewards() (gas: 297085) -TokenStakeEthStakeTest:test_revert_setRewardRatio_divideByZero() (gas: 31378) -TokenStakeEthStakeTest:test_revert_setRewardRatio_notAuthorized() (gas: 16130) -TokenStakeEthStakeTest:test_revert_setTimeUnit_notAuthorized() (gas: 16177) -TokenStakeEthStakeTest:test_revert_stake_stakingZeroTokens() (gas: 18862) -TokenStakeEthStakeTest:test_revert_withdraw_withdrawingMoreThanStaked() (gas: 422154) -TokenStakeEthStakeTest:test_revert_withdraw_withdrawingZeroTokens() (gas: 21041) -TokenStakeEthStakeTest:test_revert_zeroTimeUnit_adminLockTokens() (gas: 219914) -TokenStakeEthStakeTest:test_state_claimRewards() (gas: 459785) -TokenStakeEthStakeTest:test_state_setRewardRatio() (gas: 507588) -TokenStakeEthStakeTest:test_state_setTimeUnit() (gas: 508191) -TokenStakeEthStakeTest:test_state_stake() (gas: 369736) -TokenStakeEthStakeTest:test_state_withdraw() (gas: 472233) -TokenStakeTest:test_revert_claimRewards_noRewards() (gas: 268456) -TokenStakeTest:test_revert_setRewardRatio_divideByZero() (gas: 31378) -TokenStakeTest:test_revert_setRewardRatio_notAuthorized() (gas: 16130) -TokenStakeTest:test_revert_setTimeUnit_notAuthorized() (gas: 16177) -TokenStakeTest:test_revert_stake_stakingZeroTokens() (gas: 18862) -TokenStakeTest:test_revert_withdraw_withdrawingMoreThanStaked() (gas: 369796) -TokenStakeTest:test_revert_withdraw_withdrawingZeroTokens() (gas: 21041) -TokenStakeTest:test_revert_zeroTimeUnit_adminLockTokens() (gas: 191294) -TokenStakeTest:test_state_claimRewards() (gas: 432123) -TokenStakeTest:test_state_setRewardRatio() (gas: 484807) -TokenStakeTest:test_state_setTimeUnit() (gas: 485410) -TokenStakeTest:test_state_stake() (gas: 345036) -TokenStakeTest:test_state_withdraw() (gas: 424073) \ No newline at end of file +AABenchmarkPrepare:test_prepareBenchmarkFile() (gas: 2926370) +AccountBenchmarkTest:test_state_accountReceivesNativeTokens() (gas: 11037) +AccountBenchmarkTest:test_state_addAndWithdrawDeposit() (gas: 83332) +AccountBenchmarkTest:test_state_contractMetadata() (gas: 56507) +AccountBenchmarkTest:test_state_createAccount_viaEntrypoint() (gas: 432040) +AccountBenchmarkTest:test_state_createAccount_viaFactory() (gas: 334122) +AccountBenchmarkTest:test_state_executeBatchTransaction() (gas: 39874) +AccountBenchmarkTest:test_state_executeBatchTransaction_viaAccountSigner() (gas: 392782) +AccountBenchmarkTest:test_state_executeBatchTransaction_viaEntrypoint() (gas: 82915) +AccountBenchmarkTest:test_state_executeTransaction() (gas: 35735) +AccountBenchmarkTest:test_state_executeTransaction_viaAccountSigner() (gas: 378632) +AccountBenchmarkTest:test_state_executeTransaction_viaEntrypoint() (gas: 75593) +AccountBenchmarkTest:test_state_receiveERC1155NFT() (gas: 39343) +AccountBenchmarkTest:test_state_receiveERC721NFT() (gas: 78624) +AccountBenchmarkTest:test_state_transferOutsNativeTokens() (gas: 81713) +AirdropERC1155BenchmarkTest:test_benchmark_airdropERC1155_airdrop() (gas: 38083572) +AirdropERC20BenchmarkTest:test_benchmark_airdropERC20_airdrop() (gas: 32068413) +AirdropERC721BenchmarkTest:test_benchmark_airdropERC721_airdrop() (gas: 41912536) +DropERC1155BenchmarkTest:test_benchmark_dropERC1155_claim() (gas: 185032) +DropERC1155BenchmarkTest:test_benchmark_dropERC1155_lazyMint() (gas: 123913) +DropERC1155BenchmarkTest:test_benchmark_dropERC1155_setClaimConditions_five_conditions() (gas: 492121) +DropERC20BenchmarkTest:test_benchmark_dropERC20_claim() (gas: 230505) +DropERC20BenchmarkTest:test_benchmark_dropERC20_setClaimConditions_five_conditions() (gas: 500858) +DropERC721BenchmarkTest:test_benchmark_dropERC721_claim_five_tokens() (gas: 210967) +DropERC721BenchmarkTest:test_benchmark_dropERC721_lazyMint() (gas: 124540) +DropERC721BenchmarkTest:test_benchmark_dropERC721_lazyMint_for_delayed_reveal() (gas: 226149) +DropERC721BenchmarkTest:test_benchmark_dropERC721_reveal() (gas: 13732) +DropERC721BenchmarkTest:test_benchmark_dropERC721_setClaimConditions_five_conditions() (gas: 500494) +EditionStakeBenchmarkTest:test_benchmark_editionStake_claimRewards() (gas: 65081) +EditionStakeBenchmarkTest:test_benchmark_editionStake_stake() (gas: 185144) +EditionStakeBenchmarkTest:test_benchmark_editionStake_withdraw() (gas: 46364) +MultiwrapBenchmarkTest:test_benchmark_multiwrap_unwrap() (gas: 88950) +MultiwrapBenchmarkTest:test_benchmark_multiwrap_wrap() (gas: 473462) +NFTStakeBenchmarkTest:test_benchmark_nftStake_claimRewards() (gas: 68287) +NFTStakeBenchmarkTest:test_benchmark_nftStake_stake_five_tokens() (gas: 539145) +NFTStakeBenchmarkTest:test_benchmark_nftStake_withdraw() (gas: 38076) +PackBenchmarkTest:test_benchmark_pack_addPackContents() (gas: 219188) +PackBenchmarkTest:test_benchmark_pack_createPack() (gas: 1412868) +PackBenchmarkTest:test_benchmark_pack_openPack() (gas: 141860) +PackVRFDirectBenchmarkTest:test_benchmark_packvrf_createPack() (gas: 1379604) +PackVRFDirectBenchmarkTest:test_benchmark_packvrf_openPack() (gas: 119953) +PackVRFDirectBenchmarkTest:test_benchmark_packvrf_openPackAndClaimRewards() (gas: 3621) +SignatureDropBenchmarkTest:test_benchmark_signatureDrop_claim_five_tokens() (gas: 140517) +SignatureDropBenchmarkTest:test_benchmark_signatureDrop_lazyMint() (gas: 124311) +SignatureDropBenchmarkTest:test_benchmark_signatureDrop_lazyMint_for_delayed_reveal() (gas: 225891) +SignatureDropBenchmarkTest:test_benchmark_signatureDrop_reveal() (gas: 10647) +SignatureDropBenchmarkTest:test_benchmark_signatureDrop_setClaimConditions() (gas: 73699) +TokenERC1155BenchmarkTest:test_benchmark_tokenERC1155_burn() (gas: 5728) +TokenERC1155BenchmarkTest:test_benchmark_tokenERC1155_mintTo() (gas: 122286) +TokenERC1155BenchmarkTest:test_benchmark_tokenERC1155_mintWithSignature_pay_with_ERC20() (gas: 267175) +TokenERC1155BenchmarkTest:test_benchmark_tokenERC1155_mintWithSignature_pay_with_native_token() (gas: 296172) +TokenERC20BenchmarkTest:test_benchmark_tokenERC20_mintTo() (gas: 118586) +TokenERC20BenchmarkTest:test_benchmark_tokenERC20_mintWithSignature_pay_with_ERC20() (gas: 183032) +TokenERC20BenchmarkTest:test_benchmark_tokenERC20_mintWithSignature_pay_with_native_token() (gas: 207694) +TokenERC721BenchmarkTest:test_benchmark_tokenERC721_burn() (gas: 8954) +TokenERC721BenchmarkTest:test_benchmark_tokenERC721_mintTo() (gas: 151552) +TokenERC721BenchmarkTest:test_benchmark_tokenERC721_mintWithSignature_pay_with_ERC20() (gas: 262344) +TokenERC721BenchmarkTest:test_benchmark_tokenERC721_mintWithSignature_pay_with_native_token() (gas: 286914) +TokenStakeBenchmarkTest:test_benchmark_tokenStake_claimRewards() (gas: 67554) +TokenStakeBenchmarkTest:test_benchmark_tokenStake_stake() (gas: 177180) +TokenStakeBenchmarkTest:test_benchmark_tokenStake_withdraw() (gas: 47396) \ No newline at end of file diff --git a/.github/composite-actions/setup/action.yml b/.github/composite-actions/setup/action.yml index a2cc33f83..a080f4495 100644 --- a/.github/composite-actions/setup/action.yml +++ b/.github/composite-actions/setup/action.yml @@ -7,7 +7,7 @@ runs: - name: Setup Node.js uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 18 registry-url: "https://registry.npmjs.org" cache: "yarn" diff --git a/.github/workflows/slither.yml b/.github/workflows/slither.yml index e63b903d7..139eef493 100644 --- a/.github/workflows/slither.yml +++ b/.github/workflows/slither.yml @@ -23,6 +23,7 @@ jobs: with: submodules: recursive fetch-depth: 25 + node-version: 18 - name: Setup Project uses: ./.github/composite-actions/setup @@ -37,8 +38,8 @@ jobs: continue-on-error: true id: slither with: - node-version: 16 sarif: results.sarif + slither-args: --foundry-out-directory artifacts_forge - name: Upload SARIF file uses: github/codeql-action/upload-sarif@v2 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 137e4f915..81bdc28dd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,6 +30,7 @@ jobs: with: submodules: recursive fetch-depth: 25 + node-version: 18 - name: Setup Project uses: ./.github/composite-actions/setup @@ -42,6 +43,8 @@ jobs: run: | forge coverage --report lcov lcov --remove lcov.info -o lcov.info 'src/test/**' + lcov --remove lcov.info -o lcov.info 'contracts/external-deps/**' + lcov --remove lcov.info -o lcov.info 'contracts/eip/**' forge test - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 diff --git a/.gitignore b/.gitignore index 9c98d108a..52199fb52 100644 --- a/.gitignore +++ b/.gitignore @@ -38,8 +38,6 @@ deployArgs.json /notes.txt .yalc/ yalc.lock -package-lock.json -yarn.lock # Forge #/lib @@ -58,4 +56,4 @@ corpus/ # IDES .idea -*.DS_Store \ No newline at end of file +*.DS_Store diff --git a/.gitmodules b/.gitmodules index 6e01eb969..7872f5e50 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,11 +16,9 @@ [submodule "lib/ERC721A-Upgradeable"] path = lib/ERC721A-Upgradeable url = https://github.com/chiru-labs/ERC721A-Upgradeable - branch = v3.3.0 [submodule "lib/ERC721A"] path = lib/ERC721A url = https://github.com/chiru-labs/ERC721A - branch = v3.3.0 [submodule "lib/dynamic-contracts"] path = lib/dynamic-contracts url = https://github.com/thirdweb-dev/dynamic-contracts diff --git a/.prettierignore b/.prettierignore index 4971c404b..11631ff81 100644 --- a/.prettierignore +++ b/.prettierignore @@ -9,4 +9,5 @@ node_modules/ typechain/ # files +src/test/smart-wallet/utils/AABenchmarkArtifacts.sol coverage.json diff --git a/.prettierrc b/.prettierrc index 68c86bb0e..09e99ec7c 100644 --- a/.prettierrc +++ b/.prettierrc @@ -11,8 +11,7 @@ { "files": "*.sol", "options": { - "tabWidth": 4, - "explicitTypes": "always" + "tabWidth": 4 } } ] diff --git a/audit-reports/audit-14.pdf b/audit-reports/audit-14.pdf new file mode 100644 index 000000000..35d8df4d3 Binary files /dev/null and b/audit-reports/audit-14.pdf differ diff --git a/audit-reports/audit-15.pdf b/audit-reports/audit-15.pdf new file mode 100644 index 000000000..804e33b49 Binary files /dev/null and b/audit-reports/audit-15.pdf differ diff --git a/contracts/base/ERC1155Base.sol b/contracts/base/ERC1155Base.sol index ec928f87e..d19da2065 100644 --- a/contracts/base/ERC1155Base.sol +++ b/contracts/base/ERC1155Base.sol @@ -11,7 +11,7 @@ import "../extension/Ownable.sol"; import "../extension/Royalty.sol"; import "../extension/BatchMintMetadata.sol"; -import "../lib/TWStrings.sol"; +import "../lib/Strings.sol"; /** * The `ERC1155Base` smart contract implements the ERC1155 NFT standard. @@ -31,7 +31,7 @@ import "../lib/TWStrings.sol"; */ contract ERC1155Base is ERC1155, ContractMetadata, Ownable, Royalty, Multicall, BatchMintMetadata { - using TWStrings for uint256; + using Strings for uint256; /*////////////////////////////////////////////////////////////// State variables @@ -97,12 +97,7 @@ contract ERC1155Base is ERC1155, ContractMetadata, Ownable, Royalty, Multicall, * @param _tokenURI The full metadata URI for the NFTs minted (if a new NFT is being minted). * @param _amount The amount of the same NFT to mint. */ - function mintTo( - address _to, - uint256 _tokenId, - string memory _tokenURI, - uint256 _amount - ) public virtual { + function mintTo(address _to, uint256 _tokenId, string memory _tokenURI, uint256 _amount) public virtual { require(_canMint(), "Not authorized to mint."); uint256 tokenIdToMint; @@ -173,11 +168,7 @@ contract ERC1155Base is ERC1155, ContractMetadata, Ownable, Royalty, Multicall, * @param _tokenId The tokenId of the NFT to burn. * @param _amount The amount of the NFT to burn. */ - function burn( - address _owner, - uint256 _tokenId, - uint256 _amount - ) external virtual { + function burn(address _owner, uint256 _tokenId, uint256 _amount) external virtual { address caller = msg.sender; require(caller == _owner || isApprovedForAll[_owner][caller], "Unapproved caller"); @@ -193,11 +184,7 @@ contract ERC1155Base is ERC1155, ContractMetadata, Ownable, Royalty, Multicall, * @param _tokenIds The tokenIds of the NFTs to burn. * @param _amounts The amounts of the NFTs to burn. */ - function burnBatch( - address _owner, - uint256[] memory _tokenIds, - uint256[] memory _amounts - ) external virtual { + function burnBatch(address _owner, uint256[] memory _tokenIds, uint256[] memory _amounts) external virtual { address caller = msg.sender; require(caller == _owner || isApprovedForAll[_owner][caller], "Unapproved caller"); diff --git a/contracts/base/ERC1155DelayedReveal.sol b/contracts/base/ERC1155DelayedReveal.sol index fbc940a3c..3b06ef207 100644 --- a/contracts/base/ERC1155DelayedReveal.sol +++ b/contracts/base/ERC1155DelayedReveal.sol @@ -25,7 +25,7 @@ import "../extension/DelayedReveal.sol"; */ contract ERC1155DelayedReveal is ERC1155LazyMint, DelayedReveal { - using TWStrings for uint256; + using Strings for uint256; /*////////////////////////////////////////////////////////////// Constructor diff --git a/contracts/base/ERC1155Drop.sol b/contracts/base/ERC1155Drop.sol index 12a8af452..a85a1ba56 100644 --- a/contracts/base/ERC1155Drop.sol +++ b/contracts/base/ERC1155Drop.sol @@ -15,8 +15,8 @@ import "../extension/DropSinglePhase1155.sol"; import "../extension/LazyMint.sol"; import "../extension/DelayedReveal.sol"; -import "../lib/CurrencyTransferLib.sol"; -import "../lib/TWStrings.sol"; +import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol"; +import "../lib/Strings.sol"; /** * BASE: ERC1155Base @@ -53,7 +53,7 @@ contract ERC1155Drop is DelayedReveal, DropSinglePhase1155 { - using TWStrings for uint256; + using Strings for uint256; /*////////////////////////////////////////////////////////////// Mappings @@ -119,11 +119,7 @@ contract ERC1155Drop is * @param _tokenId The tokenId of the NFT to burn. * @param _amount The amount of the NFT to burn. */ - function burn( - address _owner, - uint256 _tokenId, - uint256 _amount - ) external virtual { + function burn(address _owner, uint256 _tokenId, uint256 _amount) external virtual { address caller = msg.sender; require(caller == _owner || isApprovedForAll[_owner][caller], "Unapproved caller"); @@ -139,11 +135,7 @@ contract ERC1155Drop is * @param _tokenIds The tokenIds of the NFTs to burn. * @param _amounts The amounts of the NFTs to burn. */ - function burnBatch( - address _owner, - uint256[] memory _tokenIds, - uint256[] memory _amounts - ) external virtual { + function burnBatch(address _owner, uint256[] memory _tokenIds, uint256[] memory _amounts) external virtual { address caller = msg.sender; require(caller == _owner || isApprovedForAll[_owner][caller], "Unapproved caller"); diff --git a/contracts/base/ERC1155LazyMint.sol b/contracts/base/ERC1155LazyMint.sol index 628c25ec2..93fb0c2e2 100644 --- a/contracts/base/ERC1155LazyMint.sol +++ b/contracts/base/ERC1155LazyMint.sol @@ -13,7 +13,7 @@ import "../extension/BatchMintMetadata.sol"; import "../extension/LazyMint.sol"; import "../extension/interface/IClaimableERC1155.sol"; -import "../lib/TWStrings.sol"; +import "../lib/Strings.sol"; import "../external-deps/openzeppelin/security/ReentrancyGuard.sol"; /** @@ -59,7 +59,7 @@ contract ERC1155LazyMint is IClaimableERC1155, ReentrancyGuard { - using TWStrings for uint256; + using Strings for uint256; /*////////////////////////////////////////////////////////////// Mappings @@ -130,11 +130,7 @@ contract ERC1155LazyMint is * @param _tokenId The tokenId of the lazy minted NFT to mint. * @param _quantity The number of tokens to mint. */ - function claim( - address _receiver, - uint256 _tokenId, - uint256 _quantity - ) public payable virtual nonReentrant { + function claim(address _receiver, uint256 _tokenId, uint256 _quantity) public payable virtual nonReentrant { require(_tokenId < nextTokenIdToMint(), "invalid id"); verifyClaim(msg.sender, _tokenId, _quantity); // Add your claim verification logic by overriding this function. @@ -152,11 +148,7 @@ contract ERC1155LazyMint is * @param _tokenId The tokenId of the lazy minted NFT to mint. * @param _quantity The number of NFTs being claimed. */ - function verifyClaim( - address _claimer, - uint256 _tokenId, - uint256 _quantity - ) public view virtual {} + function verifyClaim(address _claimer, uint256 _tokenId, uint256 _quantity) public view virtual {} /** * @notice Lets an owner or approved operator burn NFTs of the given tokenId. @@ -165,11 +157,7 @@ contract ERC1155LazyMint is * @param _tokenId The tokenId of the NFT to burn. * @param _amount The amount of the NFT to burn. */ - function burn( - address _owner, - uint256 _tokenId, - uint256 _amount - ) external virtual { + function burn(address _owner, uint256 _tokenId, uint256 _amount) external virtual { address caller = msg.sender; require(caller == _owner || isApprovedForAll[_owner][caller], "Unapproved caller"); @@ -185,11 +173,7 @@ contract ERC1155LazyMint is * @param _tokenIds The tokenIds of the NFTs to burn. * @param _amounts The amounts of the NFTs to burn. */ - function burnBatch( - address _owner, - uint256[] memory _tokenIds, - uint256[] memory _amounts - ) external virtual { + function burnBatch(address _owner, uint256[] memory _tokenIds, uint256[] memory _amounts) external virtual { address caller = msg.sender; require(caller == _owner || isApprovedForAll[_owner][caller], "Unapproved caller"); @@ -243,11 +227,7 @@ contract ERC1155LazyMint is * @param _tokenId The tokenId of the lazy minted NFT to mint. * @param _quantity The number of tokens to mint. */ - function _transferTokensOnClaim( - address _receiver, - uint256 _tokenId, - uint256 _quantity - ) internal virtual { + function _transferTokensOnClaim(address _receiver, uint256 _tokenId, uint256 _quantity) internal virtual { _mint(_receiver, _tokenId, _quantity, ""); } diff --git a/contracts/base/ERC1155SignatureMint.sol b/contracts/base/ERC1155SignatureMint.sol index 3d3d684c1..3ccee476f 100644 --- a/contracts/base/ERC1155SignatureMint.sol +++ b/contracts/base/ERC1155SignatureMint.sol @@ -8,7 +8,7 @@ import "./ERC1155Base.sol"; import "../extension/PrimarySale.sol"; import "../extension/SignatureMintERC1155.sol"; -import "../lib/CurrencyTransferLib.sol"; +import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol"; /** * BASE: ERC1155Base @@ -49,13 +49,10 @@ contract ERC1155SignatureMint is ERC1155Base, PrimarySale, SignatureMintERC1155 * @param _req The payload / mint request. * @param _signature The signature produced by an account signing the mint request. */ - function mintWithSignature(MintRequest calldata _req, bytes calldata _signature) - external - payable - virtual - override - returns (address signer) - { + function mintWithSignature( + MintRequest calldata _req, + bytes calldata _signature + ) external payable virtual override returns (address signer) { require(_req.quantity > 0, "Minting zero tokens."); uint256 tokenIdToMint; diff --git a/contracts/base/ERC20Base.sol b/contracts/base/ERC20Base.sol index e4ca039d8..bfbd65592 100644 --- a/contracts/base/ERC20Base.sol +++ b/contracts/base/ERC20Base.sol @@ -31,11 +31,7 @@ contract ERC20Base is ContractMetadata, Multicall, Ownable, ERC20Permit, IMintab Constructor //////////////////////////////////////////////////////////////*/ - constructor( - address _defaultAdmin, - string memory _name, - string memory _symbol - ) ERC20Permit(_name, _symbol) { + constructor(address _defaultAdmin, string memory _name, string memory _symbol) ERC20Permit(_name, _symbol) { _setupOwner(_defaultAdmin); } diff --git a/contracts/base/ERC20Drop.sol b/contracts/base/ERC20Drop.sol index 3cc2f512b..e197e6203 100644 --- a/contracts/base/ERC20Drop.sol +++ b/contracts/base/ERC20Drop.sol @@ -12,7 +12,7 @@ import "../extension/PrimarySale.sol"; import "../extension/DropSinglePhase.sol"; import "../extension/interface/IBurnableERC20.sol"; -import "../lib/CurrencyTransferLib.sol"; +import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol"; /** * BASE: ERC20 @@ -112,12 +112,10 @@ contract ERC20Drop is ContractMetadata, Multicall, Ownable, ERC20Permit, Primary } /// @dev Transfers the tokens being claimed. - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - virtual - override - returns (uint256) - { + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal virtual override returns (uint256) { _mint(_to, _quantityBeingClaimed); return 0; } diff --git a/contracts/base/ERC20DropVote.sol b/contracts/base/ERC20DropVote.sol index e3d1259a5..46020ef79 100644 --- a/contracts/base/ERC20DropVote.sol +++ b/contracts/base/ERC20DropVote.sol @@ -11,7 +11,7 @@ import "../extension/Ownable.sol"; import "../extension/PrimarySale.sol"; import "../extension/DropSinglePhase.sol"; -import "../lib/CurrencyTransferLib.sol"; +import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol"; /** * BASE: ERC20Votes @@ -95,12 +95,10 @@ contract ERC20DropVote is ContractMetadata, Multicall, Ownable, ERC20Votes, Prim } /// @dev Transfers the tokens being claimed. - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - virtual - override - returns (uint256) - { + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal virtual override returns (uint256) { _mint(_to, _quantityBeingClaimed); return 0; } diff --git a/contracts/base/ERC20SignatureMint.sol b/contracts/base/ERC20SignatureMint.sol index d50188c57..a3a2a2f00 100644 --- a/contracts/base/ERC20SignatureMint.sol +++ b/contracts/base/ERC20SignatureMint.sol @@ -8,7 +8,7 @@ import "./ERC20Base.sol"; import "../extension/PrimarySale.sol"; import { SignatureMintERC20 } from "../extension/SignatureMintERC20.sol"; -import "../lib/CurrencyTransferLib.sol"; +import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol"; /** * BASE: ERC20 @@ -47,12 +47,10 @@ contract ERC20SignatureMint is ERC20Base, PrimarySale, SignatureMintERC20 { * @param _req The payload / mint request. * @param _signature The signature produced by an account signing the mint request. */ - function mintWithSignature(MintRequest calldata _req, bytes calldata _signature) - external - payable - virtual - returns (address signer) - { + function mintWithSignature( + MintRequest calldata _req, + bytes calldata _signature + ) external payable virtual returns (address signer) { require(_req.quantity > 0, "Minting zero tokens."); // Verify and process payload. @@ -84,11 +82,7 @@ contract ERC20SignatureMint is ERC20Base, PrimarySale, SignatureMintERC20 { } /// @dev Collects and distributes the primary sale value of tokens being claimed. - function _collectPriceOnClaim( - address _primarySaleRecipient, - address _currency, - uint256 _price - ) internal virtual { + function _collectPriceOnClaim(address _primarySaleRecipient, address _currency, uint256 _price) internal virtual { if (_price == 0) { require(msg.value == 0, "!Value"); return; diff --git a/contracts/base/ERC20SignatureMintVote.sol b/contracts/base/ERC20SignatureMintVote.sol index 67e16793c..80dae73a3 100644 --- a/contracts/base/ERC20SignatureMintVote.sol +++ b/contracts/base/ERC20SignatureMintVote.sol @@ -8,7 +8,7 @@ import "./ERC20Vote.sol"; import "../extension/PrimarySale.sol"; import { SignatureMintERC20 } from "../extension/SignatureMintERC20.sol"; -import "../lib/CurrencyTransferLib.sol"; +import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol"; /** * BASE: ERC20Vote @@ -47,12 +47,10 @@ contract ERC20SignatureMintVote is ERC20Vote, PrimarySale, SignatureMintERC20 { * @param _req The payload / mint request. * @param _signature The signature produced by an account signing the mint request. */ - function mintWithSignature(MintRequest calldata _req, bytes calldata _signature) - external - payable - virtual - returns (address signer) - { + function mintWithSignature( + MintRequest calldata _req, + bytes calldata _signature + ) external payable virtual returns (address signer) { require(_req.quantity > 0, "Minting zero tokens."); // Verify and process payload. @@ -91,11 +89,7 @@ contract ERC20SignatureMintVote is ERC20Vote, PrimarySale, SignatureMintERC20 { } /// @dev Collects and distributes the primary sale value of tokens being claimed. - function _collectPriceOnClaim( - address _primarySaleRecipient, - address _currency, - uint256 _price - ) internal virtual { + function _collectPriceOnClaim(address _primarySaleRecipient, address _currency, uint256 _price) internal virtual { if (_price == 0) { require(msg.value == 0, "!Value"); return; diff --git a/contracts/base/ERC20Vote.sol b/contracts/base/ERC20Vote.sol index 6f09101ec..7a13bb852 100644 --- a/contracts/base/ERC20Vote.sol +++ b/contracts/base/ERC20Vote.sol @@ -31,11 +31,7 @@ contract ERC20Vote is ContractMetadata, Multicall, Ownable, ERC20Votes, IMintabl Constructor //////////////////////////////////////////////////////////////*/ - constructor( - address _defaultAdmin, - string memory _name, - string memory _symbol - ) ERC20Permit(_name, _symbol) { + constructor(address _defaultAdmin, string memory _name, string memory _symbol) ERC20Permit(_name, _symbol) { _setupOwner(_defaultAdmin); } diff --git a/contracts/base/ERC721Base.sol b/contracts/base/ERC721Base.sol index bd6123979..49cc41284 100644 --- a/contracts/base/ERC721Base.sol +++ b/contracts/base/ERC721Base.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; /// @author thirdweb -import { ERC721A } from "../eip/ERC721AVirtualApprove.sol"; +import "../eip/queryable/ERC721AQueryable.sol"; import "../extension/ContractMetadata.sol"; import "../extension/Multicall.sol"; @@ -11,7 +11,7 @@ import "../extension/Ownable.sol"; import "../extension/Royalty.sol"; import "../extension/BatchMintMetadata.sol"; -import "../lib/TWStrings.sol"; +import "../lib/Strings.sol"; /** * The `ERC721Base` smart contract implements the ERC721 NFT standard, along with the ERC721A optimization to the standard. @@ -30,8 +30,8 @@ import "../lib/TWStrings.sol"; * - EIP 2981 compliance for royalty support on NFT marketplaces. */ -contract ERC721Base is ERC721A, ContractMetadata, Multicall, Ownable, Royalty, BatchMintMetadata { - using TWStrings for uint256; +contract ERC721Base is ERC721AQueryable, ContractMetadata, Multicall, Ownable, Royalty, BatchMintMetadata { + using Strings for uint256; /*////////////////////////////////////////////////////////////// Mappings @@ -89,7 +89,7 @@ contract ERC721Base is ERC721A, ContractMetadata, Multicall, Ownable, Royalty, B * * @param _tokenId The tokenId of an NFT. */ - function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) { + function tokenURI(uint256 _tokenId) public view virtual override(ERC721A, IERC721Metadata) returns (string memory) { string memory fullUriForToken = fullURI[_tokenId]; if (bytes(fullUriForToken).length > 0) { return fullUriForToken; @@ -125,12 +125,7 @@ contract ERC721Base is ERC721A, ContractMetadata, Multicall, Ownable, Royalty, B * @param _baseURI The baseURI for the `n` number of NFTs minted. The metadata for each NFT is `baseURI/tokenId` * @param _data Additional data to pass along during the minting of the NFT. */ - function batchMintTo( - address _to, - uint256 _quantity, - string memory _baseURI, - bytes memory _data - ) public virtual { + function batchMintTo(address _to, uint256 _quantity, string memory _baseURI, bytes memory _data) public virtual { require(_canMint(), "Not authorized to mint."); _batchMintMetadata(nextTokenIdToMint(), _quantity, _baseURI); _safeMint(_to, _quantity, _data); @@ -163,12 +158,10 @@ contract ERC721Base is ERC721A, ContractMetadata, Multicall, Ownable, Royalty, B * * @return isApprovedOrOwnerOf Whether the given address is approved to transfer the given NFT. */ - function isApprovedOrOwner(address _operator, uint256 _tokenId) - public - view - virtual - returns (bool isApprovedOrOwnerOf) - { + function isApprovedOrOwner( + address _operator, + uint256 _tokenId + ) public view virtual returns (bool isApprovedOrOwnerOf) { address owner = ownerOf(_tokenId); isApprovedOrOwnerOf = (_operator == owner || isApprovedForAll(owner, _operator) || diff --git a/contracts/base/ERC721DelayedReveal.sol b/contracts/base/ERC721DelayedReveal.sol index 1c17f0051..624ec4ecc 100644 --- a/contracts/base/ERC721DelayedReveal.sol +++ b/contracts/base/ERC721DelayedReveal.sol @@ -26,7 +26,7 @@ import "../extension/DelayedReveal.sol"; */ contract ERC721DelayedReveal is ERC721LazyMint, DelayedReveal { - using TWStrings for uint256; + using Strings for uint256; /*////////////////////////////////////////////////////////////// Constructor diff --git a/contracts/base/ERC721Drop.sol b/contracts/base/ERC721Drop.sol index 60fd84dc2..a4d2e5a88 100644 --- a/contracts/base/ERC721Drop.sol +++ b/contracts/base/ERC721Drop.sol @@ -15,8 +15,8 @@ import "../extension/DropSinglePhase.sol"; import "../extension/LazyMint.sol"; import "../extension/DelayedReveal.sol"; -import "../lib/TWStrings.sol"; -import "../lib/CurrencyTransferLib.sol"; +import "../lib/Strings.sol"; +import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol"; /** * BASE: ERC721A @@ -53,7 +53,7 @@ contract ERC721Drop is DelayedReveal, DropSinglePhase { - using TWStrings for uint256; + using Strings for uint256; /*/////////////////////////////////////////////////////////////// Constructor @@ -254,12 +254,10 @@ contract ERC721Drop is * @param _to The address to which the NFTs are being transferred. * @param _quantityBeingClaimed The quantity of NFTs being claimed. */ - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - virtual - override - returns (uint256 startTokenId) - { + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal virtual override returns (uint256 startTokenId) { startTokenId = _currentIndex; _safeMint(_to, _quantityBeingClaimed); } diff --git a/contracts/base/ERC721LazyMint.sol b/contracts/base/ERC721LazyMint.sol index 4d18a2a88..84c23d61d 100644 --- a/contracts/base/ERC721LazyMint.sol +++ b/contracts/base/ERC721LazyMint.sol @@ -13,7 +13,7 @@ import "../extension/BatchMintMetadata.sol"; import "../extension/LazyMint.sol"; import "../extension/interface/IClaimableERC721.sol"; -import "../lib/TWStrings.sol"; +import "../lib/Strings.sol"; import "../external-deps/openzeppelin/security/ReentrancyGuard.sol"; /** @@ -53,7 +53,7 @@ contract ERC721LazyMint is IClaimableERC721, ReentrancyGuard { - using TWStrings for uint256; + using Strings for uint256; /*////////////////////////////////////////////////////////////// Constructor @@ -184,11 +184,10 @@ contract ERC721LazyMint is * * @return startTokenId The tokenId of the first NFT minted. */ - function _transferTokensOnClaim(address _receiver, uint256 _quantity) - internal - virtual - returns (uint256 startTokenId) - { + function _transferTokensOnClaim( + address _receiver, + uint256 _quantity + ) internal virtual returns (uint256 startTokenId) { startTokenId = _currentIndex; _safeMint(_receiver, _quantity); } diff --git a/contracts/base/ERC721Multiwrap.sol b/contracts/base/ERC721Multiwrap.sol index 62adb6a99..9d6873325 100644 --- a/contracts/base/ERC721Multiwrap.sol +++ b/contracts/base/ERC721Multiwrap.sol @@ -110,13 +110,9 @@ contract ERC721Multiwrap is Multicall, TokenStore, SoulboundERC721A, ERC721A, Co * @dev See ERC165: https://eips.ethereum.org/EIPS/eip-165 * @inheritdoc IERC165 */ - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC1155Receiver, ERC721A, IERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC1155Receiver, ERC721A, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 @@ -201,12 +197,10 @@ contract ERC721Multiwrap is Multicall, TokenStore, SoulboundERC721A, ERC721A, Co * * @return isApprovedOrOwnerOf Whether `_operator` is approved to transfer `_tokenId`. */ - function isApprovedOrOwner(address _operator, uint256 _tokenId) - public - view - virtual - returns (bool isApprovedOrOwnerOf) - { + function isApprovedOrOwner( + address _operator, + uint256 _tokenId + ) public view virtual returns (bool isApprovedOrOwnerOf) { address owner = ownerOf(_tokenId); isApprovedOrOwnerOf = (_operator == owner || isApprovedForAll(owner, _operator) || diff --git a/contracts/base/ERC721SignatureMint.sol b/contracts/base/ERC721SignatureMint.sol index 4ecab0c68..e6ec12e4f 100644 --- a/contracts/base/ERC721SignatureMint.sol +++ b/contracts/base/ERC721SignatureMint.sol @@ -9,7 +9,7 @@ import "../extension/PrimarySale.sol"; import "../extension/PermissionsEnumerable.sol"; import "../extension/SignatureMintERC721.sol"; -import "../lib/CurrencyTransferLib.sol"; +import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol"; /** * BASE: ERC721A @@ -50,13 +50,10 @@ contract ERC721SignatureMint is ERC721Base, PrimarySale, SignatureMintERC721 { * @param _req The payload / mint request. * @param _signature The signature produced by an account signing the mint request. */ - function mintWithSignature(MintRequest calldata _req, bytes calldata _signature) - external - payable - virtual - override - returns (address signer) - { + function mintWithSignature( + MintRequest calldata _req, + bytes calldata _signature + ) external payable virtual override returns (address signer) { require(_req.quantity == 1, "quantiy must be 1"); uint256 tokenIdToMint = nextTokenIdToMint(); diff --git a/contracts/base/Staking1155Base.sol b/contracts/base/Staking1155Base.sol index be9a1979c..e5c8f8773 100644 --- a/contracts/base/Staking1155Base.sol +++ b/contracts/base/Staking1155Base.sol @@ -93,13 +93,7 @@ contract Staking1155Base is ContractMetadata, Multicall, Ownable, Staking1155, E ERC 165 / 721 logic //////////////////////////////////////////////////////////////*/ - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes calldata - ) external view returns (bytes4) { + function onERC1155Received(address, address, uint256, uint256, bytes calldata) external view returns (bytes4) { require(isStaking == 2, "Direct transfer"); return this.onERC1155Received.selector; } diff --git a/contracts/eip/ERC1155.sol b/contracts/eip/ERC1155.sol index 31e6bb93f..5a0d5647e 100644 --- a/contracts/eip/ERC1155.sol +++ b/contracts/eip/ERC1155.sol @@ -47,13 +47,10 @@ contract ERC1155 is IERC1155, IERC1155Metadata { return _uri[tokenId]; } - function balanceOfBatch(address[] memory accounts, uint256[] memory ids) - public - view - virtual - override - returns (uint256[] memory) - { + function balanceOfBatch( + address[] memory accounts, + uint256[] memory ids + ) public view virtual override returns (uint256[] memory) { require(accounts.length == ids.length, "LENGTH_MISMATCH"); uint256[] memory batchBalances = new uint256[](accounts.length); @@ -162,12 +159,7 @@ contract ERC1155 is IERC1155, IERC1155Metadata { _uri[tokenId] = newuri; } - function _mint( - address to, - uint256 id, - uint256 amount, - bytes memory data - ) internal virtual { + function _mint(address to, uint256 id, uint256 amount, bytes memory data) internal virtual { require(to != address(0), "TO_ZERO_ADDR"); address operator = msg.sender; @@ -202,11 +194,7 @@ contract ERC1155 is IERC1155, IERC1155Metadata { _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data); } - function _burn( - address from, - uint256 id, - uint256 amount - ) internal virtual { + function _burn(address from, uint256 id, uint256 amount) internal virtual { require(from != address(0), "FROM_ZERO_ADDR"); address operator = msg.sender; @@ -222,11 +210,7 @@ contract ERC1155 is IERC1155, IERC1155Metadata { emit TransferSingle(operator, from, address(0), id, amount); } - function _burnBatch( - address from, - uint256[] memory ids, - uint256[] memory amounts - ) internal virtual { + function _burnBatch(address from, uint256[] memory ids, uint256[] memory amounts) internal virtual { require(from != address(0), "FROM_ZERO_ADDR"); require(ids.length == amounts.length, "LENGTH_MISMATCH"); diff --git a/contracts/eip/ERC721A.sol b/contracts/eip/ERC721A.sol index 198686d51..9ae9c99f4 100644 --- a/contracts/eip/ERC721A.sol +++ b/contracts/eip/ERC721A.sol @@ -6,9 +6,9 @@ pragma solidity ^0.8.4; import "./interface/IERC721A.sol"; import "../external-deps/openzeppelin/token/ERC721/IERC721Receiver.sol"; -import "../lib/TWAddress.sol"; +import "../lib/Address.sol"; import "../external-deps/openzeppelin/utils/Context.sol"; -import "../lib/TWStrings.sol"; +import "../lib/Strings.sol"; import "./ERC165.sol"; /** @@ -22,8 +22,8 @@ import "./ERC165.sol"; * Assumes that the maximum token id cannot exceed 2^256 - 1 (max value of uint256). */ contract ERC721A is Context, ERC165, IERC721A { - using TWAddress for address; - using TWStrings for uint256; + using Address for address; + using Strings for uint256; // The tokenId of the next token to be minted. uint256 internal _currentIndex; @@ -248,34 +248,21 @@ contract ERC721A is Context, ERC165, IERC721A { /** * @dev See {IERC721-transferFrom}. */ - function transferFrom( - address from, - address to, - uint256 tokenId - ) public virtual override { + function transferFrom(address from, address to, uint256 tokenId) public virtual override { _transfer(from, to, tokenId); } /** * @dev See {IERC721-safeTransferFrom}. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) public virtual override { + function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory _data - ) public virtual override { + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override { _transfer(from, to, tokenId); if (to.isContract()) if (!_checkContractOnERC721Received(from, to, tokenId, _data)) { @@ -312,11 +299,7 @@ contract ERC721A is Context, ERC165, IERC721A { * * Emits a {Transfer} event. */ - function _safeMint( - address to, - uint256 quantity, - bytes memory _data - ) internal { + function _safeMint(address to, uint256 quantity, bytes memory _data) internal { uint256 startTokenId = _currentIndex; if (to == address(0)) revert MintToZeroAddress(); if (quantity == 0) revert MintZeroQuantity(); @@ -404,11 +387,7 @@ contract ERC721A is Context, ERC165, IERC721A { * * Emits a {Transfer} event. */ - function _transfer( - address from, - address to, - uint256 tokenId - ) private { + function _transfer(address from, address to, uint256 tokenId) private { TokenOwnership memory prevOwnership = _ownershipOf(tokenId); if (prevOwnership.addr != from) revert TransferFromIncorrectOwner(); @@ -531,11 +510,7 @@ contract ERC721A is Context, ERC165, IERC721A { * * Emits a {Approval} event. */ - function _approve( - address to, - uint256 tokenId, - address owner - ) private { + function _approve(address to, uint256 tokenId, address owner) private { _tokenApprovals[tokenId] = to; emit Approval(owner, to, tokenId); } @@ -583,12 +558,7 @@ contract ERC721A is Context, ERC165, IERC721A { * - When `to` is zero, `tokenId` will be burned by `from`. * - `from` and `to` are never both zero. */ - function _beforeTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity - ) internal virtual {} + function _beforeTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {} /** * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes @@ -606,10 +576,5 @@ contract ERC721A is Context, ERC165, IERC721A { * - When `to` is zero, `tokenId` has been burned by `from`. * - `from` and `to` are never both zero. */ - function _afterTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity - ) internal virtual {} + function _afterTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {} } diff --git a/contracts/eip/ERC721AUpgradeable.sol b/contracts/eip/ERC721AUpgradeable.sol index b8ae2f0fd..50fdef270 100644 --- a/contracts/eip/ERC721AUpgradeable.sol +++ b/contracts/eip/ERC721AUpgradeable.sol @@ -8,9 +8,9 @@ pragma solidity ^0.8.4; import "./interface/IERC721A.sol"; import "./interface/IERC721Receiver.sol"; -import "../lib/TWAddress.sol"; +import "../lib/Address.sol"; import "../external-deps/openzeppelin/utils/Context.sol"; -import "../lib/TWStrings.sol"; +import "../lib/Strings.sol"; import "./ERC165.sol"; import "../extension/upgradeable/Initializable.sol"; @@ -59,8 +59,8 @@ library ERC721AStorage { * Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256). */ contract ERC721AUpgradeable is Initializable, Context, ERC165, IERC721A { - using TWAddress for address; - using TWStrings for uint256; + using Address for address; + using Strings for uint256; function __ERC721A_init(string memory name_, string memory symbol_) internal onlyInitializing { __ERC721A_init_unchained(name_, symbol_); @@ -283,34 +283,21 @@ contract ERC721AUpgradeable is Initializable, Context, ERC165, IERC721A { /** * @dev See {IERC721-transferFrom}. */ - function transferFrom( - address from, - address to, - uint256 tokenId - ) public virtual override { + function transferFrom(address from, address to, uint256 tokenId) public virtual override { _transfer(from, to, tokenId); } /** * @dev See {IERC721-safeTransferFrom}. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) public virtual override { + function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory _data - ) public virtual override { + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override { _transfer(from, to, tokenId); if (to.isContract()) if (!_checkContractOnERC721Received(from, to, tokenId, _data)) { @@ -348,11 +335,7 @@ contract ERC721AUpgradeable is Initializable, Context, ERC165, IERC721A { * * Emits a {Transfer} event. */ - function _safeMint( - address to, - uint256 quantity, - bytes memory _data - ) internal { + function _safeMint(address to, uint256 quantity, bytes memory _data) internal { ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage(); uint256 startTokenId = data._currentIndex; @@ -444,11 +427,7 @@ contract ERC721AUpgradeable is Initializable, Context, ERC165, IERC721A { * * Emits a {Transfer} event. */ - function _transfer( - address from, - address to, - uint256 tokenId - ) private { + function _transfer(address from, address to, uint256 tokenId) private { ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage(); TokenOwnership memory prevOwnership = _ownershipOf(tokenId); @@ -575,11 +554,7 @@ contract ERC721AUpgradeable is Initializable, Context, ERC165, IERC721A { * * Emits a {Approval} event. */ - function _approve( - address to, - uint256 tokenId, - address owner - ) private { + function _approve(address to, uint256 tokenId, address owner) private { ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage(); data._tokenApprovals[tokenId] = to; emit Approval(owner, to, tokenId); @@ -628,12 +603,7 @@ contract ERC721AUpgradeable is Initializable, Context, ERC165, IERC721A { * - When `to` is zero, `tokenId` will be burned by `from`. * - `from` and `to` are never both zero. */ - function _beforeTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity - ) internal virtual {} + function _beforeTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {} /** * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes @@ -651,10 +621,5 @@ contract ERC721AUpgradeable is Initializable, Context, ERC165, IERC721A { * - When `to` is zero, `tokenId` has been burned by `from`. * - `from` and `to` are never both zero. */ - function _afterTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity - ) internal virtual {} + function _afterTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {} } diff --git a/contracts/eip/ERC721AVirtualApprove.sol b/contracts/eip/ERC721AVirtualApprove.sol index 9da6d1b83..45c5232ce 100644 --- a/contracts/eip/ERC721AVirtualApprove.sol +++ b/contracts/eip/ERC721AVirtualApprove.sol @@ -8,9 +8,9 @@ pragma solidity ^0.8.4; import "./interface/IERC721A.sol"; import "./interface/IERC721Receiver.sol"; -import "../lib/TWAddress.sol"; +import "../lib/Address.sol"; import "../external-deps/openzeppelin/utils/Context.sol"; -import "../lib/TWStrings.sol"; +import "../lib/Strings.sol"; import "./ERC165.sol"; /** @@ -24,8 +24,8 @@ import "./ERC165.sol"; * Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256). */ contract ERC721A is Context, ERC165, IERC721A { - using TWAddress for address; - using TWStrings for uint256; + using Address for address; + using Strings for uint256; // The tokenId of the next token to be minted. uint256 internal _currentIndex; @@ -250,34 +250,21 @@ contract ERC721A is Context, ERC165, IERC721A { /** * @dev See {IERC721-transferFrom}. */ - function transferFrom( - address from, - address to, - uint256 tokenId - ) public virtual override { + function transferFrom(address from, address to, uint256 tokenId) public virtual override { _transfer(from, to, tokenId); } /** * @dev See {IERC721-safeTransferFrom}. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) public virtual override { + function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory _data - ) public virtual override { + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override { _transfer(from, to, tokenId); if (to.isContract()) if (!_checkContractOnERC721Received(from, to, tokenId, _data)) { @@ -314,11 +301,7 @@ contract ERC721A is Context, ERC165, IERC721A { * * Emits a {Transfer} event. */ - function _safeMint( - address to, - uint256 quantity, - bytes memory _data - ) internal { + function _safeMint(address to, uint256 quantity, bytes memory _data) internal { uint256 startTokenId = _currentIndex; if (to == address(0)) revert MintToZeroAddress(); if (quantity == 0) revert MintZeroQuantity(); @@ -406,11 +389,7 @@ contract ERC721A is Context, ERC165, IERC721A { * * Emits a {Transfer} event. */ - function _transfer( - address from, - address to, - uint256 tokenId - ) private { + function _transfer(address from, address to, uint256 tokenId) private { TokenOwnership memory prevOwnership = _ownershipOf(tokenId); if (prevOwnership.addr != from) revert TransferFromIncorrectOwner(); @@ -533,11 +512,7 @@ contract ERC721A is Context, ERC165, IERC721A { * * Emits a {Approval} event. */ - function _approve( - address to, - uint256 tokenId, - address owner - ) private { + function _approve(address to, uint256 tokenId, address owner) private { _tokenApprovals[tokenId] = to; emit Approval(owner, to, tokenId); } @@ -585,12 +560,7 @@ contract ERC721A is Context, ERC165, IERC721A { * - When `to` is zero, `tokenId` will be burned by `from`. * - `from` and `to` are never both zero. */ - function _beforeTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity - ) internal virtual {} + function _beforeTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {} /** * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes @@ -608,10 +578,5 @@ contract ERC721A is Context, ERC165, IERC721A { * - When `to` is zero, `tokenId` has been burned by `from`. * - `from` and `to` are never both zero. */ - function _afterTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity - ) internal virtual {} + function _afterTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {} } diff --git a/contracts/eip/ERC721AVirtualApproveUpgradeable.sol b/contracts/eip/ERC721AVirtualApproveUpgradeable.sol index 31c3b57dd..55d233bbc 100644 --- a/contracts/eip/ERC721AVirtualApproveUpgradeable.sol +++ b/contracts/eip/ERC721AVirtualApproveUpgradeable.sol @@ -95,13 +95,9 @@ contract ERC721AUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradea /** * @dev See {IERC165-supportsInterface}. */ - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC165Upgradeable, IERC165Upgradeable) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) { return interfaceId == type(IERC721Upgradeable).interfaceId || interfaceId == type(IERC721MetadataUpgradeable).interfaceId || @@ -261,34 +257,21 @@ contract ERC721AUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradea /** * @dev See {IERC721-transferFrom}. */ - function transferFrom( - address from, - address to, - uint256 tokenId - ) public virtual override { + function transferFrom(address from, address to, uint256 tokenId) public virtual override { _transfer(from, to, tokenId); } /** * @dev See {IERC721-safeTransferFrom}. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) public virtual override { + function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes memory _data - ) public virtual override { + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override { _transfer(from, to, tokenId); if (to.isContract()) if (!_checkContractOnERC721Received(from, to, tokenId, _data)) { @@ -325,11 +308,7 @@ contract ERC721AUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradea * * Emits a {Transfer} event. */ - function _safeMint( - address to, - uint256 quantity, - bytes memory _data - ) internal { + function _safeMint(address to, uint256 quantity, bytes memory _data) internal { uint256 startTokenId = _currentIndex; if (to == address(0)) revert MintToZeroAddress(); if (quantity == 0) revert MintZeroQuantity(); @@ -417,11 +396,7 @@ contract ERC721AUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradea * * Emits a {Transfer} event. */ - function _transfer( - address from, - address to, - uint256 tokenId - ) private { + function _transfer(address from, address to, uint256 tokenId) private { TokenOwnership memory prevOwnership = _ownershipOf(tokenId); if (prevOwnership.addr != from) revert TransferFromIncorrectOwner(); @@ -544,11 +519,7 @@ contract ERC721AUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradea * * Emits a {Approval} event. */ - function _approve( - address to, - uint256 tokenId, - address owner - ) private { + function _approve(address to, uint256 tokenId, address owner) private { _tokenApprovals[tokenId] = to; emit Approval(owner, to, tokenId); } @@ -598,12 +569,7 @@ contract ERC721AUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradea * - When `to` is zero, `tokenId` will be burned by `from`. * - `from` and `to` are never both zero. */ - function _beforeTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity - ) internal virtual {} + function _beforeTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {} /** * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes @@ -621,12 +587,7 @@ contract ERC721AUpgradeable is Initializable, ContextUpgradeable, ERC165Upgradea * - When `to` is zero, `tokenId` has been burned by `from`. * - `from` and `to` are never both zero. */ - function _afterTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity - ) internal virtual {} + function _afterTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {} /** * @dev This empty reserved space is put in place to allow future versions to add new diff --git a/contracts/eip/interface/IERC1155.sol b/contracts/eip/interface/IERC1155.sol index cfe341157..1f2d9e919 100644 --- a/contracts/eip/interface/IERC1155.sol +++ b/contracts/eip/interface/IERC1155.sol @@ -69,13 +69,7 @@ interface IERC1155 { @param _value Transfer amount @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to` */ - function safeTransferFrom( - address _from, - address _to, - uint256 _id, - uint256 _value, - bytes calldata _data - ) external; + function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external; /** @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call). @@ -115,10 +109,10 @@ interface IERC1155 { @param _ids ID of the Tokens @return The _owner's balance of the Token types requested (i.e. balance for each (owner, id) pair) */ - function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) - external - view - returns (uint256[] memory); + function balanceOfBatch( + address[] calldata _owners, + uint256[] calldata _ids + ) external view returns (uint256[] memory); /** @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens. diff --git a/contracts/eip/interface/IERC20.sol b/contracts/eip/interface/IERC20.sol index 6350dfc2c..246af8424 100644 --- a/contracts/eip/interface/IERC20.sol +++ b/contracts/eip/interface/IERC20.sol @@ -16,11 +16,7 @@ interface IERC20 { function approve(address spender, uint256 value) external returns (bool); - function transferFrom( - address from, - address to, - uint256 value - ) external returns (bool); + function transferFrom(address from, address to, uint256 value) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); diff --git a/contracts/eip/interface/IERC2981.sol b/contracts/eip/interface/IERC2981.sol index 79276a538..e25265b33 100644 --- a/contracts/eip/interface/IERC2981.sol +++ b/contracts/eip/interface/IERC2981.sol @@ -16,8 +16,8 @@ interface IERC2981 is IERC165 { * @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of * exchange. The royalty amount is denominated and should be payed in that same unit of exchange. */ - function royaltyInfo(uint256 tokenId, uint256 salePrice) - external - view - returns (address receiver, uint256 royaltyAmount); + function royaltyInfo( + uint256 tokenId, + uint256 salePrice + ) external view returns (address receiver, uint256 royaltyAmount); } diff --git a/contracts/eip/interface/IERC721.sol b/contracts/eip/interface/IERC721.sol index 62fca471e..6bc14e70d 100644 --- a/contracts/eip/interface/IERC721.sol +++ b/contracts/eip/interface/IERC721.sol @@ -50,11 +50,7 @@ interface IERC721 { * * Emits a {Transfer} event. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) external; + function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. @@ -70,11 +66,7 @@ interface IERC721 { * * Emits a {Transfer} event. */ - function transferFrom( - address from, - address to, - uint256 tokenId - ) external; + function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. @@ -132,10 +124,5 @@ interface IERC721 { * * Emits a {Transfer} event. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes calldata data - ) external; + function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; } diff --git a/contracts/eip/queryable/ERC721AQueryable.sol b/contracts/eip/queryable/ERC721AQueryable.sol new file mode 100644 index 000000000..a5026b487 --- /dev/null +++ b/contracts/eip/queryable/ERC721AQueryable.sol @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: MIT +// ERC721A Contracts v3.3.0 +// Creator: Chiru Labs + +pragma solidity ^0.8.4; + +import "./IERC721AQueryable.sol"; +import "../ERC721AVirtualApprove.sol"; + +/** + * @title ERC721A Queryable + * @dev ERC721A subclass with convenience query functions. + */ +abstract contract ERC721AQueryable is ERC721A, IERC721AQueryable { + /** + * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting. + * + * If the `tokenId` is out of bounds: + * - `addr` = `address(0)` + * - `startTimestamp` = `0` + * - `burned` = `false` + * + * If the `tokenId` is burned: + * - `addr` = `
` + * - `startTimestamp` = `` + * - `burned = `true` + * + * Otherwise: + * - `addr` = `
` + * - `startTimestamp` = `` + * - `burned = `false` + */ + function explicitOwnershipOf(uint256 tokenId) public view override returns (TokenOwnership memory) { + TokenOwnership memory ownership; + if (tokenId < _startTokenId() || tokenId >= _currentIndex) { + return ownership; + } + ownership = _ownerships[tokenId]; + if (ownership.burned) { + return ownership; + } + return _ownershipOf(tokenId); + } + + /** + * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order. + * See {ERC721AQueryable-explicitOwnershipOf} + */ + function explicitOwnershipsOf(uint256[] memory tokenIds) external view override returns (TokenOwnership[] memory) { + unchecked { + uint256 tokenIdsLength = tokenIds.length; + TokenOwnership[] memory ownerships = new TokenOwnership[](tokenIdsLength); + for (uint256 i; i != tokenIdsLength; ++i) { + ownerships[i] = explicitOwnershipOf(tokenIds[i]); + } + return ownerships; + } + } + + /** + * @dev Returns an array of token IDs owned by `owner`, + * in the range [`start`, `stop`) + * (i.e. `start <= tokenId < stop`). + * + * This function allows for tokens to be queried if the collection + * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}. + * + * Requirements: + * + * - `start` < `stop` + */ + /* solhint-disable*/ + function tokensOfOwnerIn( + address owner, + uint256 start, + uint256 stop + ) external view override returns (uint256[] memory) { + unchecked { + if (start >= stop) revert InvalidQueryRange(); + uint256 tokenIdsIdx; + uint256 stopLimit = _currentIndex; + // Set `start = max(start, _startTokenId())`. + if (start < _startTokenId()) { + start = _startTokenId(); + } + // Set `stop = min(stop, _currentIndex)`. + if (stop > stopLimit) { + stop = stopLimit; + } + uint256 tokenIdsMaxLength = balanceOf(owner); + // Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`, + // to cater for cases where `balanceOf(owner)` is too big. + if (start < stop) { + uint256 rangeLength = stop - start; + if (rangeLength < tokenIdsMaxLength) { + tokenIdsMaxLength = rangeLength; + } + } else { + tokenIdsMaxLength = 0; + } + uint256[] memory tokenIds = new uint256[](tokenIdsMaxLength); + if (tokenIdsMaxLength == 0) { + return tokenIds; + } + // We need to call `explicitOwnershipOf(start)`, + // because the slot at `start` may not be initialized. + TokenOwnership memory ownership = explicitOwnershipOf(start); + address currOwnershipAddr; + // If the starting slot exists (i.e. not burned), initialize `currOwnershipAddr`. + // `ownership.address` will not be zero, as `start` is clamped to the valid token ID range. + if (!ownership.burned) { + currOwnershipAddr = ownership.addr; + } + for (uint256 i = start; i != stop && tokenIdsIdx != tokenIdsMaxLength; ++i) { + ownership = _ownerships[i]; + if (ownership.burned) { + continue; + } + if (ownership.addr != address(0)) { + currOwnershipAddr = ownership.addr; + } + if (currOwnershipAddr == owner) { + tokenIds[tokenIdsIdx++] = i; + } + } + // Downsize the array to fit. + assembly { + mstore(tokenIds, tokenIdsIdx) + } + return tokenIds; + } + } + + /* solhint-enable */ + + /** + * @dev Returns an array of token IDs owned by `owner`. + * + * This function scans the ownership mapping and is O(totalSupply) in complexity. + * It is meant to be called off-chain. + * + * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into + * multiple smaller scans if the collection is large enough to cause + * an out-of-gas error (10K pfp collections should be fine). + */ + function tokensOfOwner(address owner) external view override returns (uint256[] memory) { + unchecked { + uint256 tokenIdsIdx; + address currOwnershipAddr; + uint256 tokenIdsLength = balanceOf(owner); + uint256[] memory tokenIds = new uint256[](tokenIdsLength); + TokenOwnership memory ownership; + for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) { + ownership = _ownerships[i]; + if (ownership.burned) { + continue; + } + if (ownership.addr != address(0)) { + currOwnershipAddr = ownership.addr; + } + if (currOwnershipAddr == owner) { + tokenIds[tokenIdsIdx++] = i; + } + } + return tokenIds; + } + } +} diff --git a/contracts/eip/queryable/ERC721AQueryableUpgradeable.sol b/contracts/eip/queryable/ERC721AQueryableUpgradeable.sol index 2018c8bc5..9eec8f7b6 100644 --- a/contracts/eip/queryable/ERC721AQueryableUpgradeable.sol +++ b/contracts/eip/queryable/ERC721AQueryableUpgradeable.sol @@ -48,13 +48,9 @@ abstract contract ERC721AQueryableUpgradeable is * - `burned = false` * - `extraData = ` */ - function explicitOwnershipOf(uint256 tokenId) - public - view - virtual - override - returns (TokenOwnership memory ownership) - { + function explicitOwnershipOf( + uint256 tokenId + ) public view virtual override returns (TokenOwnership memory ownership) { unchecked { if (tokenId >= _startTokenId()) { if (tokenId < _nextTokenId()) { @@ -111,11 +107,7 @@ abstract contract ERC721AQueryableUpgradeable is * Note that this function is optimized for smaller bytecode size over runtime gas, * since it is meant to be called off-chain. */ - function _tokensOfOwnerIn( - address owner, - uint256 start, - uint256 stop - ) private view returns (uint256[] memory) { + function _tokensOfOwnerIn(address owner, uint256 start, uint256 stop) private view returns (uint256[] memory) { unchecked { if (start >= stop) _revert(InvalidQueryRange.selector); // Set `start = max(start, _startTokenId())`. diff --git a/contracts/eip/queryable/ERC721AUpgradeable.sol b/contracts/eip/queryable/ERC721AUpgradeable.sol index 0e0bf0b75..af8bb4204 100644 --- a/contracts/eip/queryable/ERC721AUpgradeable.sol +++ b/contracts/eip/queryable/ERC721AUpgradeable.sol @@ -473,11 +473,9 @@ contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable { /** * @dev Returns the storage slot and value for the approved address of `tokenId`. */ - function _getApprovedSlotAndAddress(uint256 tokenId) - private - view - returns (uint256 approvedAddressSlot, address approvedAddress) - { + function _getApprovedSlotAndAddress( + uint256 tokenId + ) private view returns (uint256 approvedAddressSlot, address approvedAddress) { ERC721AStorage.TokenApprovalRef storage tokenApproval = ERC721AStorage.layout()._tokenApprovals[tokenId]; // The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`. assembly { @@ -503,11 +501,7 @@ contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable { * * Emits a {Transfer} event. */ - function transferFrom( - address from, - address to, - uint256 tokenId - ) public payable virtual override { + function transferFrom(address from, address to, uint256 tokenId) public payable virtual override { uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId); // Mask `from` to the lower 160 bits, in case the upper bits somehow aren't clean. @@ -584,11 +578,7 @@ contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable { /** * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) public payable virtual override { + function safeTransferFrom(address from, address to, uint256 tokenId) public payable virtual override { safeTransferFrom(from, to, tokenId, ""); } @@ -636,12 +626,7 @@ contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable { * - When `to` is zero, `tokenId` will be burned by `from`. * - `from` and `to` are never both zero. */ - function _beforeTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity - ) internal virtual {} + function _beforeTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {} /** * @dev Hook that is called after a set of serially-ordered token IDs @@ -659,12 +644,7 @@ contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable { * - When `to` is zero, `tokenId` has been burned by `from`. * - `from` and `to` are never both zero. */ - function _afterTokenTransfers( - address from, - address to, - uint256 startTokenId, - uint256 quantity - ) internal virtual {} + function _afterTokenTransfers(address from, address to, uint256 startTokenId, uint256 quantity) internal virtual {} /** * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract. @@ -834,11 +814,7 @@ contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable { * * Emits a {Transfer} event for each mint. */ - function _safeMint( - address to, - uint256 quantity, - bytes memory _data - ) internal virtual { + function _safeMint(address to, uint256 quantity, bytes memory _data) internal virtual { _mint(to, quantity); unchecked { @@ -887,11 +863,7 @@ contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable { * * Emits an {Approval} event. */ - function _approve( - address to, - uint256 tokenId, - bool approvalCheck - ) internal virtual { + function _approve(address to, uint256 tokenId, bool approvalCheck) internal virtual { address owner = ownerOf(tokenId); if (approvalCheck && _msgSenderERC721A() != owner) @@ -1025,21 +997,13 @@ contract ERC721AUpgradeable is ERC721A__Initializable, IERC721AUpgradeable { * - When `to` is zero, `tokenId` will be burned by `from`. * - `from` and `to` are never both zero. */ - function _extraData( - address from, - address to, - uint24 previousExtraData - ) internal view virtual returns (uint24) {} + function _extraData(address from, address to, uint24 previousExtraData) internal view virtual returns (uint24) {} /** * @dev Returns the next extra data for the packed ownership data. * The returned result is shifted into position. */ - function _nextExtraData( - address from, - address to, - uint256 prevOwnershipPacked - ) private view returns (uint256) { + function _nextExtraData(address from, address to, uint256 prevOwnershipPacked) private view returns (uint256) { uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA); return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA; } diff --git a/contracts/eip/queryable/IERC721AQueryable.sol b/contracts/eip/queryable/IERC721AQueryable.sol new file mode 100644 index 000000000..06fa1ca21 --- /dev/null +++ b/contracts/eip/queryable/IERC721AQueryable.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: MIT +// ERC721A Contracts v3.3.0 +// Creator: Chiru Labs + +pragma solidity ^0.8.4; + +import "../interface/IERC721A.sol"; + +/** + * @dev Interface of an ERC721AQueryable compliant contract. + */ +interface IERC721AQueryable is IERC721A { + /** + * Invalid query range (`start` >= `stop`). + */ + error InvalidQueryRange(); + + /** + * @dev Returns the `TokenOwnership` struct at `tokenId` without reverting. + * + * If the `tokenId` is out of bounds: + * - `addr` = `address(0)` + * - `startTimestamp` = `0` + * - `burned` = `false` + * + * If the `tokenId` is burned: + * - `addr` = `
` + * - `startTimestamp` = `` + * - `burned = `true` + * + * Otherwise: + * - `addr` = `
` + * - `startTimestamp` = `` + * - `burned = `false` + */ + function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory); + + /** + * @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order. + * See {ERC721AQueryable-explicitOwnershipOf} + */ + function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory); + + /** + * @dev Returns an array of token IDs owned by `owner`, + * in the range [`start`, `stop`) + * (i.e. `start <= tokenId < stop`). + * + * This function allows for tokens to be queried if the collection + * grows too big for a single call of {ERC721AQueryable-tokensOfOwner}. + * + * Requirements: + * + * - `start` < `stop` + */ + function tokensOfOwnerIn(address owner, uint256 start, uint256 stop) external view returns (uint256[] memory); + + /** + * @dev Returns an array of token IDs owned by `owner`. + * + * This function scans the ownership mapping and is O(totalSupply) in complexity. + * It is meant to be called off-chain. + * + * See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into + * multiple smaller scans if the collection is large enough to cause + * an out-of-gas error (10K pfp collections should be fine). + */ + function tokensOfOwner(address owner) external view returns (uint256[] memory); +} diff --git a/contracts/eip/queryable/IERC721AQueryableUpgradeable.sol b/contracts/eip/queryable/IERC721AQueryableUpgradeable.sol index c606624bb..ac52fb68c 100644 --- a/contracts/eip/queryable/IERC721AQueryableUpgradeable.sol +++ b/contracts/eip/queryable/IERC721AQueryableUpgradeable.sol @@ -53,11 +53,7 @@ interface IERC721AQueryableUpgradeable is IERC721AUpgradeable { * * - `start < stop` */ - function tokensOfOwnerIn( - address owner, - uint256 start, - uint256 stop - ) external view returns (uint256[] memory); + function tokensOfOwnerIn(address owner, uint256 start, uint256 stop) external view returns (uint256[] memory); /** * @dev Returns an array of token IDs owned by `owner`. diff --git a/contracts/eip/queryable/IERC721AUpgradeable.sol b/contracts/eip/queryable/IERC721AUpgradeable.sol index 15058be66..a2159f064 100644 --- a/contracts/eip/queryable/IERC721AUpgradeable.sol +++ b/contracts/eip/queryable/IERC721AUpgradeable.sol @@ -165,21 +165,12 @@ interface IERC721AUpgradeable { * * Emits a {Transfer} event. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId, - bytes calldata data - ) external payable; + function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external payable; /** * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`. */ - function safeTransferFrom( - address from, - address to, - uint256 tokenId - ) external payable; + function safeTransferFrom(address from, address to, uint256 tokenId) external payable; /** * @dev Transfers `tokenId` from `from` to `to`. @@ -197,11 +188,7 @@ interface IERC721AUpgradeable { * * Emits a {Transfer} event. */ - function transferFrom( - address from, - address to, - uint256 tokenId - ) external payable; + function transferFrom(address from, address to, uint256 tokenId) external payable; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. diff --git a/contracts/extension/BatchMintMetadata.sol b/contracts/extension/BatchMintMetadata.sol index 53883a65b..605b2435b 100644 --- a/contracts/extension/BatchMintMetadata.sol +++ b/contracts/extension/BatchMintMetadata.sol @@ -11,7 +11,7 @@ pragma solidity ^0.8.0; */ contract BatchMintMetadata { - /// @dev Largest tokenId of each batch of tokens with the same baseURI. + /// @dev Largest tokenId of each batch of tokens with the same baseURI + 1 {ex: batchId 100 at position 0 includes tokens 0-99} uint256[] private batchIds; /// @dev Mapping from id of a batch of tokens => to base URI for the respective batch of tokens. diff --git a/contracts/extension/BurnToClaim.sol b/contracts/extension/BurnToClaim.sol index cf5ae639c..c9e6c1a21 100644 --- a/contracts/extension/BurnToClaim.sol +++ b/contracts/extension/BurnToClaim.sol @@ -15,37 +15,38 @@ import "./interface/IBurnToClaim.sol"; abstract contract BurnToClaim is IBurnToClaim { BurnToClaimInfo internal burnToClaimInfo; + function getBurnToClaimInfo() public view returns (BurnToClaimInfo memory) { + return burnToClaimInfo; + } + function setBurnToClaimInfo(BurnToClaimInfo calldata _burnToClaimInfo) external virtual { require(_canSetBurnToClaim(), "Not authorized."); + require(_burnToClaimInfo.originContractAddress != address(0), "Origin contract not set."); + require(_burnToClaimInfo.currency != address(0), "Currency not set."); burnToClaimInfo = _burnToClaimInfo; } - function verifyBurnToClaim( - address _tokenOwner, - uint256 _tokenId, - uint256 _quantity - ) public view virtual { + function verifyBurnToClaim(address _tokenOwner, uint256 _tokenId, uint256 _quantity) public view virtual { BurnToClaimInfo memory _burnToClaimInfo = burnToClaimInfo; if (_burnToClaimInfo.tokenType == IBurnToClaim.TokenType.ERC721) { require(_quantity == 1, "Invalid amount"); - require(IERC721(_burnToClaimInfo.originContractAddress).ownerOf(_tokenId) == _tokenOwner); + require(IERC721(_burnToClaimInfo.originContractAddress).ownerOf(_tokenId) == _tokenOwner, "!Owner"); } else if (_burnToClaimInfo.tokenType == IBurnToClaim.TokenType.ERC1155) { uint256 _eligible1155TokenId = _burnToClaimInfo.tokenId; - require(_tokenId == _eligible1155TokenId || _eligible1155TokenId == type(uint256).max); - require(IERC1155(_burnToClaimInfo.originContractAddress).balanceOf(_tokenOwner, _tokenId) >= _quantity); + require(_tokenId == _eligible1155TokenId, "Invalid token Id"); + require( + IERC1155(_burnToClaimInfo.originContractAddress).balanceOf(_tokenOwner, _tokenId) >= _quantity, + "!Balance" + ); } // TODO: check if additional verification steps are required / override in main contract } - function _burnTokensOnOrigin( - address _tokenOwner, - uint256 _tokenId, - uint256 _quantity - ) internal virtual { + function _burnTokensOnOrigin(address _tokenOwner, uint256 _tokenId, uint256 _quantity) internal virtual { BurnToClaimInfo memory _burnToClaimInfo = burnToClaimInfo; if (_burnToClaimInfo.tokenType == IBurnToClaim.TokenType.ERC721) { ERC721Burnable(_burnToClaimInfo.originContractAddress).burn(_tokenId); diff --git a/contracts/extension/Drop.sol b/contracts/extension/Drop.sol index 8228c6065..50ce350df 100644 --- a/contracts/extension/Drop.sol +++ b/contracts/extension/Drop.sol @@ -49,11 +49,10 @@ abstract contract Drop is IDrop { } /// @dev Lets a contract admin set claim conditions. - function setClaimConditions(ClaimCondition[] calldata _conditions, bool _resetClaimEligibility) - external - virtual - override - { + function setClaimConditions( + ClaimCondition[] calldata _conditions, + bool _resetClaimEligibility + ) external virtual override { if (!_canSetClaimConditions()) { revert("Not authorized"); } @@ -124,12 +123,16 @@ abstract contract Drop is IDrop { address _currency, uint256 _pricePerToken, AllowlistProof calldata _allowlistProof - ) public view returns (bool isOverride) { + ) public view virtual returns (bool isOverride) { ClaimCondition memory currentClaimPhase = claimCondition.conditions[_conditionId]; uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet; uint256 claimPrice = currentClaimPhase.pricePerToken; address claimCurrency = currentClaimPhase.currency; + /* + * Here `isOverride` implies that if the merkle proof verification fails, + * the claimer would claim through open claim limit instead of allowlisted limit. + */ if (currentClaimPhase.merkleRoot != bytes32(0)) { (isOverride, ) = MerkleProof.verify( _allowlistProof.proof, @@ -192,11 +195,10 @@ abstract contract Drop is IDrop { } /// @dev Returns the supply claimed by claimer for a given conditionId. - function getSupplyClaimedByWallet(uint256 _conditionId, address _claimer) - public - view - returns (uint256 supplyClaimedByWallet) - { + function getSupplyClaimedByWallet( + uint256 _conditionId, + address _claimer + ) public view returns (uint256 supplyClaimedByWallet) { supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer]; } @@ -242,10 +244,10 @@ abstract contract Drop is IDrop { ) internal virtual; /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - virtual - returns (uint256 startTokenId); + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal virtual returns (uint256 startTokenId); /// @dev Determine what wallet can update claim conditions function _canSetClaimConditions() internal view virtual returns (bool); diff --git a/contracts/extension/Drop1155.sol b/contracts/extension/Drop1155.sol index 2d02a86f9..dc11569e3 100644 --- a/contracts/extension/Drop1155.sol +++ b/contracts/extension/Drop1155.sol @@ -134,12 +134,16 @@ abstract contract Drop1155 is IDrop1155 { address _currency, uint256 _pricePerToken, AllowlistProof calldata _allowlistProof - ) public view returns (bool isOverride) { + ) public view virtual returns (bool isOverride) { ClaimCondition memory currentClaimPhase = claimCondition[_tokenId].conditions[_conditionId]; uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet; uint256 claimPrice = currentClaimPhase.pricePerToken; address claimCurrency = currentClaimPhase.currency; + /* + * Here `isOverride` implies that if the merkle proof verification fails, + * the claimer would claim through open claim limit instead of allowlisted limit. + */ if (currentClaimPhase.merkleRoot != bytes32(0)) { (isOverride, ) = MerkleProof.verify( _allowlistProof.proof, @@ -199,11 +203,10 @@ abstract contract Drop1155 is IDrop1155 { } /// @dev Returns the claim condition at the given uid. - function getClaimConditionById(uint256 _tokenId, uint256 _conditionId) - external - view - returns (ClaimCondition memory condition) - { + function getClaimConditionById( + uint256 _tokenId, + uint256 _conditionId + ) external view returns (ClaimCondition memory condition) { condition = claimCondition[_tokenId].conditions[_conditionId]; } @@ -261,11 +264,7 @@ abstract contract Drop1155 is IDrop1155 { ) internal virtual; /// @dev Transfers the NFTs being claimed. - function transferTokensOnClaim( - address _to, - uint256 _tokenId, - uint256 _quantityBeingClaimed - ) internal virtual; + function transferTokensOnClaim(address _to, uint256 _tokenId, uint256 _quantityBeingClaimed) internal virtual; /// @dev Determine what wallet can update claim conditions function _canSetClaimConditions() internal view virtual returns (bool); diff --git a/contracts/extension/DropSinglePhase.sol b/contracts/extension/DropSinglePhase.sol index cba1f6da6..8a2f59425 100644 --- a/contracts/extension/DropSinglePhase.sol +++ b/contracts/extension/DropSinglePhase.sol @@ -100,12 +100,16 @@ abstract contract DropSinglePhase is IDropSinglePhase { address _currency, uint256 _pricePerToken, AllowlistProof calldata _allowlistProof - ) public view returns (bool isOverride) { + ) public view virtual returns (bool isOverride) { ClaimCondition memory currentClaimPhase = claimCondition; uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet; uint256 claimPrice = currentClaimPhase.pricePerToken; address claimCurrency = currentClaimPhase.currency; + /* + * Here `isOverride` implies that if the merkle proof verification fails, + * the claimer would claim through open claim limit instead of allowlisted limit. + */ if (currentClaimPhase.merkleRoot != bytes32(0)) { (isOverride, ) = MerkleProof.verify( _allowlistProof.proof, @@ -195,10 +199,10 @@ abstract contract DropSinglePhase is IDropSinglePhase { ) internal virtual; /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - virtual - returns (uint256 startTokenId); + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal virtual returns (uint256 startTokenId); function _canSetClaimConditions() internal view virtual returns (bool); } diff --git a/contracts/extension/DropSinglePhase1155.sol b/contracts/extension/DropSinglePhase1155.sol index 697013b82..2950ed485 100644 --- a/contracts/extension/DropSinglePhase1155.sol +++ b/contracts/extension/DropSinglePhase1155.sol @@ -108,12 +108,16 @@ abstract contract DropSinglePhase1155 is IDropSinglePhase1155 { address _currency, uint256 _pricePerToken, AllowlistProof calldata _allowlistProof - ) public view returns (bool isOverride) { + ) public view virtual returns (bool isOverride) { ClaimCondition memory currentClaimPhase = claimCondition[_tokenId]; uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet; uint256 claimPrice = currentClaimPhase.pricePerToken; address claimCurrency = currentClaimPhase.currency; + /* + * Here `isOverride` implies that if the merkle proof verification fails, + * the claimer would claim through open claim limit instead of allowlisted limit. + */ if (currentClaimPhase.merkleRoot != bytes32(0)) { (isOverride, ) = MerkleProof.verify( _allowlistProof.proof, @@ -205,11 +209,7 @@ abstract contract DropSinglePhase1155 is IDropSinglePhase1155 { ) internal virtual; /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim( - address _to, - uint256 _tokenId, - uint256 _quantityBeingClaimed - ) internal virtual; + function _transferTokensOnClaim(address _to, uint256 _tokenId, uint256 _quantityBeingClaimed) internal virtual; function _canSetClaimConditions() internal view virtual returns (bool); } diff --git a/contracts/extension/Initializable.sol b/contracts/extension/Initializable.sol index 7770f7d66..b91ab4621 100644 --- a/contracts/extension/Initializable.sol +++ b/contracts/extension/Initializable.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache 2.0 pragma solidity ^0.8.0; -import "../lib/TWAddress.sol"; +import "../lib/Address.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed @@ -76,7 +76,7 @@ abstract contract Initializable { modifier initializer() { bool isTopLevelCall = !_initializing; require( - (isTopLevelCall && _initialized < 1) || (!TWAddress.isContract(address(this)) && _initialized == 1), + (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; diff --git a/contracts/extension/LazyMintWithTier.sol b/contracts/extension/LazyMintWithTier.sol index 96234121b..50758b55b 100644 --- a/contracts/extension/LazyMintWithTier.sol +++ b/contracts/extension/LazyMintWithTier.sol @@ -72,11 +72,9 @@ abstract contract LazyMintWithTier is ILazyMintWithTier, BatchMintMetadata { } /// @notice Returns all metadata lazy minted for the given tier. - function _getMetadataInTier(string memory _tier) - private - view - returns (TokenRange[] memory tokens, string[] memory baseURIs) - { + function _getMetadataInTier( + string memory _tier + ) private view returns (TokenRange[] memory tokens, string[] memory baseURIs) { tokens = tokensInTier[_tier]; uint256 len = tokens.length; diff --git a/contracts/extension/Multicall.sol b/contracts/extension/Multicall.sol index dc9dd76d2..25e51cdf8 100644 --- a/contracts/extension/Multicall.sol +++ b/contracts/extension/Multicall.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; /// @author thirdweb -import "../lib/TWAddress.sol"; +import "../lib/Address.sol"; import "./interface/IMulticall.sol"; /** @@ -22,7 +22,7 @@ contract Multicall is IMulticall { function multicall(bytes[] calldata data) external virtual override returns (bytes[] memory results) { results = new bytes[](data.length); for (uint256 i = 0; i < data.length; i++) { - results[i] = TWAddress.functionDelegateCall(address(this), data[i]); + results[i] = Address.functionDelegateCall(address(this), data[i]); } return results; } diff --git a/contracts/extension/Permissions.sol b/contracts/extension/Permissions.sol index 66028cf36..9322b3d43 100644 --- a/contracts/extension/Permissions.sol +++ b/contracts/extension/Permissions.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; /// @author thirdweb import "./interface/IPermissions.sol"; -import "../lib/TWStrings.sol"; +import "../lib/Strings.sol"; /** * @title Permissions @@ -141,9 +141,9 @@ contract Permissions is IPermissions { string( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(account), 20), + Strings.toHexString(uint160(account), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ) ); @@ -157,9 +157,9 @@ contract Permissions is IPermissions { string( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(account), 20), + Strings.toHexString(uint160(account), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ) ); diff --git a/contracts/extension/PlatformFee.sol b/contracts/extension/PlatformFee.sol index d3152a7e5..5f3bdca9d 100644 --- a/contracts/extension/PlatformFee.sol +++ b/contracts/extension/PlatformFee.sol @@ -35,7 +35,7 @@ abstract contract PlatformFee is IPlatformFee { return (platformFeeRecipient, flatPlatformFee); } - /// @dev Returns the platform fee bps and recipient. + /// @dev Returns the platform fee type. function getPlatformFeeType() public view returns (PlatformFeeType) { return platformFeeType; } @@ -61,6 +61,9 @@ abstract contract PlatformFee is IPlatformFee { if (_platformFeeBps > 10_000) { revert("Exceeds max bps"); } + if (_platformFeeRecipient == address(0)) { + revert("Invalid recipient"); + } platformFeeBps = uint16(_platformFeeBps); platformFeeRecipient = _platformFeeRecipient; diff --git a/contracts/extension/PrimarySale.sol b/contracts/extension/PrimarySale.sol index 598645c93..551607e13 100644 --- a/contracts/extension/PrimarySale.sol +++ b/contracts/extension/PrimarySale.sol @@ -38,6 +38,9 @@ abstract contract PrimarySale is IPrimarySale { /// @dev Lets a contract admin set the recipient for all primary sales. function _setupPrimarySaleRecipient(address _saleRecipient) internal { + if (_saleRecipient == address(0)) { + revert("Invalid recipient"); + } recipient = _saleRecipient; emit PrimarySaleRecipientUpdated(_saleRecipient); } diff --git a/contracts/extension/Royalty.sol b/contracts/extension/Royalty.sol index b31f0ca41..cbe7ff28a 100644 --- a/contracts/extension/Royalty.sol +++ b/contracts/extension/Royalty.sol @@ -33,13 +33,10 @@ abstract contract Royalty is IRoyalty { * @return receiver Address of royalty recipient account. * @return royaltyAmount Royalty amount calculated at current royaltyBps value. */ - function royaltyInfo(uint256 tokenId, uint256 salePrice) - external - view - virtual - override - returns (address receiver, uint256 royaltyAmount) - { + function royaltyInfo( + uint256 tokenId, + uint256 salePrice + ) external view virtual override returns (address receiver, uint256 royaltyAmount) { (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId); receiver = recipient; royaltyAmount = (salePrice * bps) / 10_000; @@ -104,11 +101,7 @@ abstract contract Royalty is IRoyalty { * @param _recipient Address to be set as royalty recipient for given token Id. * @param _bps Updated royalty bps for the token Id. */ - function setRoyaltyInfoForToken( - uint256 _tokenId, - address _recipient, - uint256 _bps - ) external override { + function setRoyaltyInfoForToken(uint256 _tokenId, address _recipient, uint256 _bps) external override { if (!_canSetRoyaltyInfo()) { revert("Not authorized"); } @@ -117,11 +110,7 @@ abstract contract Royalty is IRoyalty { } /// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id. - function _setupRoyaltyInfoForToken( - uint256 _tokenId, - address _recipient, - uint256 _bps - ) internal { + function _setupRoyaltyInfoForToken(uint256 _tokenId, address _recipient, uint256 _bps) internal { if (_bps > 10_000) { revert("Exceeds max bps"); } diff --git a/contracts/extension/SharedMetadata.sol b/contracts/extension/SharedMetadata.sol index 0498ed187..72d244625 100644 --- a/contracts/extension/SharedMetadata.sol +++ b/contracts/extension/SharedMetadata.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.10; /// @author thirdweb -import "../lib/NFTMetadataRendererLib.sol"; +import "../lib/NFTMetadataRenderer.sol"; import "./interface/ISharedMetadata.sol"; import "../eip/interface/IERC4906.sol"; diff --git a/contracts/extension/SignatureAction.sol b/contracts/extension/SignatureAction.sol index 9c00cf1cc..4e857e870 100644 --- a/contracts/extension/SignatureAction.sol +++ b/contracts/extension/SignatureAction.sol @@ -18,12 +18,10 @@ abstract contract SignatureAction is EIP712, ISignatureAction { constructor() EIP712("SignatureAction", "1") {} /// @dev Verifies that a request is signed by an authorized account. - function verify(GenericRequest calldata _req, bytes calldata _signature) - public - view - override - returns (bool success, address signer) - { + function verify( + GenericRequest calldata _req, + bytes calldata _signature + ) public view override returns (bool success, address signer) { signer = _recoverAddress(_req, _signature); success = !executed[_req.uid] && _isAuthorizedSigner(signer); } @@ -32,10 +30,10 @@ abstract contract SignatureAction is EIP712, ISignatureAction { function _isAuthorizedSigner(address _signer) internal view virtual returns (bool); /// @dev Verifies a request and marks the request as processed. - function _processRequest(GenericRequest calldata _req, bytes calldata _signature) - internal - returns (address signer) - { + function _processRequest( + GenericRequest calldata _req, + bytes calldata _signature + ) internal returns (address signer) { bool success; (success, signer) = verify(_req, _signature); diff --git a/contracts/extension/SignatureActionUpgradeable.sol b/contracts/extension/SignatureActionUpgradeable.sol index 84b78b63e..18b791151 100644 --- a/contracts/extension/SignatureActionUpgradeable.sol +++ b/contracts/extension/SignatureActionUpgradeable.sol @@ -22,12 +22,10 @@ abstract contract SignatureActionUpgradeable is EIP712Upgradeable, ISignatureAct function __SignatureAction_init_unchained() internal onlyInitializing {} /// @dev Verifies that a request is signed by an authorized account. - function verify(GenericRequest calldata _req, bytes calldata _signature) - public - view - override - returns (bool success, address signer) - { + function verify( + GenericRequest calldata _req, + bytes calldata _signature + ) public view override returns (bool success, address signer) { signer = _recoverAddress(_req, _signature); success = !executed[_req.uid] && _isAuthorizedSigner(signer); } @@ -36,10 +34,10 @@ abstract contract SignatureActionUpgradeable is EIP712Upgradeable, ISignatureAct function _isAuthorizedSigner(address _signer) internal view virtual returns (bool); /// @dev Verifies a request and marks the request as processed. - function _processRequest(GenericRequest calldata _req, bytes calldata _signature) - internal - returns (address signer) - { + function _processRequest( + GenericRequest calldata _req, + bytes calldata _signature + ) internal returns (address signer) { bool success; (success, signer) = verify(_req, _signature); diff --git a/contracts/extension/SignatureMintERC1155.sol b/contracts/extension/SignatureMintERC1155.sol index 3885f9988..0d1c7aaf3 100644 --- a/contracts/extension/SignatureMintERC1155.sol +++ b/contracts/extension/SignatureMintERC1155.sol @@ -20,12 +20,10 @@ abstract contract SignatureMintERC1155 is EIP712, ISignatureMintERC1155 { constructor() EIP712("SignatureMintERC1155", "1") {} /// @dev Verifies that a mint request is signed by an account holding MINTER_ROLE (at the time of the function call). - function verify(MintRequest calldata _req, bytes calldata _signature) - public - view - override - returns (bool success, address signer) - { + function verify( + MintRequest calldata _req, + bytes calldata _signature + ) public view override returns (bool success, address signer) { signer = _recoverAddress(_req, _signature); success = !minted[_req.uid] && _canSignMintRequest(signer); } diff --git a/contracts/extension/SignatureMintERC1155Upgradeable.sol b/contracts/extension/SignatureMintERC1155Upgradeable.sol index 2d83e890e..f4e2891b5 100644 --- a/contracts/extension/SignatureMintERC1155Upgradeable.sol +++ b/contracts/extension/SignatureMintERC1155Upgradeable.sol @@ -26,12 +26,10 @@ abstract contract SignatureMintERC1155Upgradeable is Initializable, EIP712Upgrad function __SignatureMintERC1155_init_unchained() internal onlyInitializing {} /// @dev Verifies that a mint request is signed by an account holding MINTER_ROLE (at the time of the function call). - function verify(MintRequest calldata _req, bytes calldata _signature) - public - view - override - returns (bool success, address signer) - { + function verify( + MintRequest calldata _req, + bytes calldata _signature + ) public view override returns (bool success, address signer) { signer = _recoverAddress(_req, _signature); success = !minted[_req.uid] && _isAuthorizedSigner(signer); } diff --git a/contracts/extension/SignatureMintERC20.sol b/contracts/extension/SignatureMintERC20.sol index da29d15fd..fb561b8b3 100644 --- a/contracts/extension/SignatureMintERC20.sol +++ b/contracts/extension/SignatureMintERC20.sol @@ -20,12 +20,10 @@ abstract contract SignatureMintERC20 is EIP712, ISignatureMintERC20 { constructor() EIP712("SignatureMintERC20", "1") {} /// @dev Verifies that a mint request is signed by an account holding MINTER_ROLE (at the time of the function call). - function verify(MintRequest calldata _req, bytes calldata _signature) - public - view - override - returns (bool success, address signer) - { + function verify( + MintRequest calldata _req, + bytes calldata _signature + ) public view override returns (bool success, address signer) { signer = _recoverAddress(_req, _signature); success = !minted[_req.uid] && _canSignMintRequest(signer); } diff --git a/contracts/extension/SignatureMintERC20Upgradeable.sol b/contracts/extension/SignatureMintERC20Upgradeable.sol index 758f3495b..d67b1eac5 100644 --- a/contracts/extension/SignatureMintERC20Upgradeable.sol +++ b/contracts/extension/SignatureMintERC20Upgradeable.sol @@ -27,12 +27,10 @@ abstract contract SignatureMintERC20Upgradeable is Initializable, EIP712Upgradea function __SignatureMintERC20_init_unchained(string memory) internal onlyInitializing {} /// @dev Verifies that a mint request is signed by an account holding MINTER_ROLE (at the time of the function call). - function verify(MintRequest calldata _req, bytes calldata _signature) - public - view - override - returns (bool success, address signer) - { + function verify( + MintRequest calldata _req, + bytes calldata _signature + ) public view override returns (bool success, address signer) { signer = _recoverAddress(_req, _signature); success = !minted[_req.uid] && _isAuthorizedSigner(signer); } diff --git a/contracts/extension/SignatureMintERC721.sol b/contracts/extension/SignatureMintERC721.sol index 4e8fad30d..3b8d9b318 100644 --- a/contracts/extension/SignatureMintERC721.sol +++ b/contracts/extension/SignatureMintERC721.sol @@ -20,12 +20,10 @@ abstract contract SignatureMintERC721 is EIP712, ISignatureMintERC721 { constructor() EIP712("SignatureMintERC721", "1") {} /// @dev Verifies that a mint request is signed by an authorized account. - function verify(MintRequest calldata _req, bytes calldata _signature) - public - view - override - returns (bool success, address signer) - { + function verify( + MintRequest calldata _req, + bytes calldata _signature + ) public view override returns (bool success, address signer) { signer = _recoverAddress(_req, _signature); success = !minted[_req.uid] && _canSignMintRequest(signer); } diff --git a/contracts/extension/SignatureMintERC721Upgradeable.sol b/contracts/extension/SignatureMintERC721Upgradeable.sol index 82cef82a4..0dd97a417 100644 --- a/contracts/extension/SignatureMintERC721Upgradeable.sol +++ b/contracts/extension/SignatureMintERC721Upgradeable.sol @@ -26,12 +26,10 @@ abstract contract SignatureMintERC721Upgradeable is Initializable, EIP712Upgrade function __SignatureMintERC721_init_unchained() internal onlyInitializing {} /// @dev Verifies that a mint request is signed by an account holding MINTER_ROLE (at the time of the function call). - function verify(MintRequest calldata _req, bytes calldata _signature) - public - view - override - returns (bool success, address signer) - { + function verify( + MintRequest calldata _req, + bytes calldata _signature + ) public view override returns (bool success, address signer) { signer = _recoverAddress(_req, _signature); success = !minted[_req.uid] && _isAuthorizedSigner(signer); } diff --git a/contracts/extension/SoulboundERC721A.sol b/contracts/extension/SoulboundERC721A.sol index 2a8995ada..7c8eba553 100644 --- a/contracts/extension/SoulboundERC721A.sol +++ b/contracts/extension/SoulboundERC721A.sol @@ -40,12 +40,7 @@ abstract contract SoulboundERC721A is PermissionsEnumerable { function _canRestrictTransfers() internal view virtual returns (bool); /// @dev See {ERC721A-_beforeTokenTransfers}. - function _beforeTokenTransfers( - address from, - address to, - uint256, - uint256 - ) internal virtual { + function _beforeTokenTransfers(address from, address to, uint256, uint256) internal virtual { // If transfers are 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)) { diff --git a/contracts/extension/Staking1155.sol b/contracts/extension/Staking1155.sol index 499439471..28c1084d7 100644 --- a/contracts/extension/Staking1155.sol +++ b/contracts/extension/Staking1155.sol @@ -190,12 +190,10 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { * @return _tokensStaked Amount of tokens staked for given token-id. * @return _rewards Available reward amount. */ - function getStakeInfoForToken(uint256 _tokenId, address _staker) - external - view - virtual - returns (uint256 _tokensStaked, uint256 _rewards) - { + function getStakeInfoForToken( + uint256 _tokenId, + address _staker + ) external view virtual returns (uint256 _tokensStaked, uint256 _rewards) { _tokensStaked = stakers[_tokenId][_staker].amountStaked; _rewards = _availableRewards(_tokenId, _staker); } @@ -208,15 +206,13 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { * @return _tokenAmounts Amount of each token-id staked. * @return _totalRewards Total rewards available. */ - function getStakeInfo(address _staker) + function getStakeInfo( + address _staker + ) external view virtual - returns ( - uint256[] memory _tokensStaked, - uint256[] memory _tokenAmounts, - uint256 _totalRewards - ) + returns (uint256[] memory _tokensStaked, uint256[] memory _tokenAmounts, uint256 _totalRewards) { uint256[] memory _indexedTokens = indexedTokens; uint256[] memory _stakedAmounts = new uint256[](_indexedTokens.length); @@ -369,11 +365,7 @@ abstract contract Staking1155 is ReentrancyGuard, IStaking1155 { } /// @dev Set staking conditions, for a token-Id. - function _setStakingCondition( - uint256 _tokenId, - uint80 _timeUnit, - uint256 _rewardsPerUnitTime - ) internal virtual { + function _setStakingCondition(uint256 _tokenId, uint80 _timeUnit, uint256 _rewardsPerUnitTime) internal virtual { require(_timeUnit != 0, "time-unit can't be 0"); uint64 conditionId = nextConditionId[_tokenId]; diff --git a/contracts/extension/Staking1155Upgradeable.sol b/contracts/extension/Staking1155Upgradeable.sol index f4561f199..31d967efa 100644 --- a/contracts/extension/Staking1155Upgradeable.sol +++ b/contracts/extension/Staking1155Upgradeable.sol @@ -192,12 +192,10 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking * @return _tokensStaked Amount of tokens staked for given token-id. * @return _rewards Available reward amount. */ - function getStakeInfoForToken(uint256 _tokenId, address _staker) - external - view - virtual - returns (uint256 _tokensStaked, uint256 _rewards) - { + function getStakeInfoForToken( + uint256 _tokenId, + address _staker + ) external view virtual returns (uint256 _tokensStaked, uint256 _rewards) { _tokensStaked = stakers[_tokenId][_staker].amountStaked; _rewards = _availableRewards(_tokenId, _staker); } @@ -210,15 +208,13 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking * @return _tokenAmounts Amount of each token-id staked. * @return _totalRewards Total rewards available. */ - function getStakeInfo(address _staker) + function getStakeInfo( + address _staker + ) external view virtual - returns ( - uint256[] memory _tokensStaked, - uint256[] memory _tokenAmounts, - uint256 _totalRewards - ) + returns (uint256[] memory _tokensStaked, uint256[] memory _tokenAmounts, uint256 _totalRewards) { uint256[] memory _indexedTokens = indexedTokens; uint256[] memory _stakedAmounts = new uint256[](_indexedTokens.length); @@ -369,11 +365,7 @@ abstract contract Staking1155Upgradeable is ReentrancyGuardUpgradeable, IStaking } /// @dev Set staking conditions, for a token-Id. - function _setStakingCondition( - uint256 _tokenId, - uint80 _timeUnit, - uint256 _rewardsPerUnitTime - ) internal virtual { + function _setStakingCondition(uint256 _tokenId, uint80 _timeUnit, uint256 _rewardsPerUnitTime) internal virtual { require(_timeUnit != 0, "time-unit can't be 0"); uint64 conditionId = nextConditionId[_tokenId]; diff --git a/contracts/extension/Staking20.sol b/contracts/extension/Staking20.sol index dd953aef3..ab766b7f4 100644 --- a/contracts/extension/Staking20.sol +++ b/contracts/extension/Staking20.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.11; import "../external-deps/openzeppelin/security/ReentrancyGuard.sol"; import "../external-deps/openzeppelin/utils/math/SafeMath.sol"; import "../eip/interface/IERC20.sol"; -import "../lib/CurrencyTransferLib.sol"; +import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol"; import "./interface/IStaking20.sol"; @@ -271,11 +271,7 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 { } /// @dev Set staking conditions. - function _setStakingCondition( - uint80 _timeUnit, - uint256 _numerator, - uint256 _denominator - ) internal virtual { + function _setStakingCondition(uint80 _timeUnit, uint256 _numerator, uint256 _denominator) internal virtual { require(_denominator != 0, "divide by 0"); require(_timeUnit != 0, "time-unit can't be 0"); uint256 conditionId = nextConditionId; @@ -319,9 +315,9 @@ abstract contract Staking20 is ReentrancyGuard, IStaking20 { _rewards = noOverflowProduct && noOverflowSum ? rewardsSum : _rewards; } - (, _rewards) = SafeMath.tryMul(_rewards, 10**rewardTokenDecimals); + (, _rewards) = SafeMath.tryMul(_rewards, 10 ** rewardTokenDecimals); - _rewards /= (10**stakingTokenDecimals); + _rewards /= (10 ** stakingTokenDecimals); } /*//////////////////////////////////////////////////////////////////// diff --git a/contracts/extension/Staking20Upgradeable.sol b/contracts/extension/Staking20Upgradeable.sol index 647f7853f..c83c16b1a 100644 --- a/contracts/extension/Staking20Upgradeable.sol +++ b/contracts/extension/Staking20Upgradeable.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "../external-deps/openzeppelin/utils/math/SafeMath.sol"; import "../eip/interface/IERC20.sol"; -import "../lib/CurrencyTransferLib.sol"; +import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol"; import "./interface/IStaking20.sol"; @@ -278,11 +278,7 @@ abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 } /// @dev Set staking conditions. - function _setStakingCondition( - uint80 _timeUnit, - uint256 _numerator, - uint256 _denominator - ) internal virtual { + function _setStakingCondition(uint80 _timeUnit, uint256 _numerator, uint256 _denominator) internal virtual { require(_denominator != 0, "divide by 0"); require(_timeUnit != 0, "time-unit can't be 0"); uint256 conditionId = nextConditionId; @@ -326,9 +322,9 @@ abstract contract Staking20Upgradeable is ReentrancyGuardUpgradeable, IStaking20 _rewards = noOverflowProduct && noOverflowSum ? rewardsSum : _rewards; } - (, _rewards) = SafeMath.tryMul(_rewards, 10**rewardTokenDecimals); + (, _rewards) = SafeMath.tryMul(_rewards, 10 ** rewardTokenDecimals); - _rewards /= (10**stakingTokenDecimals); + _rewards /= (10 ** stakingTokenDecimals); } /*//////////////////////////////////////////////////////////////////// diff --git a/contracts/extension/Staking721.sol b/contracts/extension/Staking721.sol index bda6c73c3..38d786511 100644 --- a/contracts/extension/Staking721.sol +++ b/contracts/extension/Staking721.sol @@ -133,12 +133,9 @@ abstract contract Staking721 is ReentrancyGuard, IStaking721 { * @return _tokensStaked List of token-ids staked by staker. * @return _rewards Available reward amount. */ - function getStakeInfo(address _staker) - external - view - virtual - returns (uint256[] memory _tokensStaked, uint256 _rewards) - { + function getStakeInfo( + address _staker + ) external view virtual returns (uint256[] memory _tokensStaked, uint256 _rewards) { uint256[] memory _indexedTokens = indexedTokens; bool[] memory _isStakerToken = new bool[](_indexedTokens.length); uint256 indexedTokenCount = _indexedTokens.length; diff --git a/contracts/extension/Staking721Upgradeable.sol b/contracts/extension/Staking721Upgradeable.sol index bf1cdcc91..a5719e641 100644 --- a/contracts/extension/Staking721Upgradeable.sol +++ b/contracts/extension/Staking721Upgradeable.sol @@ -135,12 +135,9 @@ abstract contract Staking721Upgradeable is ReentrancyGuardUpgradeable, IStaking7 * @return _tokensStaked List of token-ids staked by staker. * @return _rewards Available reward amount. */ - function getStakeInfo(address _staker) - external - view - virtual - returns (uint256[] memory _tokensStaked, uint256 _rewards) - { + function getStakeInfo( + address _staker + ) external view virtual returns (uint256[] memory _tokensStaked, uint256 _rewards) { uint256[] memory _indexedTokens = indexedTokens; bool[] memory _isStakerToken = new bool[](_indexedTokens.length); uint256 indexedTokenCount = _indexedTokens.length; diff --git a/contracts/extension/TokenBundle.sol b/contracts/extension/TokenBundle.sol index 1938c0e30..76d773fca 100644 --- a/contracts/extension/TokenBundle.sol +++ b/contracts/extension/TokenBundle.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; /// @author thirdweb import "./interface/ITokenBundle.sol"; -import "../lib/CurrencyTransferLib.sol"; +import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol"; interface IERC165 { function supportsInterface(bytes4 interfaceId) external view returns (bool); @@ -81,11 +81,7 @@ abstract contract TokenBundle is ITokenBundle { } /// @dev Lets the calling contract update a token in a bundle for a unique bundle id and index. - function _updateTokenInBundle( - Token memory _tokenToBind, - uint256 _bundleId, - uint256 _index - ) internal { + function _updateTokenInBundle(Token memory _tokenToBind, uint256 _bundleId, uint256 _index) internal { require(_index < bundle[_bundleId].count, "index DNE"); _checkTokenType(_tokenToBind); bundle[_bundleId].tokens[_index] = _tokenToBind; diff --git a/contracts/extension/TokenStore.sol b/contracts/extension/TokenStore.sol index 85e8ba458..14b2042ba 100644 --- a/contracts/extension/TokenStore.sol +++ b/contracts/extension/TokenStore.sol @@ -14,7 +14,7 @@ import "../external-deps/openzeppelin/utils/ERC721/ERC721Holder.sol"; // ========== Internal imports ========== import { TokenBundle, ITokenBundle } from "./TokenBundle.sol"; -import "../lib/CurrencyTransferLib.sol"; +import { CurrencyTransferLib } from "../lib/CurrencyTransferLib.sol"; /** * @title Token Store @@ -58,11 +58,7 @@ contract TokenStore is TokenBundle, ERC721Holder, ERC1155Holder { } /// @dev Transfers an arbitrary ERC20 / ERC721 / ERC1155 token. - function _transferToken( - address _from, - address _to, - Token memory _token - ) internal { + function _transferToken(address _from, address _to, Token memory _token) internal { if (_token.tokenType == TokenType.ERC20) { CurrencyTransferLib.transferCurrencyWithWrapper( _token.assetContract, @@ -79,11 +75,7 @@ contract TokenStore is TokenBundle, ERC721Holder, ERC1155Holder { } /// @dev Transfers multiple arbitrary ERC20 / ERC721 / ERC1155 tokens. - function _transferTokenBatch( - address _from, - address _to, - Token[] memory _tokens - ) internal { + function _transferTokenBatch(address _from, address _to, Token[] memory _tokens) internal { uint256 nativeTokenValue; for (uint256 i = 0; i < _tokens.length; i += 1) { if (_tokens[i].assetContract == CurrencyTransferLib.NATIVE_TOKEN && _to == address(this)) { diff --git a/contracts/extension/interface/IAccountPermissions.sol b/contracts/extension/interface/IAccountPermissions.sol index 6c0a670db..0685a8301 100644 --- a/contracts/extension/interface/IAccountPermissions.sol +++ b/contracts/extension/interface/IAccountPermissions.sol @@ -100,10 +100,10 @@ interface IAccountPermissions { function getAllAdmins() external view returns (address[] memory admins); /// @dev Verifies that a request is signed by an authorized account. - function verifySignerPermissionRequest(SignerPermissionRequest calldata req, bytes calldata signature) - external - view - returns (bool success, address signer); + function verifySignerPermissionRequest( + SignerPermissionRequest calldata req, + bytes calldata signature + ) external view returns (bool success, address signer); /*/////////////////////////////////////////////////////////////// External functions diff --git a/contracts/extension/interface/IBurnableERC1155.sol b/contracts/extension/interface/IBurnableERC1155.sol index 7ab72fc2f..b4170c105 100644 --- a/contracts/extension/interface/IBurnableERC1155.sol +++ b/contracts/extension/interface/IBurnableERC1155.sol @@ -9,16 +9,8 @@ pragma solidity ^0.8.0; */ interface IBurnableERC1155 { /// @dev Lets a token owner burn the tokens they own (i.e. destroy for good) - function burn( - address account, - uint256 id, - uint256 value - ) external; + function burn(address account, uint256 id, uint256 value) external; /// @dev Lets a token owner burn multiple tokens they own at once (i.e. destroy for good) - function burnBatch( - address account, - uint256[] memory ids, - uint256[] memory values - ) external; + function burnBatch(address account, uint256[] memory ids, uint256[] memory values) external; } diff --git a/contracts/extension/interface/IClaimConditionsSinglePhase.sol b/contracts/extension/interface/IClaimConditionsSinglePhase.sol index 7f0e0b832..3b3d6d1ae 100644 --- a/contracts/extension/interface/IClaimConditionsSinglePhase.sol +++ b/contracts/extension/interface/IClaimConditionsSinglePhase.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; /// @author thirdweb -import "../../lib/TWBitMaps.sol"; +import "../../lib/BitMaps.sol"; import "./IClaimCondition.sol"; /** diff --git a/contracts/extension/interface/IClaimableERC1155.sol b/contracts/extension/interface/IClaimableERC1155.sol index eb83058f9..2afb26e29 100644 --- a/contracts/extension/interface/IClaimableERC1155.sol +++ b/contracts/extension/interface/IClaimableERC1155.sol @@ -23,11 +23,7 @@ interface IClaimableERC1155 { * @param _tokenId The tokenId of the lazy minted NFT to mint. * @param _quantity The number of tokens to mint. */ - function claim( - address _receiver, - uint256 _tokenId, - uint256 _quantity - ) external payable; + function claim(address _receiver, uint256 _tokenId, uint256 _quantity) external payable; /** * @notice Override this function to add logic for claim verification, based on conditions @@ -39,9 +35,5 @@ interface IClaimableERC1155 { * @param _tokenId The tokenId of the lazy minted NFT to mint. * @param _quantity The number of NFTs being claimed. */ - function verifyClaim( - address _claimer, - uint256 _tokenId, - uint256 _quantity - ) external view; + function verifyClaim(address _claimer, uint256 _tokenId, uint256 _quantity) external view; } diff --git a/contracts/extension/interface/IDrop1155.sol b/contracts/extension/interface/IDrop1155.sol index 2a18da17e..4fe60232e 100644 --- a/contracts/extension/interface/IDrop1155.sol +++ b/contracts/extension/interface/IDrop1155.sol @@ -71,9 +71,5 @@ interface IDrop1155 is IClaimConditionMultiPhase { * in the new claim conditions being set. * */ - function setClaimConditions( - uint256 tokenId, - ClaimCondition[] calldata phases, - bool resetClaimEligibility - ) external; + function setClaimConditions(uint256 tokenId, ClaimCondition[] calldata phases, bool resetClaimEligibility) external; } diff --git a/contracts/extension/interface/IDropSinglePhase1155.sol b/contracts/extension/interface/IDropSinglePhase1155.sol index dd1d8f065..8c6321095 100644 --- a/contracts/extension/interface/IDropSinglePhase1155.sol +++ b/contracts/extension/interface/IDropSinglePhase1155.sol @@ -70,9 +70,5 @@ interface IDropSinglePhase1155 is IClaimCondition { * * @param tokenId The tokenId for which to set the relevant claim condition. */ - function setClaimConditions( - uint256 tokenId, - ClaimCondition calldata phase, - bool resetClaimEligibility - ) external; + function setClaimConditions(uint256 tokenId, ClaimCondition calldata phase, bool resetClaimEligibility) external; } diff --git a/contracts/extension/interface/IMintableERC1155.sol b/contracts/extension/interface/IMintableERC1155.sol index e54223b6a..7b45769f6 100644 --- a/contracts/extension/interface/IMintableERC1155.sol +++ b/contracts/extension/interface/IMintableERC1155.sol @@ -20,10 +20,5 @@ interface IMintableERC1155 { * @param amount The number of copies of the NFT to mint. * */ - function mintTo( - address to, - uint256 tokenId, - string calldata uri, - uint256 amount - ) external; + function mintTo(address to, uint256 tokenId, string calldata uri, uint256 amount) external; } diff --git a/contracts/extension/interface/IOperatorFilterRegistry.sol b/contracts/extension/interface/IOperatorFilterRegistry.sol index 3ed0690ae..4b756a17c 100644 --- a/contracts/extension/interface/IOperatorFilterRegistry.sol +++ b/contracts/extension/interface/IOperatorFilterRegistry.sol @@ -14,29 +14,13 @@ interface IOperatorFilterRegistry { function unregister(address addr) external; - function updateOperator( - address registrant, - address operator, - bool filtered - ) external; - - function updateOperators( - address registrant, - address[] calldata operators, - bool filtered - ) external; - - function updateCodeHash( - address registrant, - bytes32 codehash, - bool filtered - ) external; - - function updateCodeHashes( - address registrant, - bytes32[] calldata codeHashes, - bool filtered - ) external; + function updateOperator(address registrant, address operator, bool filtered) external; + + function updateOperators(address registrant, address[] calldata operators, bool filtered) external; + + function updateCodeHash(address registrant, bytes32 codehash, bool filtered) external; + + function updateCodeHashes(address registrant, bytes32[] calldata codeHashes, bool filtered) external; function subscribe(address registrant, address registrantToSubscribe) external; diff --git a/contracts/extension/interface/IRoyalty.sol b/contracts/extension/interface/IRoyalty.sol index 4651098ab..f87cdff9c 100644 --- a/contracts/extension/interface/IRoyalty.sol +++ b/contracts/extension/interface/IRoyalty.sol @@ -26,11 +26,7 @@ interface IRoyalty is IERC2981 { function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external; /// @dev Lets a module admin set the royalty recipient for a particular token Id. - function setRoyaltyInfoForToken( - uint256 tokenId, - address recipient, - uint256 bps - ) external; + function setRoyaltyInfoForToken(uint256 tokenId, address recipient, uint256 bps) external; /// @dev Returns the royalty recipient for a particular token Id. function getRoyaltyInfoForToken(uint256 tokenId) external view returns (address, uint16); diff --git a/contracts/extension/interface/ISignatureAction.sol b/contracts/extension/interface/ISignatureAction.sol index 08e981fd7..dd98f5d49 100644 --- a/contracts/extension/interface/ISignatureAction.sol +++ b/contracts/extension/interface/ISignatureAction.sol @@ -37,8 +37,8 @@ interface ISignatureAction { * @return success Whether the payload is signed by the authorized wallet. * @return signer The address of the signer. */ - function verify(GenericRequest calldata req, bytes calldata signature) - external - view - returns (bool success, address signer); + function verify( + GenericRequest calldata req, + bytes calldata signature + ) external view returns (bool success, address signer); } diff --git a/contracts/extension/interface/ISignatureMintERC1155.sol b/contracts/extension/interface/ISignatureMintERC1155.sol index 3d2153edd..9f283eff0 100644 --- a/contracts/extension/interface/ISignatureMintERC1155.sol +++ b/contracts/extension/interface/ISignatureMintERC1155.sol @@ -59,10 +59,10 @@ interface ISignatureMintERC1155 { * * returns (success, signer) Result of verification and the recovered address. */ - function verify(MintRequest calldata req, bytes calldata signature) - external - view - returns (bool success, address signer); + function verify( + MintRequest calldata req, + bytes calldata signature + ) external view returns (bool success, address signer); /** * @notice Mints tokens according to the provided mint request. @@ -70,8 +70,8 @@ interface ISignatureMintERC1155 { * @param req The payload / mint request. * @param signature The signature produced by an account signing the mint request. */ - function mintWithSignature(MintRequest calldata req, bytes calldata signature) - external - payable - returns (address signer); + function mintWithSignature( + MintRequest calldata req, + bytes calldata signature + ) external payable returns (address signer); } diff --git a/contracts/extension/interface/ISignatureMintERC20.sol b/contracts/extension/interface/ISignatureMintERC20.sol index d1739a9e3..63aabbc33 100644 --- a/contracts/extension/interface/ISignatureMintERC20.sol +++ b/contracts/extension/interface/ISignatureMintERC20.sol @@ -46,10 +46,10 @@ interface ISignatureMintERC20 { * * returns (success, signer) Result of verification and the recovered address. */ - function verify(MintRequest calldata req, bytes calldata signature) - external - view - returns (bool success, address signer); + function verify( + MintRequest calldata req, + bytes calldata signature + ) external view returns (bool success, address signer); /** * @notice Mints tokens according to the provided mint request. @@ -57,8 +57,8 @@ interface ISignatureMintERC20 { * @param req The payload / mint request. * @param signature The signature produced by an account signing the mint request. */ - function mintWithSignature(MintRequest calldata req, bytes calldata signature) - external - payable - returns (address signer); + function mintWithSignature( + MintRequest calldata req, + bytes calldata signature + ) external payable returns (address signer); } diff --git a/contracts/extension/interface/ISignatureMintERC721.sol b/contracts/extension/interface/ISignatureMintERC721.sol index b67ff0a36..0478adae1 100644 --- a/contracts/extension/interface/ISignatureMintERC721.sol +++ b/contracts/extension/interface/ISignatureMintERC721.sol @@ -57,10 +57,10 @@ interface ISignatureMintERC721 { * * returns (success, signer) Result of verification and the recovered address. */ - function verify(MintRequest calldata req, bytes calldata signature) - external - view - returns (bool success, address signer); + function verify( + MintRequest calldata req, + bytes calldata signature + ) external view returns (bool success, address signer); /** * @notice Mints tokens according to the provided mint request. @@ -68,8 +68,8 @@ interface ISignatureMintERC721 { * @param req The payload / mint request. * @param signature The signature produced by an account signing the mint request. */ - function mintWithSignature(MintRequest calldata req, bytes calldata signature) - external - payable - returns (address signer); + function mintWithSignature( + MintRequest calldata req, + bytes calldata signature + ) external payable returns (address signer); } diff --git a/contracts/extension/interface/IStaking1155.sol b/contracts/extension/interface/IStaking1155.sol index 73e34e6b5..27351e332 100644 --- a/contracts/extension/interface/IStaking1155.sol +++ b/contracts/extension/interface/IStaking1155.sol @@ -94,22 +94,17 @@ interface IStaking1155 { * @param tokenId Staked token Id. * @param staker Address for which to calculated rewards. */ - function getStakeInfoForToken(uint256 tokenId, address staker) - external - view - returns (uint256 _tokensStaked, uint256 _rewards); + function getStakeInfoForToken( + uint256 tokenId, + address staker + ) external view returns (uint256 _tokensStaked, uint256 _rewards); /** * @notice View amount staked and total rewards for a user. * * @param staker Address for which to calculated rewards. */ - function getStakeInfo(address staker) - external - view - returns ( - uint256[] memory _tokensStaked, - uint256[] memory _tokenAmounts, - uint256 _totalRewards - ); + function getStakeInfo( + address staker + ) external view returns (uint256[] memory _tokensStaked, uint256[] memory _tokenAmounts, uint256 _totalRewards); } diff --git a/contracts/extension/plugin/PermissionsLogic.sol b/contracts/extension/plugin/PermissionsLogic.sol index af7bdc0bb..9206870bd 100644 --- a/contracts/extension/plugin/PermissionsLogic.sol +++ b/contracts/extension/plugin/PermissionsLogic.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.0; import "../interface/IPermissions.sol"; import "./PermissionsStorage.sol"; -import "../../lib/TWStrings.sol"; +import "../../lib/Strings.sol"; /** * @author thirdweb.com @@ -147,9 +147,9 @@ contract PermissionsLogic is IPermissions { string( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(account), 20), + Strings.toHexString(uint160(account), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ) ); @@ -163,9 +163,9 @@ contract PermissionsLogic is IPermissions { string( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(account), 20), + Strings.toHexString(uint160(account), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ) ); diff --git a/contracts/extension/upgradeable/AccountPermissions.sol b/contracts/extension/upgradeable/AccountPermissions.sol index bc9d9918a..63332489c 100644 --- a/contracts/extension/upgradeable/AccountPermissions.sol +++ b/contracts/extension/upgradeable/AccountPermissions.sol @@ -138,12 +138,10 @@ abstract contract AccountPermissions is IAccountPermissions, EIP712 { } /// @dev Verifies that a request is signed by an authorized account. - function verifySignerPermissionRequest(SignerPermissionRequest calldata req, bytes calldata signature) - public - view - virtual - returns (bool success, address signer) - { + function verifySignerPermissionRequest( + SignerPermissionRequest calldata req, + bytes calldata signature + ) public view virtual returns (bool success, address signer) { signer = _recoverAddress(_encodeRequest(req), signature); success = !_accountPermissionsStorage().executed[req.uid] && isAdmin(signer); } diff --git a/contracts/extension/upgradeable/BurnToClaim.sol b/contracts/extension/upgradeable/BurnToClaim.sol index 754e8a240..8d3736062 100644 --- a/contracts/extension/upgradeable/BurnToClaim.sol +++ b/contracts/extension/upgradeable/BurnToClaim.sol @@ -45,11 +45,7 @@ abstract contract BurnToClaim is IBurnToClaim { } /// @notice Verifies an attempt to burn tokens to claim new tokens. - function verifyBurnToClaim( - address _tokenOwner, - uint256 _tokenId, - uint256 _quantity - ) public view virtual { + function verifyBurnToClaim(address _tokenOwner, uint256 _tokenId, uint256 _quantity) public view virtual { BurnToClaimInfo memory _burnToClaimInfo = getBurnToClaimInfo(); if (_burnToClaimInfo.tokenType == IBurnToClaim.TokenType.ERC721) { @@ -67,11 +63,7 @@ abstract contract BurnToClaim is IBurnToClaim { } /// @dev Burns tokens to claim new tokens. - function _burnTokensOnOrigin( - address _tokenOwner, - uint256 _tokenId, - uint256 _quantity - ) internal virtual { + function _burnTokensOnOrigin(address _tokenOwner, uint256 _tokenId, uint256 _quantity) internal virtual { BurnToClaimInfo memory _burnToClaimInfo = getBurnToClaimInfo(); if (_burnToClaimInfo.tokenType == IBurnToClaim.TokenType.ERC721) { diff --git a/contracts/extension/upgradeable/Drop.sol b/contracts/extension/upgradeable/Drop.sol index 4bbfb1525..4d701a204 100644 --- a/contracts/extension/upgradeable/Drop.sol +++ b/contracts/extension/upgradeable/Drop.sol @@ -64,11 +64,10 @@ abstract contract Drop is IDrop { } /// @dev Lets a contract admin set claim conditions. - function setClaimConditions(ClaimCondition[] calldata _conditions, bool _resetClaimEligibility) - external - virtual - override - { + function setClaimConditions( + ClaimCondition[] calldata _conditions, + bool _resetClaimEligibility + ) external virtual override { if (!_canSetClaimConditions()) { revert("Not authorized"); } @@ -139,12 +138,16 @@ abstract contract Drop is IDrop { address _currency, uint256 _pricePerToken, AllowlistProof calldata _allowlistProof - ) public view returns (bool isOverride) { + ) public view virtual returns (bool isOverride) { ClaimCondition memory currentClaimPhase = _dropStorage().claimCondition.conditions[_conditionId]; uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet; uint256 claimPrice = currentClaimPhase.pricePerToken; address claimCurrency = currentClaimPhase.currency; + /* + * Here `isOverride` implies that if the merkle proof verification fails, + * the claimer would claim through open claim limit instead of allowlisted limit. + */ if (currentClaimPhase.merkleRoot != bytes32(0)) { (isOverride, ) = MerkleProof.verify( _allowlistProof.proof, @@ -211,11 +214,10 @@ abstract contract Drop is IDrop { } /// @dev Returns the supply claimed by claimer for a given conditionId. - function getSupplyClaimedByWallet(uint256 _conditionId, address _claimer) - public - view - returns (uint256 supplyClaimedByWallet) - { + function getSupplyClaimedByWallet( + uint256 _conditionId, + address _claimer + ) public view returns (uint256 supplyClaimedByWallet) { supplyClaimedByWallet = _dropStorage().claimCondition.supplyClaimedByWallet[_conditionId][_claimer]; } @@ -266,10 +268,10 @@ abstract contract Drop is IDrop { ) internal virtual; /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - virtual - returns (uint256 startTokenId); + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal virtual returns (uint256 startTokenId); /// @dev Determine what wallet can update claim conditions function _canSetClaimConditions() internal view virtual returns (bool); diff --git a/contracts/extension/upgradeable/Initializable.sol b/contracts/extension/upgradeable/Initializable.sol index faa5961e9..dfa3f4bcd 100644 --- a/contracts/extension/upgradeable/Initializable.sol +++ b/contracts/extension/upgradeable/Initializable.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache 2.0 pragma solidity ^0.8.0; -import "../../lib/TWAddress.sol"; +import "../../lib/Address.sol"; library InitStorage { /// @custom:storage-location erc7201:init.storage @@ -39,7 +39,7 @@ abstract contract Initializable { bool isTopLevelCall = !_initializing; require( - (isTopLevelCall && _initialized < 1) || (!TWAddress.isContract(address(this)) && _initialized == 1), + (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initStorage().initialized = 1; diff --git a/contracts/extension/upgradeable/Permissions.sol b/contracts/extension/upgradeable/Permissions.sol index 6cac1243d..c1fa7925e 100644 --- a/contracts/extension/upgradeable/Permissions.sol +++ b/contracts/extension/upgradeable/Permissions.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; /// @author thirdweb import "../interface/IPermissions.sol"; -import "../../lib/TWStrings.sol"; +import "../../lib/Strings.sol"; /** * @title Permissions @@ -157,9 +157,9 @@ contract Permissions is IPermissions { string( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(account), 20), + Strings.toHexString(uint160(account), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ) ); @@ -173,9 +173,9 @@ contract Permissions is IPermissions { string( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(account), 20), + Strings.toHexString(uint160(account), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ) ); diff --git a/contracts/extension/upgradeable/PlatformFee.sol b/contracts/extension/upgradeable/PlatformFee.sol index 89b4fd32b..6b9e9ef21 100644 --- a/contracts/extension/upgradeable/PlatformFee.sol +++ b/contracts/extension/upgradeable/PlatformFee.sol @@ -19,6 +19,10 @@ library PlatformFeeStorage { address platformFeeRecipient; /// @dev The % of primary sales collected as platform fees. uint16 platformFeeBps; + /// @dev Fee type variants: percentage fee and flat fee + IPlatformFee.PlatformFeeType platformFeeType; + /// @dev The flat amount collected by the contract as fees on primary sales. + uint256 flatPlatformFee; } function data() internal pure returns (Data storage data_) { @@ -44,6 +48,16 @@ abstract contract PlatformFee is IPlatformFee { return (_platformFeeStorage().platformFeeRecipient, uint16(_platformFeeStorage().platformFeeBps)); } + /// @dev Returns the platform fee bps and recipient. + function getFlatPlatformFeeInfo() public view returns (address, uint256) { + return (_platformFeeStorage().platformFeeRecipient, _platformFeeStorage().flatPlatformFee); + } + + /// @dev Returns the platform fee type. + function getPlatformFeeType() public view returns (PlatformFeeType) { + return _platformFeeStorage().platformFeeType; + } + /** * @notice Updates the platform fee recipient and bps. * @dev Caller should be authorized to set platform fee info. @@ -75,6 +89,38 @@ abstract contract PlatformFee is IPlatformFee { emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps); } + /// @notice Lets a module admin set a flat fee on primary sales. + function setFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) external { + if (!_canSetPlatformFeeInfo()) { + revert("Not authorized"); + } + + _setupFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee); + } + + /// @dev Sets a flat fee on primary sales. + function _setupFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) internal { + _platformFeeStorage().flatPlatformFee = _flatFee; + _platformFeeStorage().platformFeeRecipient = _platformFeeRecipient; + + emit FlatPlatformFeeUpdated(_platformFeeRecipient, _flatFee); + } + + /// @notice Lets a module admin set platform fee type. + function setPlatformFeeType(PlatformFeeType _feeType) external { + if (!_canSetPlatformFeeInfo()) { + revert("Not authorized"); + } + _setupPlatformFeeType(_feeType); + } + + /// @dev Sets platform fee type. + function _setupPlatformFeeType(PlatformFeeType _feeType) internal { + _platformFeeStorage().platformFeeType = _feeType; + + emit PlatformFeeTypeUpdated(_feeType); + } + /// @dev Returns the PlatformFee storage. function _platformFeeStorage() internal pure returns (PlatformFeeStorage.Data storage data) { data = PlatformFeeStorage.data(); diff --git a/contracts/extension/upgradeable/Royalty.sol b/contracts/extension/upgradeable/Royalty.sol index 9065090e6..b8cadcd02 100644 --- a/contracts/extension/upgradeable/Royalty.sol +++ b/contracts/extension/upgradeable/Royalty.sol @@ -47,13 +47,10 @@ abstract contract Royalty is IRoyalty { * @return receiver Address of royalty recipient account. * @return royaltyAmount Royalty amount calculated at current royaltyBps value. */ - function royaltyInfo(uint256 tokenId, uint256 salePrice) - external - view - virtual - override - returns (address receiver, uint256 royaltyAmount) - { + function royaltyInfo( + uint256 tokenId, + uint256 salePrice + ) external view virtual override returns (address receiver, uint256 royaltyAmount) { (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId); receiver = recipient; royaltyAmount = (salePrice * bps) / 10_000; @@ -118,11 +115,7 @@ abstract contract Royalty is IRoyalty { * @param _recipient Address to be set as royalty recipient for given token Id. * @param _bps Updated royalty bps for the token Id. */ - function setRoyaltyInfoForToken( - uint256 _tokenId, - address _recipient, - uint256 _bps - ) external override { + function setRoyaltyInfoForToken(uint256 _tokenId, address _recipient, uint256 _bps) external override { if (!_canSetRoyaltyInfo()) { revert("Not authorized"); } @@ -131,11 +124,7 @@ abstract contract Royalty is IRoyalty { } /// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id. - function _setupRoyaltyInfoForToken( - uint256 _tokenId, - address _recipient, - uint256 _bps - ) internal { + function _setupRoyaltyInfoForToken(uint256 _tokenId, address _recipient, uint256 _bps) internal { if (_bps > 10_000) { revert("Exceeds max bps"); } diff --git a/contracts/extension/upgradeable/RulesEngine.sol b/contracts/extension/upgradeable/RulesEngine.sol index e75133db0..93fa215b9 100644 --- a/contracts/extension/upgradeable/RulesEngine.sol +++ b/contracts/extension/upgradeable/RulesEngine.sol @@ -117,7 +117,7 @@ abstract contract RulesEngine is IRulesEngine { if (_rule.tokenType == TokenType.ERC20) { // NOTE: We are rounding down the ERC20 balance to the nearest full unit. - uint256 unit = 10**IERC20Metadata(_rule.token).decimals(); + uint256 unit = 10 ** IERC20Metadata(_rule.token).decimals(); balance = IERC20(_rule.token).balanceOf(_tokenOwner) / unit; } else if (_rule.tokenType == TokenType.ERC721) { balance = IERC721(_rule.token).balanceOf(_tokenOwner); diff --git a/contracts/extension/upgradeable/SharedMetadataBatch.sol b/contracts/extension/upgradeable/SharedMetadataBatch.sol index 1b8cbdd52..c4001864d 100644 --- a/contracts/extension/upgradeable/SharedMetadataBatch.sol +++ b/contracts/extension/upgradeable/SharedMetadataBatch.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.11; /// @author thirdweb -import "../../lib/NFTMetadataRendererLib.sol"; +import "../../lib/NFTMetadataRenderer.sol"; import "../interface/ISharedMetadataBatch.sol"; import "../../external-deps/openzeppelin/utils/EnumerableSet.sol"; diff --git a/contracts/external-deps/chainlink/VRFV2WrapperConsumerBase.sol b/contracts/external-deps/chainlink/VRFV2WrapperConsumerBase.sol index c5e2f7e68..f89a39c1a 100644 --- a/contracts/external-deps/chainlink/VRFV2WrapperConsumerBase.sol +++ b/contracts/external-deps/chainlink/VRFV2WrapperConsumerBase.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol"; -import "@chainlink/contracts/src/v0.8/interfaces/VRFV2WrapperInterface.sol"; +import "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol"; +import "@chainlink/contracts/src/v0.8/vrf/interfaces/VRFV2WrapperInterface.sol"; /** ******************************************************************************* * @notice Interface for contracts using VRF randomness through the VRF V2 wrapper diff --git a/contracts/external-deps/openzeppelin/ERC1155PresetUpgradeable.sol b/contracts/external-deps/openzeppelin/ERC1155PresetUpgradeable.sol index 7f184a9c3..e3163c3e2 100644 --- a/contracts/external-deps/openzeppelin/ERC1155PresetUpgradeable.sol +++ b/contracts/external-deps/openzeppelin/ERC1155PresetUpgradeable.sol @@ -74,12 +74,7 @@ contract ERC1155PresetUpgradeable is * * - the caller must have the `MINTER_ROLE`. */ - function mint( - address to, - uint256 id, - uint256 amount, - bytes memory data - ) public virtual { + function mint(address to, uint256 id, uint256 amount, bytes memory data) public virtual { require(hasRole(MINTER_ROLE, _msgSender()), "must have minter role"); _mint(to, id, amount, data); @@ -88,12 +83,7 @@ contract ERC1155PresetUpgradeable is /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] variant of {mint}. */ - function mintBatch( - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) public virtual { + function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) public virtual { require(hasRole(MINTER_ROLE, _msgSender()), "must have minter role"); _mintBatch(to, ids, amounts, data); @@ -130,7 +120,9 @@ contract ERC1155PresetUpgradeable is /** * @dev See {IERC165-supportsInterface}. */ - function supportsInterface(bytes4 interfaceId) + function supportsInterface( + bytes4 interfaceId + ) public view virtual diff --git a/contracts/external-deps/openzeppelin/finance/PaymentSplitterUpgradeable.sol b/contracts/external-deps/openzeppelin/finance/PaymentSplitterUpgradeable.sol index 8bb306ea1..c60b0048d 100644 --- a/contracts/external-deps/openzeppelin/finance/PaymentSplitterUpgradeable.sol +++ b/contracts/external-deps/openzeppelin/finance/PaymentSplitterUpgradeable.sol @@ -62,10 +62,10 @@ contract PaymentSplitterUpgradeable is Initializable, ContextUpgradeable { __PaymentSplitter_init_unchained(payees, shares_); } - function __PaymentSplitter_init_unchained(address[] memory payees, uint256[] memory shares_) - internal - onlyInitializing - { + function __PaymentSplitter_init_unchained( + address[] memory payees, + uint256[] memory shares_ + ) internal onlyInitializing { require(payees.length == shares_.length, "PaymentSplitter: payees and shares length mismatch"); require(payees.length > 0, "PaymentSplitter: no payees"); diff --git a/contracts/external-deps/openzeppelin/governance/utils/IVotes.sol b/contracts/external-deps/openzeppelin/governance/utils/IVotes.sol index 6317d7752..0bef3f920 100644 --- a/contracts/external-deps/openzeppelin/governance/utils/IVotes.sol +++ b/contracts/external-deps/openzeppelin/governance/utils/IVotes.sol @@ -50,12 +50,5 @@ interface IVotes { /** * @dev Delegates votes from signer to `delegatee`. */ - function delegateBySig( - address delegatee, - uint256 nonce, - uint256 expiry, - uint8 v, - bytes32 r, - bytes32 s - ) external; + function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external; } diff --git a/contracts/external-deps/openzeppelin/metatx/MinimalForwarderEOAOnly.sol b/contracts/external-deps/openzeppelin/metatx/MinimalForwarderEOAOnly.sol index b55bcb7cd..6a5c2ef61 100644 --- a/contracts/external-deps/openzeppelin/metatx/MinimalForwarderEOAOnly.sol +++ b/contracts/external-deps/openzeppelin/metatx/MinimalForwarderEOAOnly.sol @@ -39,11 +39,10 @@ contract MinimalForwarderEOAOnly is EIP712 { return _nonces[req.from] == req.nonce && signer == req.from; } - function execute(ForwardRequest calldata req, bytes calldata signature) - public - payable - returns (bool, bytes memory) - { + function execute( + ForwardRequest calldata req, + bytes calldata signature + ) public payable returns (bool, bytes memory) { require(msg.sender == tx.origin, "not EOA"); require(verify(req, signature), "MinimalForwarder: signature does not match request"); _nonces[req.from] = req.nonce + 1; diff --git a/contracts/external-deps/openzeppelin/proxy/Clones.sol b/contracts/external-deps/openzeppelin/proxy/Clones.sol index 93ea0cec7..712519892 100644 --- a/contracts/external-deps/openzeppelin/proxy/Clones.sol +++ b/contracts/external-deps/openzeppelin/proxy/Clones.sol @@ -79,11 +79,10 @@ library Clones { /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ - function predictDeterministicAddress(address implementation, bytes32 salt) - internal - view - returns (address predicted) - { + function predictDeterministicAddress( + address implementation, + bytes32 salt + ) internal view returns (address predicted) { return predictDeterministicAddress(implementation, salt, address(this)); } } diff --git a/contracts/external-deps/openzeppelin/proxy/ERC1967/ERC1967Upgrade.sol b/contracts/external-deps/openzeppelin/proxy/ERC1967/ERC1967Upgrade.sol index 766292a08..d74e634a3 100644 --- a/contracts/external-deps/openzeppelin/proxy/ERC1967/ERC1967Upgrade.sol +++ b/contracts/external-deps/openzeppelin/proxy/ERC1967/ERC1967Upgrade.sol @@ -5,8 +5,8 @@ pragma solidity ^0.8.2; import "../beacon/IBeacon.sol"; import "../IERC1822Proxiable.sol"; -import "../../../../lib/TWAddress.sol"; -import "../../../../lib/TWStorageSlot.sol"; +import "../../../../lib/Address.sol"; +import "../../../../lib/StorageSlot.sol"; /** * @dev This abstract contract provides getters and event emitting update functions for @@ -36,15 +36,15 @@ abstract contract ERC1967Upgrade { * @dev Returns the current implementation address. */ function _getImplementation() internal view returns (address) { - return TWStorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; + return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; } /** * @dev Stores a new address in the EIP1967 implementation slot. */ function _setImplementation(address newImplementation) private { - require(TWAddress.isContract(newImplementation), "ERC1967: new implementation is not a contract"); - TWStorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; + require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); + StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; } /** @@ -62,14 +62,10 @@ abstract contract ERC1967Upgrade { * * Emits an {Upgraded} event. */ - function _upgradeToAndCall( - address newImplementation, - bytes memory data, - bool forceCall - ) internal { + function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal { _upgradeTo(newImplementation); if (data.length > 0 || forceCall) { - TWAddress.functionDelegateCall(newImplementation, data); + Address.functionDelegateCall(newImplementation, data); } } @@ -78,15 +74,11 @@ abstract contract ERC1967Upgrade { * * Emits an {Upgraded} event. */ - function _upgradeToAndCallUUPS( - address newImplementation, - bytes memory data, - bool forceCall - ) internal { + function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal { // Upgrades from old implementations will perform a rollback test. This test requires the new // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing // this special case will break upgrade paths from old UUPS implementation to new ones. - if (TWStorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) { + if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) { _setImplementation(newImplementation); } else { try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { @@ -114,7 +106,7 @@ abstract contract ERC1967Upgrade { * @dev Returns the current admin. */ function _getAdmin() internal view returns (address) { - return TWStorageSlot.getAddressSlot(_ADMIN_SLOT).value; + return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; } /** @@ -122,7 +114,7 @@ abstract contract ERC1967Upgrade { */ function _setAdmin(address newAdmin) private { require(newAdmin != address(0), "ERC1967: new admin is the zero address"); - TWStorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; + StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; } /** @@ -150,19 +142,19 @@ abstract contract ERC1967Upgrade { * @dev Returns the current beacon. */ function _getBeacon() internal view returns (address) { - return TWStorageSlot.getAddressSlot(_BEACON_SLOT).value; + return StorageSlot.getAddressSlot(_BEACON_SLOT).value; } /** * @dev Stores a new beacon in the EIP1967 beacon slot. */ function _setBeacon(address newBeacon) private { - require(TWAddress.isContract(newBeacon), "ERC1967: new beacon is not a contract"); + require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); require( - TWAddress.isContract(IBeacon(newBeacon).implementation()), + Address.isContract(IBeacon(newBeacon).implementation()), "ERC1967: beacon implementation is not a contract" ); - TWStorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; + StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; } /** @@ -171,15 +163,11 @@ abstract contract ERC1967Upgrade { * * Emits a {BeaconUpgraded} event. */ - function _upgradeBeaconToAndCall( - address newBeacon, - bytes memory data, - bool forceCall - ) internal { + function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal { _setBeacon(newBeacon); emit BeaconUpgraded(newBeacon); if (data.length > 0 || forceCall) { - TWAddress.functionDelegateCall(IBeacon(newBeacon).implementation(), data); + Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); } } } diff --git a/contracts/external-deps/openzeppelin/proxy/utils/Initializable.sol b/contracts/external-deps/openzeppelin/proxy/utils/Initializable.sol index 3345941e4..a2ade335e 100644 --- a/contracts/external-deps/openzeppelin/proxy/utils/Initializable.sol +++ b/contracts/external-deps/openzeppelin/proxy/utils/Initializable.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.2; -import "../../../../lib/TWAddress.sol"; +import "../../../../lib/Address.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed @@ -78,7 +78,7 @@ abstract contract Initializable { modifier initializer() { bool isTopLevelCall = !_initializing; require( - (isTopLevelCall && _initialized < 1) || (!TWAddress.isContract(address(this)) && _initialized == 1), + (isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; diff --git a/contracts/external-deps/openzeppelin/token/ERC20/ERC20.sol b/contracts/external-deps/openzeppelin/token/ERC20/ERC20.sol index c21319b88..66ce9ec07 100644 --- a/contracts/external-deps/openzeppelin/token/ERC20/ERC20.sol +++ b/contracts/external-deps/openzeppelin/token/ERC20/ERC20.sol @@ -155,11 +155,7 @@ contract ERC20 is Context, IERC20, IERC20Metadata { * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ - function transferFrom( - address from, - address to, - uint256 amount - ) public virtual override returns (bool) { + function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); @@ -223,11 +219,7 @@ contract ERC20 is Context, IERC20, IERC20Metadata { * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ - function _transfer( - address from, - address to, - uint256 amount - ) internal virtual { + 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"); @@ -307,11 +299,7 @@ contract ERC20 is Context, IERC20, IERC20Metadata { * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ - function _approve( - address owner, - address spender, - uint256 amount - ) internal virtual { + 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"); @@ -327,11 +315,7 @@ contract ERC20 is Context, IERC20, IERC20Metadata { * * Might emit an {Approval} event. */ - function _spendAllowance( - address owner, - address spender, - uint256 amount - ) internal virtual { + 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"); @@ -355,11 +339,7 @@ contract ERC20 is Context, IERC20, IERC20Metadata { * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual {} + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes @@ -375,9 +355,5 @@ contract ERC20 is Context, IERC20, IERC20Metadata { * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ - function _afterTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual {} + function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} } diff --git a/contracts/external-deps/openzeppelin/token/ERC20/extensions/ERC20Votes.sol b/contracts/external-deps/openzeppelin/token/ERC20/extensions/ERC20Votes.sol index f7ac33a48..e80527066 100644 --- a/contracts/external-deps/openzeppelin/token/ERC20/extensions/ERC20Votes.sol +++ b/contracts/external-deps/openzeppelin/token/ERC20/extensions/ERC20Votes.sol @@ -183,11 +183,7 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { * * Emits a {DelegateVotesChanged} event. */ - function _afterTokenTransfer( - address from, - address to, - uint256 amount - ) internal virtual override { + function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual override { super._afterTokenTransfer(from, to, amount); _moveVotingPower(delegates(from), delegates(to), amount); @@ -208,11 +204,7 @@ abstract contract ERC20Votes is IVotes, ERC20Permit { _moveVotingPower(currentDelegate, delegatee, delegatorBalance); } - function _moveVotingPower( - address src, - address dst, - uint256 amount - ) private { + function _moveVotingPower(address src, address dst, uint256 amount) private { if (src != dst && amount > 0) { if (src != address(0)) { (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); diff --git a/contracts/external-deps/openzeppelin/token/ERC20/utils/SafeERC20.sol b/contracts/external-deps/openzeppelin/token/ERC20/utils/SafeERC20.sol index 1ced3264a..572ccd811 100644 --- a/contracts/external-deps/openzeppelin/token/ERC20/utils/SafeERC20.sol +++ b/contracts/external-deps/openzeppelin/token/ERC20/utils/SafeERC20.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import "../../../../../eip/interface/IERC20.sol"; -import "../../../../../lib/TWAddress.sol"; +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; /** * @title SafeERC20 @@ -16,22 +16,13 @@ import "../../../../../lib/TWAddress.sol"; * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { - using TWAddress for address; + using Address for address; - function safeTransfer( - IERC20 token, - address to, - uint256 value - ) internal { + function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } - function safeTransferFrom( - IERC20 token, - address from, - address to, - uint256 value - ) internal { + function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } @@ -42,11 +33,7 @@ library SafeERC20 { * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ - function safeApprove( - IERC20 token, - address spender, - uint256 value - ) internal { + function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' @@ -57,20 +44,12 @@ library SafeERC20 { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } - function safeIncreaseAllowance( - IERC20 token, - address spender, - uint256 value - ) internal { + function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } - function safeDecreaseAllowance( - IERC20 token, - address spender, - uint256 value - ) internal { + function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); diff --git a/contracts/external-deps/openzeppelin/token/ERC721/utils/ERC721Holder.sol b/contracts/external-deps/openzeppelin/token/ERC721/utils/ERC721Holder.sol index 394926d51..cfa533a47 100644 --- a/contracts/external-deps/openzeppelin/token/ERC721/utils/ERC721Holder.sol +++ b/contracts/external-deps/openzeppelin/token/ERC721/utils/ERC721Holder.sol @@ -17,12 +17,7 @@ contract ERC721Holder is IERC721Receiver { * * Always returns `IERC721Receiver.onERC721Received.selector`. */ - function onERC721Received( - address, - address, - uint256, - bytes memory - ) public virtual override returns (bytes4) { + function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) { return this.onERC721Received.selector; } } diff --git a/contracts/external-deps/openzeppelin/utils/Create2.sol b/contracts/external-deps/openzeppelin/utils/Create2.sol index b1e4ee15c..d810d8045 100644 --- a/contracts/external-deps/openzeppelin/utils/Create2.sol +++ b/contracts/external-deps/openzeppelin/utils/Create2.sol @@ -27,11 +27,7 @@ library Create2 { * - the factory must have a balance of at least `amount`. * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. */ - function deploy( - uint256 amount, - bytes32 salt, - bytes memory bytecode - ) internal returns (address) { + function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address) { address addr; require(address(this).balance >= amount, "Create2: insufficient balance"); require(bytecode.length != 0, "Create2: bytecode length is zero"); @@ -55,11 +51,7 @@ library Create2 { * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. */ - function computeAddress( - bytes32 salt, - bytes32 bytecodeHash, - address deployer - ) internal pure returns (address) { + function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address) { bytes32 _data = keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)); return address(uint160(uint256(_data))); } diff --git a/contracts/external-deps/openzeppelin/utils/ERC721/ERC721Holder.sol b/contracts/external-deps/openzeppelin/utils/ERC721/ERC721Holder.sol index b7dc2ba09..5d364e52e 100644 --- a/contracts/external-deps/openzeppelin/utils/ERC721/ERC721Holder.sol +++ b/contracts/external-deps/openzeppelin/utils/ERC721/ERC721Holder.sol @@ -17,12 +17,7 @@ contract ERC721Holder is IERC721Receiver { * * Always returns `IERC721Receiver.onERC721Received.selector`. */ - function onERC721Received( - address, - address, - uint256, - bytes memory - ) public virtual override returns (bytes4) { + function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) { return this.onERC721Received.selector; } } diff --git a/contracts/external-deps/openzeppelin/utils/cryptography/ECDSA.sol b/contracts/external-deps/openzeppelin/utils/cryptography/ECDSA.sol index 9cf8e0fa0..0c9f09bdb 100644 --- a/contracts/external-deps/openzeppelin/utils/cryptography/ECDSA.sol +++ b/contracts/external-deps/openzeppelin/utils/cryptography/ECDSA.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; -import "../../../../lib/TWStrings.sol"; +import "../../../../lib/Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. @@ -98,11 +98,7 @@ library ECDSA { * * _Available since v4.3._ */ - function tryRecover( - bytes32 hash, - bytes32 r, - bytes32 vs - ) internal pure returns (address, RecoverError) { + function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); @@ -113,11 +109,7 @@ library ECDSA { * * _Available since v4.2._ */ - function recover( - bytes32 hash, - bytes32 r, - bytes32 vs - ) internal pure returns (address) { + function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; @@ -129,12 +121,7 @@ library ECDSA { * * _Available since v4.3._ */ - function tryRecover( - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) internal pure returns (address, RecoverError) { + function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most @@ -161,12 +148,7 @@ library ECDSA { * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ - function recover( - bytes32 hash, - uint8 v, - bytes32 r, - bytes32 s - ) internal pure returns (address) { + function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; @@ -200,7 +182,7 @@ library ECDSA { * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", TWStrings.toString(s.length), s)); + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** diff --git a/contracts/external-deps/openzeppelin/utils/math/SafeMath.sol b/contracts/external-deps/openzeppelin/utils/math/SafeMath.sol index 550f0e779..2f48fb736 100644 --- a/contracts/external-deps/openzeppelin/utils/math/SafeMath.sol +++ b/contracts/external-deps/openzeppelin/utils/math/SafeMath.sol @@ -165,11 +165,7 @@ library SafeMath { * * - Subtraction cannot overflow. */ - function sub( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { + function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { unchecked { require(b <= a, errorMessage); return a - b; @@ -188,11 +184,7 @@ library SafeMath { * * - The divisor cannot be zero. */ - function div( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { + function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a / b; @@ -214,11 +206,7 @@ library SafeMath { * * - The divisor cannot be zero. */ - function mod( - uint256 a, - uint256 b, - string memory errorMessage - ) internal pure returns (uint256) { + function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { unchecked { require(b > 0, errorMessage); return a % b; diff --git a/contracts/infra/ContractPublisher.sol b/contracts/infra/ContractPublisher.sol index 809992eb7..d3f5d883c 100644 --- a/contracts/infra/ContractPublisher.sol +++ b/contracts/infra/ContractPublisher.sol @@ -16,7 +16,7 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/metatx/ERC2771Context.sol"; import "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import "@openzeppelin/contracts/utils/Multicall.sol"; +import "../extension/Multicall.sol"; // ========== Internal imports ========== import { IContractPublisher } from "./interface/IContractPublisher.sol"; @@ -71,11 +71,9 @@ contract ContractPublisher is IContractPublisher, ERC2771Context, AccessControlE //////////////////////////////////////////////////////////////*/ /// @notice Returns the latest version of all contracts published by a publisher. - function getAllPublishedContracts(address _publisher) - external - view - returns (CustomContractInstance[] memory published) - { + function getAllPublishedContracts( + address _publisher + ) external view returns (CustomContractInstance[] memory published) { CustomContractInstance[] memory linkedData = prevPublisher.getAllPublishedContracts(_publisher); uint256 currentTotal = EnumerableSet.length(contractsOfPublisher[_publisher].contractIds); uint256 prevTotal = linkedData.length; @@ -93,11 +91,10 @@ contract ContractPublisher is IContractPublisher, ERC2771Context, AccessControlE } /// @notice Returns all versions of a published contract. - function getPublishedContractVersions(address _publisher, string memory _contractId) - external - view - returns (CustomContractInstance[] memory published) - { + function getPublishedContractVersions( + address _publisher, + string memory _contractId + ) external view returns (CustomContractInstance[] memory published) { CustomContractInstance[] memory linkedVersions = prevPublisher.getPublishedContractVersions( _publisher, _contractId @@ -121,11 +118,10 @@ contract ContractPublisher is IContractPublisher, ERC2771Context, AccessControlE } /// @notice Returns the latest version of a contract published by a publisher. - function getPublishedContract(address _publisher, string memory _contractId) - external - view - returns (CustomContractInstance memory published) - { + function getPublishedContract( + address _publisher, + string memory _contractId + ) external view returns (CustomContractInstance memory published) { published = contractsOfPublisher[_publisher].contracts[keccak256(bytes(_contractId))].latest; // if not found, check the previous publisher if (published.publishTimestamp == 0) { @@ -171,11 +167,10 @@ contract ContractPublisher is IContractPublisher, ERC2771Context, AccessControlE } /// @notice Lets a publisher unpublish a contract and all its versions. - function unpublishContract(address _publisher, string memory _contractId) - external - onlyPublisher(_publisher) - onlyUnpausedOrAdmin - { + function unpublishContract( + address _publisher, + string memory _contractId + ) external onlyPublisher(_publisher) onlyUnpausedOrAdmin { bytes32 contractIdInBytes = keccak256(bytes(_contractId)); bool removed = EnumerableSet.remove(contractsOfPublisher[_publisher].contractIds, contractIdInBytes); @@ -204,11 +199,9 @@ contract ContractPublisher is IContractPublisher, ERC2771Context, AccessControlE } /// @notice Retrieve the published metadata URI from a compiler metadata URI - function getPublishedUriFromCompilerUri(string memory compilerMetadataUri) - public - view - returns (string[] memory publishedMetadataUris) - { + function getPublishedUriFromCompilerUri( + string memory compilerMetadataUri + ) public view returns (string[] memory publishedMetadataUris) { string[] memory linkedUris = prevPublisher.getPublishedUriFromCompilerUri(compilerMetadataUri); uint256 prevTotal = linkedUris.length; uint256 currentTotal = compilerMetadataUriToPublishedMetadataUris[compilerMetadataUri].index; diff --git a/contracts/infra/TWFactory.sol b/contracts/infra/TWFactory.sol index 5682619fe..7d88d1636 100644 --- a/contracts/infra/TWFactory.sol +++ b/contracts/infra/TWFactory.sol @@ -12,15 +12,16 @@ pragma solidity ^0.8.11; // \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | // \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ -import "./TWRegistry.sol"; +import { TWRegistry } from "./TWRegistry.sol"; import "./interface/IThirdwebContract.sol"; import "../extension/interface/IContractFactory.sol"; -import "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; -import "@openzeppelin/contracts/metatx/ERC2771Context.sol"; -import "@openzeppelin/contracts/utils/Create2.sol"; -import "@openzeppelin/contracts/utils/Multicall.sol"; -import "@openzeppelin/contracts/proxy/Clones.sol"; +import { AccessControlEnumerable, Context } from "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; +import { ERC2771Context } from "@openzeppelin/contracts/metatx/ERC2771Context.sol"; +import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol"; +import { Multicall } from "../extension/Multicall.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol"; contract TWFactory is Multicall, ERC2771Context, AccessControlEnumerable, IContractFactory { /// @dev Only FACTORY_ROLE holders can approve/unapprove implementations for proxies to point to. @@ -62,11 +63,7 @@ contract TWFactory is Multicall, ERC2771Context, AccessControlEnumerable, IContr * @dev Deploys a proxy at a deterministic address by taking in `salt` as a parameter. * Proxy points to the latest version of the given contract type. */ - function deployProxyDeterministic( - bytes32 _type, - bytes memory _data, - bytes32 _salt - ) public returns (address) { + function deployProxyDeterministic(bytes32 _type, bytes memory _data, bytes32 _salt) public returns (address) { address _implementation = implementation[_type][currentVersion[_type]]; return deployProxyByImplementation(_implementation, _data, _salt); } diff --git a/contracts/infra/TWFee.sol b/contracts/infra/TWFee.sol index 4ee38b12c..430f79240 100644 --- a/contracts/infra/TWFee.sol +++ b/contracts/infra/TWFee.sol @@ -7,15 +7,15 @@ import "./TWFactory.sol"; import "./interface/ITWFee.sol"; import "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; -import "@openzeppelin/contracts/utils/Multicall.sol"; +import { Multicall } from "../extension/Multicall.sol"; import "@openzeppelin/contracts/metatx/ERC2771Context.sol"; interface IFeeTierPlacementExtension { /// @dev Returns the fee tier for a given proxy contract address and proxy deployer address. - function getFeeTier(address deployer, address proxy) - external - view - returns (uint128 tierId, uint128 validUntilTimestamp); + function getFeeTier( + address deployer, + address proxy + ) external view returns (uint128 tierId, uint128 validUntilTimestamp); } contract TWFee is ITWFee, Multicall, ERC2771Context, AccessControlEnumerable, IFeeTierPlacementExtension { @@ -68,12 +68,10 @@ contract TWFee is ITWFee, Multicall, ERC2771Context, AccessControlEnumerable, IF } /// @dev Returns the fee tier for a proxy deployer wallet or contract address. - function getFeeTier(address _deployer, address _proxy) - public - view - override - returns (uint128 tierId, uint128 validUntilTimestamp) - { + function getFeeTier( + address _deployer, + address _proxy + ) public view override returns (uint128 tierId, uint128 validUntilTimestamp) { Tier memory targetTier = tier[_proxy]; if (block.timestamp <= targetTier.validUntilTimestamp) { tierId = targetTier.id; diff --git a/contracts/infra/TWMinimalFactory.sol b/contracts/infra/TWMinimalFactory.sol index bfd48cb36..63b81b9ed 100644 --- a/contracts/infra/TWMinimalFactory.sol +++ b/contracts/infra/TWMinimalFactory.sol @@ -17,11 +17,7 @@ import "@openzeppelin/contracts/proxy/Clones.sol"; contract TWMinimalFactory { /// @dev Deploys a proxy that points to the given implementation. - constructor( - address _implementation, - bytes memory _data, - bytes32 _salt - ) payable { + constructor(address _implementation, bytes memory _data, bytes32 _salt) payable { address instance; bytes32 salthash = keccak256(abi.encodePacked(msg.sender, _salt)); assembly { diff --git a/contracts/infra/TWMultichainRegistry.sol b/contracts/infra/TWMultichainRegistry.sol index 86d20a90d..772b98674 100644 --- a/contracts/infra/TWMultichainRegistry.sol +++ b/contracts/infra/TWMultichainRegistry.sol @@ -14,7 +14,7 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; -import "@openzeppelin/contracts/utils/Multicall.sol"; +import "../extension/Multicall.sol"; import "@openzeppelin/contracts/metatx/ERC2771Context.sol"; import "./interface/ITWMultichainRegistry.sol"; @@ -37,12 +37,7 @@ contract TWMultichainRegistry is ITWMultichainRegistry, Multicall, ERC2771Contex } // slither-disable-next-line similar-names - function add( - address _deployer, - address _deployment, - uint256 _chainId, - string memory metadataUri - ) external { + function add(address _deployer, address _deployment, uint256 _chainId, string memory metadataUri) external { require(hasRole(OPERATOR_ROLE, _msgSender()) || _deployer == _msgSender(), "not operator or deployer."); bool added = deployments[_deployer][_chainId].add(_deployment); @@ -58,11 +53,7 @@ contract TWMultichainRegistry is ITWMultichainRegistry, Multicall, ERC2771Contex } // slither-disable-next-line similar-names - function remove( - address _deployer, - address _deployment, - uint256 _chainId - ) external { + function remove(address _deployer, address _deployment, uint256 _chainId) external { require(hasRole(OPERATOR_ROLE, _msgSender()) || _deployer == _msgSender(), "not operator or deployer."); bool removed = deployments[_deployer][_chainId].remove(_deployment); diff --git a/contracts/infra/TWRegistry.sol b/contracts/infra/TWRegistry.sol index fc117200a..c4f6667a3 100644 --- a/contracts/infra/TWRegistry.sol +++ b/contracts/infra/TWRegistry.sol @@ -14,7 +14,7 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; -import "@openzeppelin/contracts/utils/Multicall.sol"; +import "../extension/Multicall.sol"; import "@openzeppelin/contracts/metatx/ERC2771Context.sol"; contract TWRegistry is Multicall, ERC2771Context, AccessControlEnumerable { diff --git a/contracts/infra/TWStatelessFactory.sol b/contracts/infra/TWStatelessFactory.sol index c22c1368d..7db3b5684 100644 --- a/contracts/infra/TWStatelessFactory.sol +++ b/contracts/infra/TWStatelessFactory.sol @@ -15,7 +15,7 @@ pragma solidity ^0.8.11; import "../extension/interface/IContractFactory.sol"; import "@openzeppelin/contracts/metatx/ERC2771Context.sol"; -import "@openzeppelin/contracts/utils/Multicall.sol"; +import "../extension/Multicall.sol"; import "@openzeppelin/contracts/proxy/Clones.sol"; contract TWStatelessFactory is Multicall, ERC2771Context, IContractFactory { diff --git a/contracts/infra/forwarder/Forwarder.sol b/contracts/infra/forwarder/Forwarder.sol index a77c7eef8..ed680c92e 100644 --- a/contracts/infra/forwarder/Forwarder.sol +++ b/contracts/infra/forwarder/Forwarder.sol @@ -49,11 +49,10 @@ contract Forwarder is EIP712 { return _nonces[req.from] == req.nonce && signer == req.from; } - function execute(ForwardRequest calldata req, bytes calldata signature) - public - payable - returns (bool, bytes memory) - { + function execute( + ForwardRequest calldata req, + bytes calldata signature + ) public payable returns (bool, bytes memory) { require(verify(req, signature), "MinimalForwarder: signature does not match request"); _nonces[req.from] = req.nonce + 1; diff --git a/contracts/infra/forwarder/ForwarderChainlessDomain.sol b/contracts/infra/forwarder/ForwarderChainlessDomain.sol index c45fe6830..5b5f4b34f 100644 --- a/contracts/infra/forwarder/ForwarderChainlessDomain.sol +++ b/contracts/infra/forwarder/ForwarderChainlessDomain.sol @@ -62,11 +62,10 @@ contract ForwarderChainlessDomain is EIP712ChainlessDomain { return _nonces[req.from] == req.nonce && signer == req.from; } - function execute(ForwardRequest calldata req, bytes calldata signature) - public - payable - returns (bool, bytes memory) - { + function execute( + ForwardRequest calldata req, + bytes calldata signature + ) public payable returns (bool, bytes memory) { // require(req.chainid == block.chainid, "MinimalForwarder: invalid chainId"); require(verify(req, signature), "MinimalForwarder: signature does not match request"); _nonces[req.from] = req.nonce + 1; diff --git a/contracts/infra/interface/IContractPublisher.sol b/contracts/infra/interface/IContractPublisher.sol index 082d52527..27806668f 100644 --- a/contracts/infra/interface/IContractPublisher.sol +++ b/contracts/infra/interface/IContractPublisher.sol @@ -51,10 +51,9 @@ interface IContractPublisher { * * @return published An array of all contracts published by the publisher. */ - function getAllPublishedContracts(address publisher) - external - view - returns (CustomContractInstance[] memory published); + function getAllPublishedContracts( + address publisher + ) external view returns (CustomContractInstance[] memory published); /** * @notice Returns all versions of a published contract. @@ -64,10 +63,10 @@ interface IContractPublisher { * * @return published The desired contracts published by the publisher. */ - function getPublishedContractVersions(address publisher, string memory contractId) - external - view - returns (CustomContractInstance[] memory published); + function getPublishedContractVersions( + address publisher, + string memory contractId + ) external view returns (CustomContractInstance[] memory published); /** * @notice Returns the latest version of a contract published by a publisher. @@ -77,10 +76,10 @@ interface IContractPublisher { * * @return published The desired contract published by the publisher. */ - function getPublishedContract(address publisher, string memory contractId) - external - view - returns (CustomContractInstance memory published); + function getPublishedContract( + address publisher, + string memory contractId + ) external view returns (CustomContractInstance memory published); /** * @notice Let's an account publish a contract. @@ -123,8 +122,7 @@ interface IContractPublisher { /** * @notice Retrieve the published metadata URI from a compiler metadata URI. */ - function getPublishedUriFromCompilerUri(string memory compilerMetadataUri) - external - view - returns (string[] memory publishedMetadataUris); + function getPublishedUriFromCompilerUri( + string memory compilerMetadataUri + ) external view returns (string[] memory publishedMetadataUris); } diff --git a/contracts/infra/interface/ITWMultichainRegistry.sol b/contracts/infra/interface/ITWMultichainRegistry.sol index be5ba1080..c91ab1fc6 100644 --- a/contracts/infra/interface/ITWMultichainRegistry.sol +++ b/contracts/infra/interface/ITWMultichainRegistry.sol @@ -12,19 +12,10 @@ interface ITWMultichainRegistry { event Deleted(address indexed deployer, address indexed deployment, uint256 indexed chainId); /// @notice Add a deployment for a deployer. - function add( - address _deployer, - address _deployment, - uint256 _chainId, - string memory metadataUri - ) external; + function add(address _deployer, address _deployment, uint256 _chainId, string memory metadataUri) external; /// @notice Remove a deployment for a deployer. - function remove( - address _deployer, - address _deployment, - uint256 _chainId - ) external; + function remove(address _deployer, address _deployment, uint256 _chainId) external; /// @notice Get all deployments for a deployer. function getAll(address _deployer) external view returns (Deployment[] memory allDeployments); diff --git a/contracts/infra/interface/ITWRegistry.sol b/contracts/infra/interface/ITWRegistry.sol index 51754efff..78ebf554f 100644 --- a/contracts/infra/interface/ITWRegistry.sol +++ b/contracts/infra/interface/ITWRegistry.sol @@ -11,18 +11,10 @@ interface ITWRegistry { event Deleted(address indexed deployer, address indexed deployment, uint256 indexed chainId); /// @notice Add a deployment for a deployer. - function add( - address _deployer, - address _deployment, - uint256 _chainId - ) external; + function add(address _deployer, address _deployment, uint256 _chainId) external; /// @notice Remove a deployment for a deployer. - function remove( - address _deployer, - address _deployment, - uint256 _chainId - ) external; + function remove(address _deployer, address _deployment, uint256 _chainId) external; /// @notice Get all deployments for a deployer. function getAll(address _deployer) external view returns (Deployment[] memory allDeployments); diff --git a/contracts/infra/registry/entrypoint/TWMultichainRegistryRouter.sol b/contracts/infra/registry/entrypoint/TWMultichainRegistryRouter.sol index c2fa62655..03c88e335 100644 --- a/contracts/infra/registry/entrypoint/TWMultichainRegistryRouter.sol +++ b/contracts/infra/registry/entrypoint/TWMultichainRegistryRouter.sol @@ -35,10 +35,10 @@ contract TWMultichainRegistryRouter is PermissionsEnumerableLogic, ERC2771Contex Constructor + initializer logic //////////////////////////////////////////////////////////////*/ - constructor(address _pluginMap, address[] memory _trustedForwarders) - ERC2771ContextLogic(_trustedForwarders) - Router(_pluginMap) - { + constructor( + address _pluginMap, + address[] memory _trustedForwarders + ) ERC2771ContextLogic(_trustedForwarders) Router(_pluginMap) { _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); } diff --git a/contracts/infra/registry/registry-extension/TWMultichainRegistryLogic.sol b/contracts/infra/registry/registry-extension/TWMultichainRegistryLogic.sol index b5362fc5b..3a0159d23 100644 --- a/contracts/infra/registry/registry-extension/TWMultichainRegistryLogic.sol +++ b/contracts/infra/registry/registry-extension/TWMultichainRegistryLogic.sol @@ -36,12 +36,7 @@ contract TWMultichainRegistryLogic is ITWMultichainRegistry, ERC2771ContextConsu //////////////////////////////////////////////////////////////*/ // slither-disable-next-line similar-names - function add( - address _deployer, - address _deployment, - uint256 _chainId, - string memory metadataUri - ) external { + function add(address _deployer, address _deployment, uint256 _chainId, string memory metadataUri) external { require( PermissionsEnumerableLogic(address(this)).hasRole(OPERATOR_ROLE, _msgSender()) || _deployer == _msgSender(), "not operator or deployer." @@ -62,11 +57,7 @@ contract TWMultichainRegistryLogic is ITWMultichainRegistry, ERC2771ContextConsu } // slither-disable-next-line similar-names - function remove( - address _deployer, - address _deployment, - uint256 _chainId - ) external { + function remove(address _deployer, address _deployment, uint256 _chainId) external { require( PermissionsEnumerableLogic(address(this)).hasRole(OPERATOR_ROLE, _msgSender()) || _deployer == _msgSender(), "not operator or deployer." diff --git a/contracts/legacy-contracts/extension/DropSinglePhase1155_V1.sol b/contracts/legacy-contracts/extension/DropSinglePhase1155_V1.sol index ec1eb964e..003906bd7 100644 --- a/contracts/legacy-contracts/extension/DropSinglePhase1155_V1.sol +++ b/contracts/legacy-contracts/extension/DropSinglePhase1155_V1.sol @@ -5,10 +5,10 @@ pragma solidity ^0.8.0; import "./interface/IDropSinglePhase1155_V1.sol"; import "../../lib/MerkleProof.sol"; -import "../../lib/TWBitMaps.sol"; +import "../../lib/BitMaps.sol"; abstract contract DropSinglePhase1155_V1 is IDropSinglePhase1155_V1 { - using TWBitMaps for TWBitMaps.BitMap; + using BitMaps for BitMaps.BitMap; /*/////////////////////////////////////////////////////////////// Mappings @@ -30,7 +30,7 @@ abstract contract DropSinglePhase1155_V1 is IDropSinglePhase1155_V1 { * @dev Map from a claim condition uid to whether an address in an allowlist * has already claimed tokens i.e. used their place in the allowlist. */ - mapping(bytes32 => TWBitMaps.BitMap) private usedAllowlistSpot; + mapping(bytes32 => BitMaps.BitMap) private usedAllowlistSpot; /*/////////////////////////////////////////////////////////////// Drop logic @@ -207,11 +207,10 @@ abstract contract DropSinglePhase1155_V1 is IDropSinglePhase1155_V1 { } /// @dev Returns the timestamp for when a claimer is eligible for claiming NFTs again. - function getClaimTimestamp(uint256 _tokenId, address _claimer) - public - view - returns (uint256 lastClaimedAt, uint256 nextValidClaimTimestamp) - { + function getClaimTimestamp( + uint256 _tokenId, + address _claimer + ) public view returns (uint256 lastClaimedAt, uint256 nextValidClaimTimestamp) { lastClaimedAt = lastClaimTimestamp[conditionId[_tokenId]][_claimer]; unchecked { @@ -263,11 +262,7 @@ abstract contract DropSinglePhase1155_V1 is IDropSinglePhase1155_V1 { ) internal virtual; /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim( - address _to, - uint256 _tokenId, - uint256 _quantityBeingClaimed - ) internal virtual; + function _transferTokensOnClaim(address _to, uint256 _tokenId, uint256 _quantityBeingClaimed) internal virtual; function _canSetClaimConditions() internal view virtual returns (bool); } diff --git a/contracts/legacy-contracts/extension/DropSinglePhase_V1.sol b/contracts/legacy-contracts/extension/DropSinglePhase_V1.sol index 904a57957..571a0150b 100644 --- a/contracts/legacy-contracts/extension/DropSinglePhase_V1.sol +++ b/contracts/legacy-contracts/extension/DropSinglePhase_V1.sol @@ -5,10 +5,10 @@ pragma solidity ^0.8.0; import "./interface/IDropSinglePhase_V1.sol"; import "../../lib/MerkleProof.sol"; -import "../../lib/TWBitMaps.sol"; +import "../../lib/BitMaps.sol"; abstract contract DropSinglePhase_V1 is IDropSinglePhase_V1 { - using TWBitMaps for TWBitMaps.BitMap; + using BitMaps for BitMaps.BitMap; /*/////////////////////////////////////////////////////////////// State variables @@ -34,7 +34,7 @@ abstract contract DropSinglePhase_V1 is IDropSinglePhase_V1 { * @dev Map from a claim condition uid to whether an address in an allowlist * has already claimed tokens i.e. used their place in the allowlist. */ - mapping(bytes32 => TWBitMaps.BitMap) private usedAllowlistSpot; + mapping(bytes32 => BitMaps.BitMap) private usedAllowlistSpot; /*/////////////////////////////////////////////////////////////// Drop logic @@ -191,11 +191,9 @@ abstract contract DropSinglePhase_V1 is IDropSinglePhase_V1 { } /// @dev Returns the timestamp for when a claimer is eligible for claiming NFTs again. - function getClaimTimestamp(address _claimer) - public - view - returns (uint256 lastClaimedAt, uint256 nextValidClaimTimestamp) - { + function getClaimTimestamp( + address _claimer + ) public view returns (uint256 lastClaimedAt, uint256 nextValidClaimTimestamp) { lastClaimedAt = lastClaimTimestamp[conditionId][_claimer]; unchecked { @@ -245,10 +243,10 @@ abstract contract DropSinglePhase_V1 is IDropSinglePhase_V1 { ) internal virtual; /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - virtual - returns (uint256 startTokenId); + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal virtual returns (uint256 startTokenId); function _canSetClaimConditions() internal view virtual returns (bool); } diff --git a/contracts/legacy-contracts/extension/LazyMintWithTier_V1.sol b/contracts/legacy-contracts/extension/LazyMintWithTier_V1.sol index 141f6f39d..2acda2134 100644 --- a/contracts/legacy-contracts/extension/LazyMintWithTier_V1.sol +++ b/contracts/legacy-contracts/extension/LazyMintWithTier_V1.sol @@ -72,11 +72,9 @@ abstract contract LazyMintWithTier_V1 is ILazyMintWithTier, BatchMintMetadata_V1 } /// @notice Returns all metadata lazy minted for the given tier. - function _getMetadataInTier(string memory _tier) - private - view - returns (TokenRange[] memory tokens, string[] memory baseURIs) - { + function _getMetadataInTier( + string memory _tier + ) private view returns (TokenRange[] memory tokens, string[] memory baseURIs) { tokens = tokensInTier[_tier]; uint256 len = tokens.length; diff --git a/contracts/legacy-contracts/extension/PlatformFee_V1.sol b/contracts/legacy-contracts/extension/PlatformFee_V1.sol new file mode 100644 index 000000000..9014b7dca --- /dev/null +++ b/contracts/legacy-contracts/extension/PlatformFee_V1.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +/// @author thirdweb + +import "./interface/IPlatformFee_V1.sol"; + +/** + * @title Platform Fee + * @notice Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading + * the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic + * that uses information about platform fees, if desired. + */ + +abstract contract PlatformFee is IPlatformFee { + /// @dev The address that receives all platform fees from all sales. + address private platformFeeRecipient; + + /// @dev The % of primary sales collected as platform fees. + uint16 private platformFeeBps; + + /// @dev Fee type variants: percentage fee and flat fee + PlatformFeeType private platformFeeType; + + /// @dev The flat amount collected by the contract as fees on primary sales. + uint256 private flatPlatformFee; + + /// @dev Returns the platform fee recipient and bps. + function getPlatformFeeInfo() public view override returns (address, uint16) { + return (platformFeeRecipient, uint16(platformFeeBps)); + } + + /// @dev Returns the platform fee bps and recipient. + function getFlatPlatformFeeInfo() public view returns (address, uint256) { + return (platformFeeRecipient, flatPlatformFee); + } + + /// @dev Returns the platform fee bps and recipient. + function getPlatformFeeType() public view returns (PlatformFeeType) { + return platformFeeType; + } + + /** + * @notice Updates the platform fee recipient and bps. + * @dev Caller should be authorized to set platform fee info. + * See {_canSetPlatformFeeInfo}. + * Emits {PlatformFeeInfoUpdated Event}; See {_setupPlatformFeeInfo}. + * + * @param _platformFeeRecipient Address to be set as new platformFeeRecipient. + * @param _platformFeeBps Updated platformFeeBps. + */ + function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external override { + if (!_canSetPlatformFeeInfo()) { + revert("Not authorized"); + } + _setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps); + } + + /// @dev Sets the platform fee recipient and bps + function _setupPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) internal { + if (_platformFeeBps > 10_000) { + revert("Exceeds max bps"); + } + + platformFeeBps = uint16(_platformFeeBps); + platformFeeRecipient = _platformFeeRecipient; + + emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps); + } + + /// @notice Lets a module admin set a flat fee on primary sales. + function setFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) external { + if (!_canSetPlatformFeeInfo()) { + revert("Not authorized"); + } + + _setupFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee); + } + + /// @dev Sets a flat fee on primary sales. + function _setupFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) internal { + flatPlatformFee = _flatFee; + platformFeeRecipient = _platformFeeRecipient; + + emit FlatPlatformFeeUpdated(_platformFeeRecipient, _flatFee); + } + + /// @notice Lets a module admin set platform fee type. + function setPlatformFeeType(PlatformFeeType _feeType) external { + if (!_canSetPlatformFeeInfo()) { + revert("Not authorized"); + } + _setupPlatformFeeType(_feeType); + } + + /// @dev Sets platform fee type. + function _setupPlatformFeeType(PlatformFeeType _feeType) internal { + platformFeeType = _feeType; + + emit PlatformFeeTypeUpdated(_feeType); + } + + /// @dev Returns whether platform fee info can be set in the given execution context. + function _canSetPlatformFeeInfo() internal view virtual returns (bool); +} diff --git a/contracts/legacy-contracts/extension/PrimarySale_V1.sol b/contracts/legacy-contracts/extension/PrimarySale_V1.sol new file mode 100644 index 000000000..8536c9724 --- /dev/null +++ b/contracts/legacy-contracts/extension/PrimarySale_V1.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +/// @author thirdweb + +import "./interface/IPrimarySale_V1.sol"; + +/** + * @title Primary Sale + * @notice Thirdweb's `PrimarySale` is a contract extension to be used with any base contract. It exposes functions for setting and reading + * the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about + * primary sales, if desired. + */ + +abstract contract PrimarySale is IPrimarySale { + /// @dev The address that receives all primary sales value. + address private recipient; + + /// @dev Returns primary sale recipient address. + function primarySaleRecipient() public view override returns (address) { + return recipient; + } + + /** + * @notice Updates primary sale recipient. + * @dev Caller should be authorized to set primary sales info. + * See {_canSetPrimarySaleRecipient}. + * Emits {PrimarySaleRecipientUpdated Event}; See {_setupPrimarySaleRecipient}. + * + * @param _saleRecipient Address to be set as new recipient of primary sales. + */ + function setPrimarySaleRecipient(address _saleRecipient) external override { + if (!_canSetPrimarySaleRecipient()) { + revert("Not authorized"); + } + _setupPrimarySaleRecipient(_saleRecipient); + } + + /// @dev Lets a contract admin set the recipient for all primary sales. + function _setupPrimarySaleRecipient(address _saleRecipient) internal { + recipient = _saleRecipient; + emit PrimarySaleRecipientUpdated(_saleRecipient); + } + + /// @dev Returns whether primary sale recipient can be set in the given execution context. + function _canSetPrimarySaleRecipient() internal view virtual returns (bool); +} diff --git a/contracts/legacy-contracts/extension/interface/IClaimCondition_V1.sol b/contracts/legacy-contracts/extension/interface/IClaimCondition_V1.sol index b706821bd..7615ded78 100644 --- a/contracts/legacy-contracts/extension/interface/IClaimCondition_V1.sol +++ b/contracts/legacy-contracts/extension/interface/IClaimCondition_V1.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; /// @author thirdweb -import "../../../lib/TWBitMaps.sol"; +import "../../../lib/BitMaps.sol"; /** * Thirdweb's 'Drop' contracts are distribution mechanisms for tokens. diff --git a/contracts/legacy-contracts/extension/interface/IDropSinglePhase1155_V1.sol b/contracts/legacy-contracts/extension/interface/IDropSinglePhase1155_V1.sol index e0234f546..0cf271b46 100644 --- a/contracts/legacy-contracts/extension/interface/IDropSinglePhase1155_V1.sol +++ b/contracts/legacy-contracts/extension/interface/IDropSinglePhase1155_V1.sol @@ -54,9 +54,5 @@ interface IDropSinglePhase1155_V1 is IClaimCondition_V1 { * * @param tokenId The tokenId for which to set the relevant claim condition. */ - function setClaimConditions( - uint256 tokenId, - ClaimCondition calldata phase, - bool resetClaimEligibility - ) external; + function setClaimConditions(uint256 tokenId, ClaimCondition calldata phase, bool resetClaimEligibility) external; } diff --git a/contracts/legacy-contracts/extension/interface/IPlatformFee_V1.sol b/contracts/legacy-contracts/extension/interface/IPlatformFee_V1.sol new file mode 100644 index 000000000..1a1fc778a --- /dev/null +++ b/contracts/legacy-contracts/extension/interface/IPlatformFee_V1.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +/// @author thirdweb + +/** + * Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading + * the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic + * that uses information about platform fees, if desired. + */ + +interface IPlatformFee { + /// @dev Fee type variants: percentage fee and flat fee + enum PlatformFeeType { + Bps, + Flat + } + + /// @dev Returns the platform fee bps and recipient. + function getPlatformFeeInfo() external view returns (address, uint16); + + /// @dev Lets a module admin update the fees on primary sales. + function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external; + + /// @dev Emitted when fee on primary sales is updated. + event PlatformFeeInfoUpdated(address indexed platformFeeRecipient, uint256 platformFeeBps); + + /// @dev Emitted when the flat platform fee is updated. + event FlatPlatformFeeUpdated(address platformFeeRecipient, uint256 flatFee); + + /// @dev Emitted when the platform fee type is updated. + event PlatformFeeTypeUpdated(PlatformFeeType feeType); +} diff --git a/contracts/legacy-contracts/extension/interface/IPrimarySale_V1.sol b/contracts/legacy-contracts/extension/interface/IPrimarySale_V1.sol new file mode 100644 index 000000000..6ca726842 --- /dev/null +++ b/contracts/legacy-contracts/extension/interface/IPrimarySale_V1.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +/// @author thirdweb + +/** + * Thirdweb's `Primary` is a contract extension to be used with any base contract. It exposes functions for setting and reading + * the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about + * primary sales, if desired. + */ + +interface IPrimarySale { + /// @dev The adress that receives all primary sales value. + function primarySaleRecipient() external view returns (address); + + /// @dev Lets a module admin set the default recipient of all primary sales. + function setPrimarySaleRecipient(address _saleRecipient) external; + + /// @dev Emitted when a new sale recipient is set. + event PrimarySaleRecipientUpdated(address indexed recipient); +} diff --git a/contracts/legacy-contracts/interface/ISignatureMintERC721_V1.sol b/contracts/legacy-contracts/interface/ISignatureMintERC721_V1.sol index d8703429c..e22061e86 100644 --- a/contracts/legacy-contracts/interface/ISignatureMintERC721_V1.sol +++ b/contracts/legacy-contracts/interface/ISignatureMintERC721_V1.sol @@ -6,13 +6,13 @@ pragma solidity ^0.8.11; import "../../prebuilts/interface/token/ITokenERC721.sol"; interface ISignatureMintERC721_V1 { - function mintWithSignature(ITokenERC721.MintRequest calldata _req, bytes calldata _signature) - external - payable - returns (uint256 tokenIdMinted); + function mintWithSignature( + ITokenERC721.MintRequest calldata _req, + bytes calldata _signature + ) external payable returns (uint256 tokenIdMinted); - function verify(ITokenERC721.MintRequest calldata _req, bytes calldata _signature) - external - view - returns (bool, address); + function verify( + ITokenERC721.MintRequest calldata _req, + bytes calldata _signature + ) external view returns (bool, address); } diff --git a/contracts/legacy-contracts/interface/drop/IDropERC1155_V2.sol b/contracts/legacy-contracts/interface/drop/IDropERC1155_V2.sol index 83f1e89b6..9184580db 100644 --- a/contracts/legacy-contracts/interface/drop/IDropERC1155_V2.sol +++ b/contracts/legacy-contracts/interface/drop/IDropERC1155_V2.sol @@ -92,9 +92,5 @@ interface IDropERC1155_V2 is IERC1155Upgradeable, IDropClaimCondition_V2 { * `limitMerkleProofClaim` values when setting new * claim conditions. */ - function setClaimConditions( - uint256 tokenId, - ClaimCondition[] calldata phases, - bool resetClaimEligibility - ) external; + function setClaimConditions(uint256 tokenId, ClaimCondition[] calldata phases, bool resetClaimEligibility) external; } diff --git a/contracts/legacy-contracts/interface/drop/IDropERC721_V3.sol b/contracts/legacy-contracts/interface/drop/IDropERC721_V3.sol index ad76fe188..7148c2b4d 100644 --- a/contracts/legacy-contracts/interface/drop/IDropERC721_V3.sol +++ b/contracts/legacy-contracts/interface/drop/IDropERC721_V3.sol @@ -63,11 +63,7 @@ interface IDropERC721_V3 is IERC721Upgradeable, IDropClaimCondition_V2 { * result of encrypting the URI of the NFTs in the revealed * state. */ - function lazyMint( - uint256 amount, - string calldata baseURIForTokens, - bytes calldata encryptedBaseURI - ) external; + function lazyMint(uint256 amount, string calldata baseURIForTokens, bytes calldata encryptedBaseURI) external; /** * @notice Lets an account claim a given quantity of NFTs. diff --git a/contracts/legacy-contracts/pre-builts/DropERC1155_V2.sol b/contracts/legacy-contracts/pre-builts/DropERC1155_V2.sol index b8cecfaac..a02fcdede 100644 --- a/contracts/legacy-contracts/pre-builts/DropERC1155_V2.sol +++ b/contracts/legacy-contracts/pre-builts/DropERC1155_V2.sol @@ -10,7 +10,7 @@ import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/BitMapsUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; @@ -44,7 +44,7 @@ contract DropERC1155_V2 is IPlatformFee, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, AccessControlEnumerableUpgradeable, ERC1155Upgradeable, IDropERC1155_V2 @@ -209,7 +209,9 @@ contract DropERC1155_V2 is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) + function supportsInterface( + bytes4 interfaceId + ) public view virtual @@ -220,12 +222,10 @@ contract DropERC1155_V2 is } /// @dev Returns the royalty recipient and amount, given a tokenId and sale price. - function royaltyInfo(uint256 tokenId, uint256 salePrice) - external - view - virtual - returns (address receiver, uint256 royaltyAmount) - { + function royaltyInfo( + uint256 tokenId, + uint256 salePrice + ) external view virtual returns (address receiver, uint256 royaltyAmount) { (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId); receiver = recipient; royaltyAmount = (salePrice * bps) / MAX_BPS; @@ -558,11 +558,10 @@ contract DropERC1155_V2 is } /// @dev Returns the claim condition at the given uid. - function getClaimConditionById(uint256 _tokenId, uint256 _conditionId) - external - view - returns (ClaimCondition memory condition) - { + function getClaimConditionById( + uint256 _tokenId, + uint256 _conditionId + ) external view returns (ClaimCondition memory condition) { condition = claimCondition[_tokenId].phases[_conditionId]; } @@ -605,10 +604,10 @@ contract DropERC1155_V2 is } /// @dev Lets a contract admin update the default royalty recipient and bps. - function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setDefaultRoyaltyInfo( + address _royaltyRecipient, + uint256 _royaltyBps + ) external onlyRole(DEFAULT_ADMIN_ROLE) { require(_royaltyBps <= MAX_BPS, "exceed royalty bps"); royaltyRecipient = _royaltyRecipient; @@ -631,10 +630,10 @@ contract DropERC1155_V2 is } /// @dev Lets a contract admin update the platform fee recipient and bps - function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setPlatformFeeInfo( + address _platformFeeRecipient, + uint256 _platformFeeBps + ) external onlyRole(DEFAULT_ADMIN_ROLE) { require(_platformFeeBps <= MAX_BPS, "bps <= 10000."); platformFeeBps = uint16(_platformFeeBps); @@ -660,11 +659,7 @@ contract DropERC1155_V2 is //////////////////////////////////////////////////////////////*/ /// @dev Lets a token owner burn the tokens they own (i.e. destroy for good) - function burn( - address account, - uint256 id, - uint256 value - ) public virtual { + function burn(address account, uint256 id, uint256 value) public virtual { require( account == _msgSender() || isApprovedForAll(account, _msgSender()), "ERC1155: caller is not owner nor approved." @@ -674,11 +669,7 @@ contract DropERC1155_V2 is } /// @dev Lets a token owner burn multiple tokens they own at once (i.e. destroy for good) - function burnBatch( - address account, - uint256[] memory ids, - uint256[] memory values - ) public virtual { + function burnBatch(address account, uint256[] memory ids, uint256[] memory values) public virtual { require( account == _msgSender() || isApprovedForAll(account, _msgSender()), "ERC1155: caller is not owner nor approved." diff --git a/contracts/legacy-contracts/pre-builts/DropERC20_V2.sol b/contracts/legacy-contracts/pre-builts/DropERC20_V2.sol index 5960927ae..2b36a8632 100644 --- a/contracts/legacy-contracts/pre-builts/DropERC20_V2.sol +++ b/contracts/legacy-contracts/pre-builts/DropERC20_V2.sol @@ -12,7 +12,7 @@ import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgrad import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/BitMapsUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; // ========== Internal imports ========== @@ -38,7 +38,7 @@ contract DropERC20_V2 is IPlatformFee, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, AccessControlEnumerableUpgradeable, ERC20BurnableUpgradeable, ERC20VotesUpgradeable, @@ -139,13 +139,9 @@ contract DropERC20_V2 is //////////////////////////////////////////////////////////////*/ /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(AccessControlEnumerableUpgradeable) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(AccessControlEnumerableUpgradeable) returns (bool) { return super.supportsInterface(interfaceId); } @@ -158,11 +154,7 @@ contract DropERC20_V2 is } /// @dev Runs on every transfer. - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal override(ERC20Upgradeable) { + function _beforeTokenTransfer(address from, address to, uint256 amount) internal override(ERC20Upgradeable) { super._beforeTokenTransfer(from, to, amount); if (!hasRole(TRANSFER_ROLE, address(0)) && from != address(0) && to != address(0)) { @@ -236,10 +228,10 @@ contract DropERC20_V2 is } /// @dev Lets a contract admin (account with `DEFAULT_ADMIN_ROLE`) set claim conditions. - function setClaimConditions(ClaimCondition[] calldata _phases, bool _resetClaimEligibility) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setClaimConditions( + ClaimCondition[] calldata _phases, + bool _resetClaimEligibility + ) external onlyRole(DEFAULT_ADMIN_ROLE) { uint256 existingStartIndex = claimCondition.currentStartId; uint256 existingPhaseCount = claimCondition.count; @@ -303,11 +295,7 @@ contract DropERC20_V2 is } /// @dev Collects and distributes the primary sale value of tokens being claimed. - function collectClaimPrice( - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal { + function collectClaimPrice(uint256 _quantityToClaim, address _currency, uint256 _pricePerToken) internal { if (_pricePerToken == 0) { return; } @@ -327,11 +315,7 @@ contract DropERC20_V2 is } /// @dev Transfers the tokens being claimed. - function transferClaimedTokens( - address _to, - uint256 _conditionId, - uint256 _quantityBeingClaimed - ) internal { + function transferClaimedTokens(address _to, uint256 _conditionId, uint256 _quantityBeingClaimed) internal { // Update the supply minted under mint condition. claimCondition.phases[_conditionId].supplyClaimed += _quantityBeingClaimed; @@ -425,11 +409,10 @@ contract DropERC20_V2 is } /// @dev Returns the timestamp for when a claimer is eligible for claiming tokens again. - function getClaimTimestamp(uint256 _conditionId, address _claimer) - public - view - returns (uint256 lastClaimTimestamp, uint256 nextValidClaimTimestamp) - { + function getClaimTimestamp( + uint256 _conditionId, + address _claimer + ) public view returns (uint256 lastClaimTimestamp, uint256 nextValidClaimTimestamp) { lastClaimTimestamp = claimCondition.limitLastClaimTimestamp[_conditionId][_claimer]; if (lastClaimTimestamp != 0) { @@ -484,10 +467,10 @@ contract DropERC20_V2 is } /// @dev Lets a contract admin update the platform fee recipient and bps - function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setPlatformFeeInfo( + address _platformFeeRecipient, + uint256 _platformFeeBps + ) external onlyRole(DEFAULT_ADMIN_ROLE) { require(_platformFeeBps <= MAX_BPS, "bps <= 10000."); platformFeeBps = uint64(_platformFeeBps); diff --git a/contracts/legacy-contracts/pre-builts/DropERC721_V3.sol b/contracts/legacy-contracts/pre-builts/DropERC721_V3.sol index 2e4dd3591..d8d0afff4 100644 --- a/contracts/legacy-contracts/pre-builts/DropERC721_V3.sol +++ b/contracts/legacy-contracts/pre-builts/DropERC721_V3.sol @@ -12,7 +12,7 @@ import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/BitMapsUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; // ========== Internal imports ========== @@ -42,7 +42,7 @@ contract DropERC721_V3 is IPlatformFee, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, AccessControlEnumerableUpgradeable, ERC721EnumerableUpgradeable, IDropERC721_V3 @@ -206,7 +206,9 @@ contract DropERC721_V3 is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) + function supportsInterface( + bytes4 interfaceId + ) public view virtual @@ -217,12 +219,10 @@ contract DropERC721_V3 is } /// @dev Returns the royalty recipient and amount, given a tokenId and sale price. - function royaltyInfo(uint256 tokenId, uint256 salePrice) - external - view - virtual - returns (address receiver, uint256 royaltyAmount) - { + function royaltyInfo( + uint256 tokenId, + uint256 salePrice + ) external view virtual returns (address receiver, uint256 royaltyAmount) { (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId); receiver = recipient; royaltyAmount = (salePrice * bps) / MAX_BPS; @@ -260,11 +260,10 @@ contract DropERC721_V3 is } /// @dev Lets an account with `MINTER_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs. - function reveal(uint256 index, bytes calldata _key) - external - onlyRole(MINTER_ROLE) - returns (string memory revealedURI) - { + function reveal( + uint256 index, + bytes calldata _key + ) external onlyRole(MINTER_ROLE) returns (string memory revealedURI) { require(index < baseURIIndices.length, "invalid index."); uint256 _index = baseURIIndices[index]; @@ -389,10 +388,10 @@ contract DropERC721_V3 is } /// @dev Lets a contract admin (account with `DEFAULT_ADMIN_ROLE`) set claim conditions. - function setClaimConditions(ClaimCondition[] calldata _phases, bool _resetClaimEligibility) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setClaimConditions( + ClaimCondition[] calldata _phases, + bool _resetClaimEligibility + ) external onlyRole(DEFAULT_ADMIN_ROLE) { uint256 existingStartIndex = claimCondition.currentStartId; uint256 existingPhaseCount = claimCondition.count; @@ -453,11 +452,7 @@ contract DropERC721_V3 is } /// @dev Collects and distributes the primary sale value of NFTs being claimed. - function collectClaimPrice( - uint256 _quantityToClaim, - address _currency, - uint256 _pricePerToken - ) internal { + function collectClaimPrice(uint256 _quantityToClaim, address _currency, uint256 _pricePerToken) internal { if (_pricePerToken == 0) { return; } @@ -474,11 +469,7 @@ contract DropERC721_V3 is } /// @dev Transfers the NFTs being claimed. - function transferClaimedTokens( - address _to, - uint256 _conditionId, - uint256 _quantityBeingClaimed - ) internal { + function transferClaimedTokens(address _to, uint256 _conditionId, uint256 _quantityBeingClaimed) internal { // Update the supply minted under mint condition. claimCondition.phases[_conditionId].supplyClaimed += _quantityBeingClaimed; @@ -598,11 +589,10 @@ contract DropERC721_V3 is } /// @dev Returns the timestamp for when a claimer is eligible for claiming NFTs again. - function getClaimTimestamp(uint256 _conditionId, address _claimer) - public - view - returns (uint256 lastClaimTimestamp, uint256 nextValidClaimTimestamp) - { + function getClaimTimestamp( + uint256 _conditionId, + address _claimer + ) public view returns (uint256 lastClaimTimestamp, uint256 nextValidClaimTimestamp) { lastClaimTimestamp = claimCondition.limitLastClaimTimestamp[_conditionId][_claimer]; unchecked { @@ -655,10 +645,10 @@ contract DropERC721_V3 is } /// @dev Lets a contract admin update the default royalty recipient and bps. - function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setDefaultRoyaltyInfo( + address _royaltyRecipient, + uint256 _royaltyBps + ) external onlyRole(DEFAULT_ADMIN_ROLE) { require(_royaltyBps <= MAX_BPS, "> MAX_BPS"); royaltyRecipient = _royaltyRecipient; @@ -681,10 +671,10 @@ contract DropERC721_V3 is } /// @dev Lets a contract admin update the platform fee recipient and bps - function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setPlatformFeeInfo( + address _platformFeeRecipient, + uint256 _platformFeeBps + ) external onlyRole(DEFAULT_ADMIN_ROLE) { require(_platformFeeBps <= MAX_BPS, "> MAX_BPS."); platformFeeBps = uint16(_platformFeeBps); @@ -722,9 +712,10 @@ contract DropERC721_V3 is function _beforeTokenTransfer( address from, address to, - uint256 tokenId + uint256 tokenId, + uint256 batchSize ) internal virtual override(ERC721EnumerableUpgradeable) { - super._beforeTokenTransfer(from, to, tokenId); + super._beforeTokenTransfer(from, to, tokenId, batchSize); // 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)) { diff --git a/contracts/legacy-contracts/pre-builts/SignatureDrop_V4.sol b/contracts/legacy-contracts/pre-builts/SignatureDrop_V4.sol index 7bd07026a..a49c089cc 100644 --- a/contracts/legacy-contracts/pre-builts/SignatureDrop_V4.sol +++ b/contracts/legacy-contracts/pre-builts/SignatureDrop_V4.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.11; // ========== External imports ========== -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; @@ -42,7 +42,7 @@ contract SignatureDrop_V4 is DropSinglePhase_V1, SignatureMintERC721Upgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, ERC721AUpgradeable { using StringsUpgradeable for uint256; @@ -114,13 +114,9 @@ contract SignatureDrop_V4 is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721AUpgradeable, IERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721AUpgradeable, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId; } @@ -156,11 +152,10 @@ contract SignatureDrop_V4 is } /// @dev Lets an account with `MINTER_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs. - function reveal(uint256 _index, bytes calldata _key) - external - onlyRole(minterRole) - returns (string memory revealedURI) - { + function reveal( + uint256 _index, + bytes calldata _key + ) external onlyRole(minterRole) returns (string memory revealedURI) { uint256 batchId = getBatchIdAtIndex(_index); revealedURI = getRevealURI(batchId, _key); @@ -175,11 +170,10 @@ contract SignatureDrop_V4 is //////////////////////////////////////////////////////////////*/ /// @dev Claim lazy minted tokens via signature. - function mintWithSignature(MintRequest calldata _req, bytes calldata _signature) - external - payable - returns (address signer) - { + function mintWithSignature( + MintRequest calldata _req, + bytes calldata _signature + ) external payable returns (address signer) { uint256 tokenIdToMint = _currentIndex; if (tokenIdToMint + _req.quantity > nextTokenIdToLazyMint) { revert("Not enough tokens"); @@ -251,11 +245,10 @@ contract SignatureDrop_V4 is } /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - override - returns (uint256 startTokenId) - { + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal override returns (uint256 startTokenId) { startTokenId = _currentIndex; _safeMint(_to, _quantityBeingClaimed); } diff --git a/contracts/legacy-contracts/smart-wallet/interface/IAccountPermissions_V1.sol b/contracts/legacy-contracts/smart-wallet/interface/IAccountPermissions_V1.sol index 635d382b5..3a2124861 100644 --- a/contracts/legacy-contracts/smart-wallet/interface/IAccountPermissions_V1.sol +++ b/contracts/legacy-contracts/smart-wallet/interface/IAccountPermissions_V1.sol @@ -98,10 +98,10 @@ interface IAccountPermissions_V1 { function getAllAdmins() external view returns (address[] memory admins); /// @dev Verifies that a request is signed by an authorized account. - function verifySignerPermissionRequest(SignerPermissionRequest calldata req, bytes calldata signature) - external - view - returns (bool success, address signer); + function verifySignerPermissionRequest( + SignerPermissionRequest calldata req, + bytes calldata signature + ) external view returns (bool success, address signer); /*/////////////////////////////////////////////////////////////// External functions diff --git a/contracts/lib/Address.sol b/contracts/lib/Address.sol new file mode 100644 index 000000000..41c55c188 --- /dev/null +++ b/contracts/lib/Address.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +/// @author thirdweb + +library Address { + /** + * @dev Returns whether an address is a smart contract. + * + * `account` MAY NOT be a smart contract when this function returns `true` + * Other than EOAs, `isContract` will return `false` for: + * + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * - a contract in construction (since the code is only stored at the end of + * the constructor execution) + */ + function isContract(address account) internal view returns (bool) { + return account.code.length > 0; + } + + /// @dev Sends `amount` of wei to `recipient`. + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + (bool success, ) = recipient.call{ value: amount }(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /// @dev Performs a low-level call on `target` with `data`. + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /// @dev Performs a call on `target` with `data`, with `errorMessage` as a fallback + /// revert reason when `target` reverts. + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /// @dev Performs a low-level call on `target` with `data` and `value`. + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /// @dev Performs a static call on `target` with `data` and `value`, with `errorMessage` as a fallback + /// revert reason when `target` reverts. + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + require(isContract(target) && !isContract(msg.sender), "Address: invalid call"); + + (bool success, bytes memory returndata) = target.call{ value: value }(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /// @dev Performs a static call on `target` with `data`. + function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { + return functionStaticCall(target, data, "Address: low-level static call failed"); + } + + /// @dev Performs a static call on `target` with `data`, with `errorMessage` as a fallback + /// revert reason when `target` reverts. + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + require(isContract(target) && !isContract(msg.sender), "Address: invalid static call"); + + (bool success, bytes memory returndata) = target.staticcall(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /// @dev Performs a delegate call on `target` with `data`. + function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { + return functionDelegateCall(target, data, "Address: low-level delegate call failed"); + } + + /// @dev Performs a delegate call on `target` with `data`, with `errorMessage` as a fallback + /// revert reason when `target` reverts. + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + require(isContract(target) && !isContract(msg.sender), "Address: invalid delegate call"); + + (bool success, bytes memory returndata) = target.delegatecall(data); + return verifyCallResult(success, returndata, errorMessage); + } + + /// @dev Verifies that a low level call was successful. + function verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) internal pure returns (bytes memory) { + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} diff --git a/contracts/lib/TWBitMaps.sol b/contracts/lib/BitMaps.sol similarity index 89% rename from contracts/lib/TWBitMaps.sol rename to contracts/lib/BitMaps.sol index fde0a1030..00336dc07 100644 --- a/contracts/lib/TWBitMaps.sol +++ b/contracts/lib/BitMaps.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Apache 2.0 +// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; /// @author thirdweb @@ -7,7 +7,7 @@ pragma solidity ^0.8.0; * @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential. * Largely inspired by Uniswap's [merkle-distributor](https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol). */ -library TWBitMaps { +library BitMaps { struct BitMap { mapping(uint256 => uint256) _data; } @@ -24,11 +24,7 @@ library TWBitMaps { /** * @dev Sets the bit at `index` to the boolean `value`. */ - function setTo( - BitMap storage bitmap, - uint256 index, - bool value - ) internal { + function setTo(BitMap storage bitmap, uint256 index, bool value) internal { if (value) { set(bitmap, index); } else { diff --git a/contracts/lib/BytesLib.sol b/contracts/lib/BytesLib.sol index 5fe71410b..6cc1ebdaf 100644 --- a/contracts/lib/BytesLib.sol +++ b/contracts/lib/BytesLib.sol @@ -1,15 +1,8 @@ -// SPDX-License-Identifier: Apache 2.0 -/* - * @title Solidity Bytes Arrays Utils - * @author Gonçalo Sá - * - * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. - * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. - * - * Credits: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol - */ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; -pragma solidity >=0.8.0 <0.9.0; +/// @author thirdweb +/// Credits: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol library BytesLib { function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { diff --git a/contracts/lib/CurrencyTransferLib.sol b/contracts/lib/CurrencyTransferLib.sol index 32a46f7e3..1cb9cf8d9 100644 --- a/contracts/lib/CurrencyTransferLib.sol +++ b/contracts/lib/CurrencyTransferLib.sol @@ -5,8 +5,7 @@ pragma solidity ^0.8.0; // Helper interfaces import { IWETH } from "../infra/interface/IWETH.sol"; - -import "../external-deps/openzeppelin/token/ERC20/utils/SafeERC20.sol"; +import { SafeERC20, IERC20 } from "../external-deps/openzeppelin/token/ERC20/utils/SafeERC20.sol"; library CurrencyTransferLib { using SafeERC20 for IERC20; @@ -15,12 +14,7 @@ library CurrencyTransferLib { address public constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; /// @dev Transfers a given amount of currency. - function transferCurrency( - address _currency, - address _from, - address _to, - uint256 _amount - ) internal { + function transferCurrency(address _currency, address _from, address _to, uint256 _amount) internal { if (_amount == 0) { return; } @@ -62,12 +56,7 @@ library CurrencyTransferLib { } /// @dev Transfer `amount` of ERC20 token from `from` to `to`. - function safeTransferERC20( - address _currency, - address _from, - address _to, - uint256 _amount - ) internal { + function safeTransferERC20(address _currency, address _from, address _to, uint256 _amount) internal { if (_from == _to) { return; } @@ -88,11 +77,7 @@ library CurrencyTransferLib { } /// @dev Transfers `amount` of native token to `to`. (With native token wrapping) - function safeTransferNativeTokenWithWrapper( - address to, - uint256 value, - address _nativeTokenWrapper - ) internal { + function safeTransferNativeTokenWithWrapper(address to, uint256 value, address _nativeTokenWrapper) internal { // solhint-disable avoid-low-level-calls // slither-disable-next-line low-level-calls (bool success, ) = to.call{ value: value }(""); diff --git a/contracts/lib/MerkleProof.sol b/contracts/lib/MerkleProof.sol index f5df67ee3..c71db5cfe 100644 --- a/contracts/lib/MerkleProof.sol +++ b/contracts/lib/MerkleProof.sol @@ -3,29 +3,8 @@ pragma solidity ^0.8.0; /// @author thirdweb -/** - * @dev These functions deal with verification of Merkle Trees proofs. - * - * The proofs can be generated using the JavaScript library - * https://github.com/miguelmota/merkletreejs[merkletreejs]. - * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled. - * - * See `test/utils/cryptography/MerkleProof.test.js` for some examples. - * - * Source: https://github.com/ensdomains/governance/blob/master/contracts/MerkleProof.sol - */ library MerkleProof { - /** - * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree - * defined by `root`. For this, a `proof` must be provided, containing - * sibling hashes on the branch from the leaf to the root of the tree. Each - * pair of leaves and each pair of pre-images are assumed to be sorted. - */ - function verify( - bytes32[] memory proof, - bytes32 root, - bytes32 leaf - ) internal pure returns (bool, uint256) { + function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool, uint256) { bytes32 computedHash = leaf; uint256 index = 0; diff --git a/contracts/lib/NFTMetadataRendererLib.sol b/contracts/lib/NFTMetadataRenderer.sol similarity index 94% rename from contracts/lib/NFTMetadataRendererLib.sol rename to contracts/lib/NFTMetadataRenderer.sol index 0cc7ff845..4fd657159 100644 --- a/contracts/lib/NFTMetadataRendererLib.sol +++ b/contracts/lib/NFTMetadataRenderer.sol @@ -1,12 +1,12 @@ -// SPDX-License-Identifier: Apache 2.0 -pragma solidity ^0.8.10; +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; /* solhint-disable quotes */ /// @author thirdweb /// credits: Zora -import "./TWStrings.sol"; +import "./Strings.sol"; import "../external-deps/openzeppelin/utils/Base64.sol"; /// NFT metadata library for rendering metadata associated with editions @@ -49,14 +49,14 @@ library NFTMetadataRenderer { '{"name": "', name, " ", - TWStrings.toString(tokenOfEdition), + Strings.toString(tokenOfEdition), '", "', 'description": "', description, '", "', mediaData, 'properties": {"number": ', - TWStrings.toString(tokenOfEdition), + Strings.toString(tokenOfEdition), ', "name": "', name, '"}}' diff --git a/contracts/lib/StorageSlot.sol b/contracts/lib/StorageSlot.sol new file mode 100644 index 000000000..9fe0bb02e --- /dev/null +++ b/contracts/lib/StorageSlot.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +/// @author thirdweb + +library StorageSlot { + struct AddressSlot { + address value; + } + + struct BooleanSlot { + bool value; + } + + struct Bytes32Slot { + bytes32 value; + } + + struct Uint256Slot { + uint256 value; + } + + /// @dev Returns an `AddressSlot` with member `value` located at `slot`. + function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } + + /// @dev Returns an `BooleanSlot` with member `value` located at `slot`. + function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } + + /// @dev Returns an `Bytes32Slot` with member `value` located at `slot`. + function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } + + /// @dev Returns an `Uint256Slot` with member `value` located at `slot`. + function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { + /// @solidity memory-safe-assembly + assembly { + r.slot := slot + } + } +} diff --git a/contracts/lib/StringSet.sol b/contracts/lib/StringSet.sol new file mode 100644 index 000000000..630300bd1 --- /dev/null +++ b/contracts/lib/StringSet.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +/// @author thirdweb + +library StringSet { + /** + * @param _values storage of set values + * @param _indexes position of the value in the array + 1. (Note: index 0 means a value is not in the set.) + */ + struct Set { + string[] _values; + mapping(string => uint256) _indexes; + } + + /// @dev Add a value to a set. + /// Returns `true` if the value is not already present in set. + function _add(Set storage set, string memory value) private returns (bool) { + if (!_contains(set, value)) { + set._values.push(value); + + set._indexes[value] = set._values.length; + return true; + } else { + return false; + } + } + + /// @dev Removes a value from a set. + /// Returns `true` if the value was present and so, successfully removed from the set. + function _remove(Set storage set, string memory value) private returns (bool) { + uint256 valueIndex = set._indexes[value]; + + if (valueIndex != 0) { + uint256 toDeleteIndex = valueIndex - 1; + uint256 lastIndex = set._values.length - 1; + + if (lastIndex != toDeleteIndex) { + string memory lastValue = set._values[lastIndex]; + + set._values[toDeleteIndex] = lastValue; + + set._indexes[lastValue] = valueIndex; + } + + set._values.pop(); + + delete set._indexes[value]; + + return true; + } else { + return false; + } + } + + /// @dev Returns whether `value` is in the set. + function _contains(Set storage set, string memory value) private view returns (bool) { + return set._indexes[value] != 0; + } + + /// @dev Returns the number of elements in the set. + function _length(Set storage set) private view returns (uint256) { + return set._values.length; + } + + /// @dev Returns the element stored at position `index` in the set. + /// Note: the ordering of elements is not guaranteed to be fixed. It is unsafe to rely on + /// or compute based on the index of set elements. + function _at(Set storage set, uint256 index) private view returns (string memory) { + return set._values[index]; + } + + /// @dev Returns the values stored in the set. + function _values(Set storage set) private view returns (string[] memory) { + return set._values; + } + + /// @dev Add `value` to the set. + function add(Set storage set, string memory value) internal returns (bool) { + return _add(set, value); + } + + /// @dev Remove `value` from the set. + function remove(Set storage set, string memory value) internal returns (bool) { + return _remove(set, value); + } + + /// @dev Returns whether `value` is in the set. + function contains(Set storage set, string memory value) internal view returns (bool) { + return _contains(set, value); + } + + /// @dev Returns the number of elements in the set. + function length(Set storage set) internal view returns (uint256) { + return _length(set); + } + + /// @dev Returns the element stored at position `index` in the set. + function at(Set storage set, uint256 index) internal view returns (string memory) { + return _at(set, index); + } + + /// @dev Returns the values stored in the set. + function values(Set storage set) internal view returns (string[] memory) { + return _values(set); + } +} diff --git a/contracts/lib/Strings.sol b/contracts/lib/Strings.sol new file mode 100644 index 000000000..499df4583 --- /dev/null +++ b/contracts/lib/Strings.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +/// @author thirdweb + +/** + * @dev String operations. + */ +library Strings { + bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; + + /** + * @dev Converts a `uint256` to its ASCII `string` decimal representation. + */ + function toString(uint256 value) internal pure returns (string memory) { + // Inspired by OraclizeAPI's implementation - MIT licence + // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol + + if (value == 0) { + return "0"; + } + uint256 temp = value; + uint256 digits; + while (temp != 0) { + digits++; + temp /= 10; + } + bytes memory buffer = new bytes(digits); + while (value != 0) { + digits -= 1; + buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); + value /= 10; + } + return string(buffer); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. + */ + function toHexString(uint256 value) internal pure returns (string memory) { + if (value == 0) { + return "0x00"; + } + uint256 temp = value; + uint256 length = 0; + while (temp != 0) { + length++; + temp >>= 8; + } + return toHexString(value, length); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + */ + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = _HEX_SYMBOLS[value & 0xf]; + value >>= 4; + } + require(value == 0, "Strings: hex length insufficient"); + return string(buffer); + } + + /// @dev Returns the hexadecimal representation of `value`. + /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte, + /// and the alphabets are capitalized conditionally according to + /// https://eips.ethereum.org/EIPS/eip-55 + function toHexStringChecksummed(address value) internal pure returns (string memory str) { + str = toHexString(value); + /// @solidity memory-safe-assembly + assembly { + let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` + let o := add(str, 0x22) + let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... ` + let t := shl(240, 136) // `0b10001000 << 240` + for { + let i := 0 + } 1 { + + } { + mstore(add(i, i), mul(t, byte(i, hashed))) + i := add(i, 1) + if eq(i, 20) { + break + } + } + mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))) + o := add(o, 0x20) + mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))) + } + } + + /// @dev Returns the hexadecimal representation of `value`. + /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. + function toHexString(address value) internal pure returns (string memory str) { + str = toHexStringNoPrefix(value); + /// @solidity memory-safe-assembly + assembly { + let strLength := add(mload(str), 2) // Compute the length. + mstore(str, 0x3078) // Write the "0x" prefix. + str := sub(str, 2) // Move the pointer. + mstore(str, strLength) // Write the length. + } + } + + /// @dev Returns the hexadecimal representation of `value`. + /// The output is encoded using 2 hexadecimal digits per byte. + function toHexStringNoPrefix(address value) internal pure returns (string memory str) { + /// @solidity memory-safe-assembly + assembly { + str := mload(0x40) + + // Allocate the memory. + // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, + // 0x02 bytes for the prefix, and 0x28 bytes for the digits. + // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80. + mstore(0x40, add(str, 0x80)) + + // Store "0123456789abcdef" in scratch space. + mstore(0x0f, 0x30313233343536373839616263646566) + + str := add(str, 2) + mstore(str, 40) + + let o := add(str, 0x20) + mstore(add(o, 40), 0) + + value := shl(96, value) + + // We write the string from rightmost digit to leftmost digit. + // The following is essentially a do-while loop that also handles the zero case. + for { + let i := 0 + } 1 { + + } { + let p := add(o, add(i, i)) + let temp := byte(i, value) + mstore8(add(p, 1), mload(and(temp, 15))) + mstore8(p, mload(shr(4, temp))) + i := add(i, 1) + if eq(i, 20) { + break + } + } + } + } + + /// @dev Returns the hex encoded string from the raw bytes. + /// The output is encoded using 2 hexadecimal digits per byte. + function toHexString(bytes memory raw) internal pure returns (string memory str) { + str = toHexStringNoPrefix(raw); + /// @solidity memory-safe-assembly + assembly { + let strLength := add(mload(str), 2) // Compute the length. + mstore(str, 0x3078) // Write the "0x" prefix. + str := sub(str, 2) // Move the pointer. + mstore(str, strLength) // Write the length. + } + } + + /// @dev Returns the hex encoded string from the raw bytes. + /// The output is encoded using 2 hexadecimal digits per byte. + function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) { + /// @solidity memory-safe-assembly + assembly { + let length := mload(raw) + str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. + mstore(str, add(length, length)) // Store the length of the output. + + // Store "0123456789abcdef" in scratch space. + mstore(0x0f, 0x30313233343536373839616263646566) + + let o := add(str, 0x20) + let end := add(raw, length) + + for { + + } iszero(eq(raw, end)) { + + } { + raw := add(raw, 1) + mstore8(add(o, 1), mload(and(mload(raw), 15))) + mstore8(o, mload(and(shr(4, mload(raw)), 15))) + o := add(o, 2) + } + mstore(o, 0) // Zeroize the slot after the string. + mstore(0x40, add(o, 0x20)) // Allocate the memory. + } + } +} diff --git a/contracts/lib/TWAddress.sol b/contracts/lib/TWAddress.sol deleted file mode 100644 index 0d7b31449..000000000 --- a/contracts/lib/TWAddress.sol +++ /dev/null @@ -1,222 +0,0 @@ -// SPDX-License-Identifier: Apache 2.0 -pragma solidity ^0.8.0; - -/// @author thirdweb - -/** - * @dev Collection of functions related to the address type - */ -library TWAddress { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - * - * [IMPORTANT] - * ==== - * You shouldn't rely on `isContract` to protect against flash loan attacks! - * - * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets - * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract - * constructor. - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize/address.code.length, which returns 0 - // for contracts in construction, since the code is only stored at the end - // of the constructor execution. - - return account.code.length > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * [EIP1884](https://eips.ethereum.org/EIPS/eip-1884) increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{ value: amount }(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value - ) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{ value: value }(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} diff --git a/contracts/lib/TWStorageSlot.sol b/contracts/lib/TWStorageSlot.sol deleted file mode 100644 index 26b86fb8c..000000000 --- a/contracts/lib/TWStorageSlot.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: Apache 2.0 -pragma solidity ^0.8.0; - -/// @author thirdweb - -/** - * @dev Library for reading and writing primitive types to specific storage slots. - * - * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. - * This library helps with reading and writing to such slots without the need for inline assembly. - * - * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. - * - * Example usage to set ERC1967 implementation slot: - * ``` - * contract ERC1967 { - * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - * - * function _getImplementation() internal view returns (address) { - * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; - * } - * - * function _setImplementation(address newImplementation) internal { - * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); - * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; - * } - * } - * ``` - * - * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._ - */ -library TWStorageSlot { - struct AddressSlot { - address value; - } - - struct BooleanSlot { - bool value; - } - - struct Bytes32Slot { - bytes32 value; - } - - struct Uint256Slot { - uint256 value; - } - - /** - * @dev Returns an `AddressSlot` with member `value` located at `slot`. - */ - function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `BooleanSlot` with member `value` located at `slot`. - */ - function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. - */ - function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } - - /** - * @dev Returns an `Uint256Slot` with member `value` located at `slot`. - */ - function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { - /// @solidity memory-safe-assembly - assembly { - r.slot := slot - } - } -} diff --git a/contracts/lib/TWStringSet.sol b/contracts/lib/TWStringSet.sol deleted file mode 100644 index 2ca77b527..000000000 --- a/contracts/lib/TWStringSet.sol +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: Apache 2.0 -pragma solidity ^0.8.0; - -/// @author thirdweb - -library TWStringSet { - struct Set { - // Storage of set values - string[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(string => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, string memory value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, string memory value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - string memory lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, string memory value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (string memory) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (string[] memory) { - return set._values; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Set storage set, string memory value) internal returns (bool) { - return _add(set, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Set storage set, string memory value) internal returns (bool) { - return _remove(set, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Set storage set, string memory value) internal view returns (bool) { - return _contains(set, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Set storage set) internal view returns (uint256) { - return _length(set); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Set storage set, uint256 index) internal view returns (string memory) { - return _at(set, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Set storage set) internal view returns (string[] memory) { - return _values(set); - } -} diff --git a/contracts/lib/TWStrings.sol b/contracts/lib/TWStrings.sol deleted file mode 100644 index b04e5f05a..000000000 --- a/contracts/lib/TWStrings.sol +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: Apache 2.0 -pragma solidity ^0.8.0; - -/// @author thirdweb - -/** - * @dev String operations. - */ -library TWStrings { - bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; - - /** - * @dev Converts a `uint256` to its ASCII `string` decimal representation. - */ - function toString(uint256 value) internal pure returns (string memory) { - // Inspired by OraclizeAPI's implementation - MIT licence - // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol - - if (value == 0) { - return "0"; - } - uint256 temp = value; - uint256 digits; - while (temp != 0) { - digits++; - temp /= 10; - } - bytes memory buffer = new bytes(digits); - while (value != 0) { - digits -= 1; - buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); - value /= 10; - } - return string(buffer); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. - */ - function toHexString(uint256 value) internal pure returns (string memory) { - if (value == 0) { - return "0x00"; - } - uint256 temp = value; - uint256 length = 0; - while (temp != 0) { - length++; - temp >>= 8; - } - return toHexString(value, length); - } - - /** - * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. - */ - function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { - bytes memory buffer = new bytes(2 * length + 2); - buffer[0] = "0"; - buffer[1] = "x"; - for (uint256 i = 2 * length + 1; i > 1; --i) { - buffer[i] = _HEX_SYMBOLS[value & 0xf]; - value >>= 4; - } - require(value == 0, "Strings: hex length insufficient"); - return string(buffer); - } -} diff --git a/contracts/package.json b/contracts/package.json index c8ae602aa..48693cf4e 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,7 +1,7 @@ { "name": "@thirdweb-dev/contracts", "description": "Collection of smart contracts deployable via the thirdweb SDK, dashboard and CLI", - "version": "3.10.3-1", + "version": "3.10.3", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/contracts/prebuilts/account/dynamic/DynamicAccount.sol b/contracts/prebuilts/account/dynamic/DynamicAccount.sol index 8ccda1e5f..0d5ac3b26 100644 --- a/contracts/prebuilts/account/dynamic/DynamicAccount.sol +++ b/contracts/prebuilts/account/dynamic/DynamicAccount.sol @@ -23,17 +23,17 @@ contract DynamicAccount is AccountCore, BaseRouter { Constructor and Initializer //////////////////////////////////////////////////////////////*/ - constructor(IEntryPoint _entrypoint, Extension[] memory _defaultExtensions) - AccountCore(_entrypoint, msg.sender) - BaseRouter(_defaultExtensions) - { + 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) public override initializer { + function initialize(address _defaultAdmin, bytes calldata _data) public override initializer { __BaseRouter_init(); - AccountCoreStorage.data().firstAdmin = _defaultAdmin; + AccountCoreStorage.data().creationSalt = _generateSalt(_defaultAdmin, _data); _setAdmin(_defaultAdmin, true); } diff --git a/contracts/prebuilts/account/dynamic/DynamicAccountFactory.sol b/contracts/prebuilts/account/dynamic/DynamicAccountFactory.sol index 4763f97aa..864091a6b 100644 --- a/contracts/prebuilts/account/dynamic/DynamicAccountFactory.sol +++ b/contracts/prebuilts/account/dynamic/DynamicAccountFactory.sol @@ -22,14 +22,22 @@ import { DynamicAccount, IEntryPoint } from "./DynamicAccount.sol"; // \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ contract DynamicAccountFactory is BaseAccountFactory, ContractMetadata, PermissionsEnumerable { + address public constant ENTRYPOINT_ADDRESS = 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789; + /*/////////////////////////////////////////////////////////////// Constructor //////////////////////////////////////////////////////////////*/ - constructor(IEntryPoint _entrypoint, IExtension.Extension[] memory _defaultExtensions) - BaseAccountFactory(payable(address(new DynamicAccount(_entrypoint, _defaultExtensions))), address(_entrypoint)) + constructor( + address _defaultAdmin, + IExtension.Extension[] memory _defaultExtensions + ) + BaseAccountFactory( + payable(address(new DynamicAccount(IEntryPoint(ENTRYPOINT_ADDRESS), _defaultExtensions))), + ENTRYPOINT_ADDRESS + ) { - _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); + _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); } /*/////////////////////////////////////////////////////////////// @@ -37,11 +45,7 @@ contract DynamicAccountFactory is BaseAccountFactory, ContractMetadata, Permissi //////////////////////////////////////////////////////////////*/ /// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`. - function _initializeAccount( - address _account, - address _admin, - bytes calldata _data - ) internal override { + function _initializeAccount(address _account, address _admin, bytes calldata _data) internal override { DynamicAccount(payable(_account)).initialize(_admin, _data); } diff --git a/contracts/prebuilts/account/interface/IAccountFactory.sol b/contracts/prebuilts/account/interface/IAccountFactory.sol index 65d084066..4452a34be 100644 --- a/contracts/prebuilts/account/interface/IAccountFactory.sol +++ b/contracts/prebuilts/account/interface/IAccountFactory.sol @@ -9,16 +9,8 @@ interface IAccountFactory is IAccountFactoryCore { //////////////////////////////////////////////////////////////*/ /// @notice Callback function for an Account to register its signers. - function onSignerAdded( - address signer, - address creatorAdmin, - bytes memory data - ) external; + function onSignerAdded(address signer, bytes32 salt) external; /// @notice Callback function for an Account to un-register its signers. - function onSignerRemoved( - address signer, - address creatorAdmin, - bytes memory data - ) external; + function onSignerRemoved(address signer, bytes32 salt) external; } diff --git a/contracts/prebuilts/account/interface/IAggregator.sol b/contracts/prebuilts/account/interface/IAggregator.sol index 51a209d47..73774f5e7 100644 --- a/contracts/prebuilts/account/interface/IAggregator.sol +++ b/contracts/prebuilts/account/interface/IAggregator.sol @@ -30,8 +30,7 @@ interface IAggregator { * @param userOps array of UserOperations to collect the signatures from. * @return aggregatedSignature the aggregated signature */ - function aggregateSignatures(UserOperation[] calldata userOps) - external - view - returns (bytes memory aggregatedSignature); + function aggregateSignatures( + UserOperation[] calldata userOps + ) external view returns (bytes memory aggregatedSignature); } diff --git a/contracts/prebuilts/account/interface/IEntrypoint.sol b/contracts/prebuilts/account/interface/IEntrypoint.sol index 73284f257..fa6317a54 100644 --- a/contracts/prebuilts/account/interface/IEntrypoint.sol +++ b/contracts/prebuilts/account/interface/IEntrypoint.sol @@ -153,8 +153,10 @@ interface IEntryPoint is IStakeManager, INonceManager { * @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; + function handleAggregatedOps( + UserOpsPerAggregator[] calldata opsPerAggregator, + address payable beneficiary + ) external; /** * generate a request Id - unique identifier for this request. @@ -211,16 +213,12 @@ interface IEntryPoint is IStakeManager, INonceManager { * it performs full validation of the UserOperation, but ignores signature error. * an optional target address is called after the userop succeeds, and its value is returned * (before the entire call is reverted) - * Note that in order to collect the the success/failure of the target call, it must be executed + * Note that in order to collect the success/failure of the target call, it must be executed * with trace enabled to track the emitted events. * @param op the UserOperation to simulate * @param target if nonzero, a target address to call after userop simulation. If called, the targetSuccess and targetResult * are set to the return from that call. * @param targetCallData callData to pass to target address */ - function simulateHandleOp( - UserOperation calldata op, - address target, - bytes calldata targetCallData - ) external; + function simulateHandleOp(UserOperation calldata op, address target, bytes calldata targetCallData) external; } diff --git a/contracts/prebuilts/account/interface/IPaymaster.sol b/contracts/prebuilts/account/interface/IPaymaster.sol index bdd549985..0f5f623b6 100644 --- a/contracts/prebuilts/account/interface/IPaymaster.sol +++ b/contracts/prebuilts/account/interface/IPaymaster.sol @@ -49,9 +49,5 @@ interface IPaymaster { * @param context - the context value returned by validatePaymasterUserOp * @param actualGasCost - actual gas used so far (without this postOp call). */ - function postOp( - PostOpMode mode, - bytes calldata context, - uint256 actualGasCost - ) external; + function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external; } diff --git a/contracts/prebuilts/account/managed/ManagedAccountFactory.sol b/contracts/prebuilts/account/managed/ManagedAccountFactory.sol index 789ec4c3b..70bfd832e 100644 --- a/contracts/prebuilts/account/managed/ManagedAccountFactory.sol +++ b/contracts/prebuilts/account/managed/ManagedAccountFactory.sol @@ -26,15 +26,19 @@ contract ManagedAccountFactory is BaseAccountFactory, ContractMetadata, Permissi Constructor //////////////////////////////////////////////////////////////*/ - constructor(IEntryPoint _entrypoint, Extension[] memory _defaultExtensions) + 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, msg.sender); + _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); bytes32 _extensionRole = keccak256("EXTENSION_ROLE"); - _setupRole(_extensionRole, msg.sender); + _setupRole(_extensionRole, _defaultAdmin); _setRoleAdmin(_extensionRole, _extensionRole); } @@ -43,11 +47,7 @@ contract ManagedAccountFactory is BaseAccountFactory, ContractMetadata, Permissi //////////////////////////////////////////////////////////////*/ /// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`. - function _initializeAccount( - address _account, - address _admin, - bytes calldata _data - ) internal override { + function _initializeAccount(address _account, address _admin, bytes calldata _data) internal override { ManagedAccount(payable(_account)).initialize(_admin, _data); } diff --git a/contracts/prebuilts/account/non-upgradeable/Account.sol b/contracts/prebuilts/account/non-upgradeable/Account.sol index 09938c401..adc4bb3e3 100644 --- a/contracts/prebuilts/account/non-upgradeable/Account.sol +++ b/contracts/prebuilts/account/non-upgradeable/Account.sol @@ -33,6 +33,8 @@ contract Account is AccountCore, ContractMetadata, ERC1271, ERC721Holder, ERC115 using ECDSA for bytes32; using EnumerableSet for EnumerableSet.AddressSet; + bytes32 private constant MSG_TYPEHASH = keccak256("AccountMessage(bytes message)"); + /*/////////////////////////////////////////////////////////////// Constructor, Initializer, Modifiers //////////////////////////////////////////////////////////////*/ @@ -61,14 +63,12 @@ contract Account is AccountCore, ContractMetadata, ERC1271, ERC721Holder, ERC115 } /// @notice See EIP-1271 - function isValidSignature(bytes32 _hash, bytes memory _signature) - public - view - virtual - override - returns (bytes4 magicValue) - { - address signer = _hash.recover(_signature); + function isValidSignature( + bytes32 _message, + bytes memory _signature + ) public view virtual override returns (bytes4 magicValue) { + bytes32 messageHash = getMessageHash(abi.encode(_message)); + address signer = messageHash.recover(_signature); if (isAdmin(signer)) { return MAGICVALUE; @@ -87,16 +87,22 @@ contract Account is AccountCore, ContractMetadata, ERC1271, ERC721Holder, ERC115 } } + /** + * @notice Returns the hash of message that should be signed for EIP1271 verification. + * @param message Message to be hashed i.e. `keccak256(abi.encode(data))` + * @return Hashed message + */ + function getMessageHash(bytes memory message) public view returns (bytes32) { + bytes32 messageHash = keccak256(abi.encode(MSG_TYPEHASH, keccak256(message))); + return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), messageHash)); + } + /*/////////////////////////////////////////////////////////////// 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 { + function execute(address _target, uint256 _value, bytes calldata _calldata) external virtual onlyAdminOrEntrypoint { _registerOnFactory(); _call(_target, _value, _calldata); } @@ -115,6 +121,17 @@ contract Account is AccountCore, ContractMetadata, ERC1271, ERC721Holder, ERC115 } } + /// @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 //////////////////////////////////////////////////////////////*/ @@ -123,7 +140,7 @@ contract Account is AccountCore, ContractMetadata, ERC1271, ERC721Holder, ERC115 function _registerOnFactory() internal virtual { BaseAccountFactory factoryContract = BaseAccountFactory(factory); if (!factoryContract.isRegistered(address(this))) { - factoryContract.onRegister(AccountCoreStorage.data().firstAdmin, ""); + factoryContract.onRegister(AccountCoreStorage.data().creationSalt); } } diff --git a/contracts/prebuilts/account/non-upgradeable/AccountFactory.sol b/contracts/prebuilts/account/non-upgradeable/AccountFactory.sol index 909309685..017b2711a 100644 --- a/contracts/prebuilts/account/non-upgradeable/AccountFactory.sol +++ b/contracts/prebuilts/account/non-upgradeable/AccountFactory.sol @@ -30,10 +30,11 @@ contract AccountFactory is BaseAccountFactory, ContractMetadata, PermissionsEnum Constructor //////////////////////////////////////////////////////////////*/ - constructor(IEntryPoint _entrypoint) - BaseAccountFactory(address(new Account(_entrypoint, address(this))), address(_entrypoint)) - { - _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); + constructor( + address _defaultAdmin, + IEntryPoint _entrypoint + ) BaseAccountFactory(address(new Account(_entrypoint, address(this))), address(_entrypoint)) { + _setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin); } /*/////////////////////////////////////////////////////////////// @@ -41,11 +42,7 @@ contract AccountFactory is BaseAccountFactory, ContractMetadata, PermissionsEnum //////////////////////////////////////////////////////////////*/ /// @dev Called in `createAccount`. Initializes the account contract created in `createAccount`. - function _initializeAccount( - address _account, - address _admin, - bytes calldata _data - ) internal override { + function _initializeAccount(address _account, address _admin, bytes calldata _data) internal override { Account(payable(_account)).initialize(_admin, _data); } diff --git a/contracts/prebuilts/account/token-bound-account/TokenBoundAccount.sol b/contracts/prebuilts/account/token-bound-account/TokenBoundAccount.sol index 3c7acc8bf..3ed222fcf 100644 --- a/contracts/prebuilts/account/token-bound-account/TokenBoundAccount.sol +++ b/contracts/prebuilts/account/token-bound-account/TokenBoundAccount.sol @@ -108,13 +108,10 @@ contract TokenBoundAccount is } /// @notice See EIP-1271 - function isValidSignature(bytes32 _hash, bytes memory _signature) - public - view - virtual - override - returns (bytes4 magicValue) - { + function isValidSignature( + bytes32 _hash, + bytes memory _signature + ) public view virtual override returns (bytes4 magicValue) { address signer = _hash.recover(_signature); if (owner() == signer) { @@ -136,15 +133,7 @@ contract TokenBoundAccount is entryPoint().withdrawTo(withdrawAddress, amount); } - function token() - external - view - returns ( - uint256 chainId, - address tokenContract, - uint256 tokenId - ) - { + function token() external view returns (uint256 chainId, address tokenContract, uint256 tokenId) { return ERC6551AccountLib.token(); } @@ -167,11 +156,7 @@ contract TokenBoundAccount is } /// @notice Executes a transaction (called directly from an admin, or by entryPoint) - function execute( - address _target, - uint256 _value, - bytes calldata _calldata - ) external virtual onlyAdminOrEntrypoint { + function execute(address _target, uint256 _value, bytes calldata _calldata) external virtual onlyAdminOrEntrypoint { _call(_target, _value, _calldata); } @@ -212,12 +197,10 @@ contract TokenBoundAccount is } /// @notice Validates the signature of a user operation. - function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash) - internal - virtual - override - returns (uint256 validationData) - { + function _validateSignature( + UserOperation calldata userOp, + bytes32 userOpHash + ) internal virtual override returns (uint256 validationData) { bytes32 hash = userOpHash.toEthSignedMessageHash(); address signer = hash.recover(userOp.signature); @@ -240,15 +223,9 @@ contract TokenBoundAccount is _value = abi.decode(data[36:68], (uint256)); } - function decodeExecuteBatchCalldata(bytes calldata data) - internal - pure - returns ( - address[] memory _targets, - uint256[] memory _values, - bytes[] memory _callData - ) - { + 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[])); diff --git a/contracts/prebuilts/account/token-bound-account/erc6551-utils/ERC6551AccountLib.sol b/contracts/prebuilts/account/token-bound-account/erc6551-utils/ERC6551AccountLib.sol index ae4dec027..ff99e843c 100644 --- a/contracts/prebuilts/account/token-bound-account/erc6551-utils/ERC6551AccountLib.sol +++ b/contracts/prebuilts/account/token-bound-account/erc6551-utils/ERC6551AccountLib.sol @@ -20,15 +20,7 @@ library ERC6551AccountLib { return Create2.computeAddress(bytes32(_salt), bytecodeHash, registry); } - function token() - internal - view - returns ( - uint256, - address, - uint256 - ) - { + function token() internal view returns (uint256, address, uint256) { bytes memory footer = new bytes(0x60); assembly { diff --git a/contracts/prebuilts/account/token-bound-account/erc6551-utils/IERC6551Account.sol b/contracts/prebuilts/account/token-bound-account/erc6551-utils/IERC6551Account.sol index e64484278..160e27e7b 100644 --- a/contracts/prebuilts/account/token-bound-account/erc6551-utils/IERC6551Account.sol +++ b/contracts/prebuilts/account/token-bound-account/erc6551-utils/IERC6551Account.sol @@ -22,14 +22,7 @@ interface IERC6551Account { * @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 - ); + 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 diff --git a/contracts/prebuilts/account/utils/AccountCore.sol b/contracts/prebuilts/account/utils/AccountCore.sol index c59cf8cac..d62dd4999 100644 --- a/contracts/prebuilts/account/utils/AccountCore.sol +++ b/contracts/prebuilts/account/utils/AccountCore.sol @@ -56,9 +56,9 @@ contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, Acc } /// @notice Initializes the smart contract wallet. - function initialize(address _defaultAdmin, bytes calldata) public virtual initializer { + function initialize(address _defaultAdmin, bytes calldata _data) public virtual initializer { // This is passed as data in the `_registerOnFactory()` call in `AccountExtension` / `Account`. - AccountCoreStorage.data().firstAdmin = _defaultAdmin; + AccountCoreStorage.data().creationSalt = _generateSalt(_defaultAdmin, _data); _setAdmin(_defaultAdmin, true); } @@ -75,7 +75,17 @@ contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, Acc return entrypointContract; } - /// @notice Returns whether a signer is authorized to perform transactions using the wallet. + /** + @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, UserOperation calldata _userOp) public view virtual returns (bool) { // First, check if the signer is an admin. @@ -102,6 +112,7 @@ contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, Acc // 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); @@ -115,12 +126,14 @@ contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, Acc } } - // Check if the value is within the allowed range and if the target is approved. + // Check if the value is within the allowed range. if (permissions.nativeTokenLimitPerTransaction < value) { // Account: value too high OR Account: target not approved. return false; } - } else if (sig == AccountExtension.executeBatch.selector) { + } + // 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); @@ -134,7 +147,7 @@ contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, Acc } } - // For each target+value pair, check if the value is within the allowed range and if the target is approved. + // 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. @@ -155,17 +168,6 @@ contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, Acc External functions //////////////////////////////////////////////////////////////*/ - /// @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); - } - /// @notice Overrides the Entrypoint contract being used. function setEntrypointOverride(IEntryPoint _entrypointOverride) public virtual { _onlyAdmin(); @@ -176,6 +178,11 @@ contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, Acc 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]); @@ -191,27 +198,19 @@ contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, Acc _value = abi.decode(data[36:68], (uint256)); } - function decodeExecuteBatchCalldata(bytes calldata data) - internal - pure - returns ( - address[] memory _targets, - uint256[] memory _values, - bytes[] memory _callData - ) - { + 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(UserOperation calldata userOp, bytes32 userOpHash) - internal - virtual - override - returns (uint256 validationData) - { + function _validateSignature( + UserOperation calldata userOp, + bytes32 userOpHash + ) internal virtual override returns (uint256 validationData) { bytes32 hash = userOpHash.toEthSignedMessageHash(); address signer = hash.recover(userOp.signature); @@ -230,9 +229,9 @@ contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, Acc super._setAdmin(_account, _isAdmin); if (factory.code.length > 0) { if (_isAdmin) { - BaseAccountFactory(factory).onSignerAdded(_account, AccountCoreStorage.data().firstAdmin, ""); + BaseAccountFactory(factory).onSignerAdded(_account, AccountCoreStorage.data().creationSalt); } else { - BaseAccountFactory(factory).onSignerRemoved(_account, AccountCoreStorage.data().firstAdmin, ""); + BaseAccountFactory(factory).onSignerRemoved(_account, AccountCoreStorage.data().creationSalt); } } } @@ -240,7 +239,7 @@ contract AccountCore is IAccountCore, Initializable, Multicall, BaseAccount, Acc /// @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().firstAdmin, ""); + BaseAccountFactory(factory).onSignerAdded(_req.signer, AccountCoreStorage.data().creationSalt); } } } diff --git a/contracts/prebuilts/account/utils/AccountCoreStorage.sol b/contracts/prebuilts/account/utils/AccountCoreStorage.sol index c549482bd..4356ef94a 100644 --- a/contracts/prebuilts/account/utils/AccountCoreStorage.sol +++ b/contracts/prebuilts/account/utils/AccountCoreStorage.sol @@ -9,7 +9,7 @@ library AccountCoreStorage { struct Data { address entrypointOverride; - address firstAdmin; + bytes32 creationSalt; } function data() internal pure returns (Data storage acountCoreData) { diff --git a/contracts/prebuilts/account/utils/AccountExtension.sol b/contracts/prebuilts/account/utils/AccountExtension.sol index 161a124c1..c9aad670e 100644 --- a/contracts/prebuilts/account/utils/AccountExtension.sol +++ b/contracts/prebuilts/account/utils/AccountExtension.sol @@ -32,6 +32,8 @@ contract AccountExtension is ContractMetadata, ERC1271, AccountPermissions, ERC7 using ECDSA for bytes32; using EnumerableSet for EnumerableSet.AddressSet; + bytes32 private constant MSG_TYPEHASH = keccak256("AccountMessage(bytes message)"); + /*/////////////////////////////////////////////////////////////// Constructor, Initializer, Modifiers //////////////////////////////////////////////////////////////*/ @@ -63,14 +65,12 @@ contract AccountExtension is ContractMetadata, ERC1271, AccountPermissions, ERC7 } /// @notice See EIP-1271 - function isValidSignature(bytes32 _hash, bytes memory _signature) - public - view - virtual - override - returns (bytes4 magicValue) - { - address signer = _hash.recover(_signature); + function isValidSignature( + bytes32 _message, + bytes memory _signature + ) public view virtual override returns (bytes4 magicValue) { + bytes32 messageHash = getMessageHash(abi.encode(_message)); + address signer = messageHash.recover(_signature); if (isAdmin(signer)) { return MAGICVALUE; @@ -89,16 +89,22 @@ contract AccountExtension is ContractMetadata, ERC1271, AccountPermissions, ERC7 } } + /** + * @notice Returns the hash of message that should be signed for EIP1271 verification. + * @param message Message to be hashed i.e. `keccak256(abi.encode(data))` + * @return Hashed message + */ + function getMessageHash(bytes memory message) public view returns (bytes32) { + bytes32 messageHash = keccak256(abi.encode(MSG_TYPEHASH, keccak256(message))); + return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), messageHash)); + } + /*/////////////////////////////////////////////////////////////// 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 { + function execute(address _target, uint256 _value, bytes calldata _calldata) external virtual onlyAdminOrEntrypoint { _registerOnFactory(); _call(_target, _value, _calldata); } @@ -116,6 +122,17 @@ contract AccountExtension is ContractMetadata, ERC1271, AccountPermissions, ERC7 } } + /// @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 //////////////////////////////////////////////////////////////*/ @@ -125,16 +142,12 @@ contract AccountExtension is ContractMetadata, ERC1271, AccountPermissions, ERC7 address factory = AccountCore(payable(address(this))).factory(); BaseAccountFactory factoryContract = BaseAccountFactory(factory); if (!factoryContract.isRegistered(address(this))) { - factoryContract.onRegister(AccountCoreStorage.data().firstAdmin, ""); + 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) { + 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) { diff --git a/contracts/prebuilts/account/utils/BaseAccount.sol b/contracts/prebuilts/account/utils/BaseAccount.sol index 4ae5c5cc8..14e02ce6a 100644 --- a/contracts/prebuilts/account/utils/BaseAccount.sol +++ b/contracts/prebuilts/account/utils/BaseAccount.sol @@ -70,10 +70,10 @@ abstract contract BaseAccount is IAccount { * 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(UserOperation calldata userOp, bytes32 userOpHash) - internal - virtual - returns (uint256 validationData); + function _validateSignature( + UserOperation calldata userOp, + bytes32 userOpHash + ) internal virtual returns (uint256 validationData); /** * Validate the nonce of the UserOperation. diff --git a/contracts/prebuilts/account/utils/BaseAccountFactory.sol b/contracts/prebuilts/account/utils/BaseAccountFactory.sol index fdc5855b3..151e5789e 100644 --- a/contracts/prebuilts/account/utils/BaseAccountFactory.sol +++ b/contracts/prebuilts/account/utils/BaseAccountFactory.sol @@ -72,20 +72,16 @@ abstract contract BaseAccountFactory is IAccountFactory, Multicall { } /// @notice Callback function for an Account to register itself on the factory. - function onRegister(address _defaultAdmin, bytes memory _data) external { + function onRegister(bytes32 _salt) external { address account = msg.sender; - require(_isAccountOfFactory(account, _defaultAdmin, _data), "AccountFactory: not an account."); + require(_isAccountOfFactory(account, _salt), "AccountFactory: not an account."); require(allAccounts.add(account), "AccountFactory: account already registered"); } - function onSignerAdded( - address _signer, - address _defaultAdmin, - bytes memory _data - ) external { + function onSignerAdded(address _signer, bytes32 _salt) external { address account = msg.sender; - require(_isAccountOfFactory(account, _defaultAdmin, _data), "AccountFactory: not an account."); + require(_isAccountOfFactory(account, _salt), "AccountFactory: not an account."); bool isNewSigner = accountsOfSigner[_signer].add(account); @@ -95,13 +91,9 @@ abstract contract BaseAccountFactory is IAccountFactory, Multicall { } /// @notice Callback function for an Account to un-register its signers. - function onSignerRemoved( - address _signer, - address _defaultAdmin, - bytes memory _data - ) external { + function onSignerRemoved(address _signer, bytes32 _salt) external { address account = msg.sender; - require(_isAccountOfFactory(account, _defaultAdmin, _data), "AccountFactory: not an account."); + require(_isAccountOfFactory(account, _salt), "AccountFactory: not an account."); bool isAccount = accountsOfSigner[_signer].remove(account); @@ -119,6 +111,23 @@ abstract contract BaseAccountFactory is IAccountFactory, Multicall { 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(); @@ -140,13 +149,8 @@ abstract contract BaseAccountFactory is IAccountFactory, Multicall { //////////////////////////////////////////////////////////////*/ /// @dev Returns whether the caller is an account deployed by this factory. - function _isAccountOfFactory( - address _account, - address _admin, - bytes memory _data - ) internal view virtual returns (bool) { - bytes32 salt = _generateSalt(_admin, _data); - address predicted = Clones.predictDeterministicAddress(accountImplementation, salt); + function _isAccountOfFactory(address _account, bytes32 _salt) internal view virtual returns (bool) { + address predicted = Clones.predictDeterministicAddress(accountImplementation, _salt); return _account == predicted; } @@ -156,14 +160,10 @@ abstract contract BaseAccountFactory is IAccountFactory, Multicall { } /// @dev Returns the salt used when deploying an Account. - function _generateSalt(address _admin, bytes memory) internal view virtual returns (bytes32) { - return keccak256(abi.encode(_admin)); + 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; + 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 index f42e6db7b..260024d3a 100644 --- a/contracts/prebuilts/account/utils/Entrypoint.sol +++ b/contracts/prebuilts/account/utils/Entrypoint.sol @@ -119,10 +119,10 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard * @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) - public - nonReentrant - { + function handleAggregatedOps( + UserOpsPerAggregator[] calldata opsPerAggregator, + address payable beneficiary + ) public nonReentrant { uint256 opasLen = opsPerAggregator.length; uint256 totalOps = 0; for (uint256 i = 0; i < opasLen; i++) { @@ -357,11 +357,7 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard } // create the sender's contract if needed. - function _createSenderIfNeeded( - uint256 opIndex, - UserOpInfo memory opInfo, - bytes calldata initCode - ) internal { + 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"); @@ -536,11 +532,9 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard } } - function _getValidationData(uint256 validationData) - internal - view - returns (address aggregator, bool outOfTimeRange) - { + function _getValidationData( + uint256 validationData + ) internal view returns (address aggregator, bool outOfTimeRange) { if (validationData == 0) { return (address(0), false); } diff --git a/contracts/prebuilts/account/utils/Exec.sol b/contracts/prebuilts/account/utils/Exec.sol index 2b161a414..97d0952ba 100644 --- a/contracts/prebuilts/account/utils/Exec.sol +++ b/contracts/prebuilts/account/utils/Exec.sol @@ -7,32 +7,19 @@ pragma solidity ^0.8.11; * 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) { + function call(address to, uint256 value, bytes memory data, uint256 txGas) internal returns (bool success) { assembly { 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) { + function staticcall(address to, bytes memory data, uint256 txGas) internal view returns (bool success) { assembly { success := staticcall(txGas, to, add(data, 0x20), mload(data), 0, 0) } } - function delegateCall( - address to, - bytes memory data, - uint256 txGas - ) internal returns (bool success) { + function delegateCall(address to, bytes memory data, uint256 txGas) internal returns (bool success) { assembly { success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0) } @@ -60,11 +47,7 @@ library Exec { } } - function callAndRevert( - address to, - bytes memory data, - uint256 maxLen - ) internal { + 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 index 68134a063..32ebbfdd5 100644 --- a/contracts/prebuilts/account/utils/Helpers.sol +++ b/contracts/prebuilts/account/utils/Helpers.sol @@ -32,10 +32,10 @@ function _parseValidationData(uint256 validationData) pure returns (ValidationDa } // intersect account and paymaster ranges. -function _intersectTimeRange(uint256 validationData, uint256 paymasterValidationData) - pure - returns (ValidationData memory) -{ +function _intersectTimeRange( + uint256 validationData, + uint256 paymasterValidationData +) pure returns (ValidationData memory) { ValidationData memory accountValidationData = _parseValidationData(validationData); ValidationData memory pmValidationData = _parseValidationData(paymasterValidationData); address aggregator = accountValidationData.aggregator; @@ -66,11 +66,7 @@ function _packValidationData(ValidationData memory data) pure returns (uint256) * @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) { +function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter) pure returns (uint256) { return (sigFailed ? 1 : 0) | (uint256(validUntil) << 160) | (uint256(validAfter) << (160 + 48)); } diff --git a/contracts/prebuilts/account/utils/TokenCallbackHandler.sol b/contracts/prebuilts/account/utils/TokenCallbackHandler.sol index eb543bac0..1900af613 100644 --- a/contracts/prebuilts/account/utils/TokenCallbackHandler.sol +++ b/contracts/prebuilts/account/utils/TokenCallbackHandler.sol @@ -22,12 +22,7 @@ contract TokenCallbackHandler is IERC777Recipient, IERC721Receiver, IERC1155Rece bytes calldata ) external pure override {} - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external pure override returns (bytes4) { + function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) { return IERC721Receiver.onERC721Received.selector; } diff --git a/contracts/prebuilts/drop/DropERC1155.sol b/contracts/prebuilts/drop/DropERC1155.sol index 448812973..99748d52d 100644 --- a/contracts/prebuilts/drop/DropERC1155.sol +++ b/contracts/prebuilts/drop/DropERC1155.sol @@ -16,7 +16,7 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; @@ -47,7 +47,7 @@ contract DropERC1155 is PermissionsEnumerable, Drop1155, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, ERC1155Upgradeable { using StringsUpgradeable for uint256; @@ -155,13 +155,9 @@ contract DropERC1155 is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC1155Upgradeable, IERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC1155Upgradeable, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId; } @@ -269,11 +265,7 @@ contract DropERC1155 is } /// @dev Transfers the NFTs being claimed. - function transferTokensOnClaim( - address _to, - uint256 _tokenId, - uint256 _quantityBeingClaimed - ) internal override { + function transferTokensOnClaim(address _to, uint256 _tokenId, uint256 _quantityBeingClaimed) internal override { _mint(_to, _tokenId, _quantityBeingClaimed, ""); } @@ -322,11 +314,7 @@ contract DropERC1155 is } /// @dev Lets a token owner burn multiple tokens they own at once (i.e. destroy for good) - function burnBatch( - address account, - uint256[] memory ids, - uint256[] memory values - ) public virtual { + function burnBatch(address account, uint256[] memory ids, uint256[] memory values) public virtual { require( account == _msgSender() || isApprovedForAll(account, _msgSender()), "ERC1155: caller is not owner nor approved." diff --git a/contracts/prebuilts/drop/DropERC20.sol b/contracts/prebuilts/drop/DropERC20.sol index 790de9690..b8ec7f748 100644 --- a/contracts/prebuilts/drop/DropERC20.sol +++ b/contracts/prebuilts/drop/DropERC20.sol @@ -16,7 +16,7 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; // ========== Internal imports ========== @@ -40,7 +40,7 @@ contract DropERC20 is PermissionsEnumerable, Drop, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, ERC20BurnableUpgradeable, ERC20VotesUpgradeable { @@ -218,11 +218,7 @@ contract DropERC20 is } /// @dev Runs on every transfer. - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal override(ERC20Upgradeable) { + function _beforeTokenTransfer(address from, address to, uint256 amount) internal override(ERC20Upgradeable) { super._beforeTokenTransfer(from, to, amount); if (!hasRole(transferRole, address(0)) && from != address(0) && to != address(0)) { diff --git a/contracts/prebuilts/drop/DropERC721.sol b/contracts/prebuilts/drop/DropERC721.sol index 69fe66c18..96a233520 100644 --- a/contracts/prebuilts/drop/DropERC721.sol +++ b/contracts/prebuilts/drop/DropERC721.sol @@ -14,7 +14,7 @@ pragma solidity ^0.8.11; // ========== External imports ========== -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; @@ -49,7 +49,7 @@ contract DropERC721 is PermissionsEnumerable, Drop, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, ERC721AUpgradeable { using StringsUpgradeable for uint256; @@ -137,13 +137,9 @@ contract DropERC721 is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721AUpgradeable, IERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721AUpgradeable, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId; } @@ -185,11 +181,10 @@ contract DropERC721 is /// @dev Lets an account with `METADATA_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs. /// @param _index the ID of a token with the desired batch. /// @param _key the key to decrypt the batch's URI. - function reveal(uint256 _index, bytes calldata _key) - external - onlyRole(metadataRole) - returns (string memory revealedURI) - { + function reveal( + uint256 _index, + bytes calldata _key + ) external onlyRole(metadataRole) returns (string memory revealedURI) { uint256 batchId = getBatchIdAtIndex(_index); revealedURI = getRevealURI(batchId, _key); @@ -281,11 +276,10 @@ contract DropERC721 is } /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - override - returns (uint256 startTokenId) - { + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal override returns (uint256 startTokenId) { startTokenId = _currentIndex; _safeMint(_to, _quantityBeingClaimed); } diff --git a/contracts/prebuilts/drop/drop.md b/contracts/prebuilts/drop/drop.md index 5b528426e..a4de5e77a 100644 --- a/contracts/prebuilts/drop/drop.md +++ b/contracts/prebuilts/drop/drop.md @@ -160,11 +160,11 @@ There are a few key differences between the three implementations — The distribution mechanism of thirdweb’s `Drop` contracts is vulnerable to [sybil attacks](https://en.wikipedia.org/wiki/Sybil_attack). That is, despite the various ways in which restrictions can be applied to the minting of tokens, some restrictions that claim conditions can express target wallets and not persons. -For example, the restriction `quantityLimitPerWallet` expresses the max quantity a _wallet_ can claim during the respective claim condition. A sophisticated actor may generate multiple wallets to claim tokens in a way that undermine such restrictions, when viewing such restrictions as restrictions on unique persons, and not wallets. +For example, the restriction `quantityLimitPerWallet` expresses the max quantity a _wallet_ can claim during the respective claim condition. A sophisticated actor may generate multiple wallets to claim tokens in a way that undermines such restrictions, when viewing such restrictions as restrictions on unique persons, and not wallets. ### Allowlist behavior -When specifiying allowlist of addresses, and quantities, price, etc. for those addresses, contract admins must ensure that they don't list an address more than once in the same merkle tree. +When specifying allowlist of addresses, and quantities, price, etc. for those addresses, contract admins must ensure that they don't list an address more than once in the same merkle tree. For e.g. admin wishes to grant user X permission to mint 2 tokens at 0.25 ETH, and 4 tokens at 0.5 ETH. In this case, the contract design will not permit the user X to claim 6 tokens with different prices as desired. Instead, the user may be limited to claiming just 2 tokens or 4 tokens based on their order of claiming. diff --git a/contracts/prebuilts/evolving-nfts/EvolvingNFTLogic.sol b/contracts/prebuilts/evolving-nfts/EvolvingNFTLogic.sol index 6b5c18c05..f7c645d40 100644 --- a/contracts/prebuilts/evolving-nfts/EvolvingNFTLogic.sol +++ b/contracts/prebuilts/evolving-nfts/EvolvingNFTLogic.sol @@ -22,7 +22,7 @@ import "../../eip/queryable/ERC721AQueryableUpgradeable.sol"; // ========== Internal imports ========== import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; -import "../../lib/CurrencyTransferLib.sol"; +import { CurrencyTransferLib } from "../../lib/CurrencyTransferLib.sol"; // ========== Features ========== @@ -65,13 +65,9 @@ contract EvolvingNFTLogic is //////////////////////////////////////////////////////////////*/ /// @dev Returns the URI for a given tokenId. - function tokenURI(uint256 _tokenId) - public - view - virtual - override(ERC721AUpgradeable, IERC721AUpgradeable) - returns (string memory) - { + function tokenURI( + uint256 _tokenId + ) public view virtual override(ERC721AUpgradeable, IERC721AUpgradeable) returns (string memory) { if (!_exists(_tokenId)) { revert("!ID"); } @@ -104,13 +100,9 @@ contract EvolvingNFTLogic is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721AUpgradeable, IERC721AUpgradeable, IERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721AUpgradeable, IERC721AUpgradeable, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId; } @@ -155,11 +147,10 @@ contract EvolvingNFTLogic is } /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - override - returns (uint256 startTokenId_) - { + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal override returns (uint256 startTokenId_) { startTokenId_ = _nextTokenId(); _safeMint(_to, _quantityBeingClaimed); } diff --git a/contracts/prebuilts/interface/IPack.sol b/contracts/prebuilts/interface/IPack.sol index c27639a32..f28cac3f8 100644 --- a/contracts/prebuilts/interface/IPack.sol +++ b/contracts/prebuilts/interface/IPack.sol @@ -47,7 +47,7 @@ interface IPack is ITokenBundle { * @param amountDistributedPerOpen The number of reward units distributed per open. * @param recipient The recipient of the packs created. * - * @return packId The unique identifer of the created set of packs. + * @return packId The unique identifier of the created set of packs. * @return packTotalSupply The total number of packs created. */ function createPack( diff --git a/contracts/prebuilts/interface/IPackVRFDirect.sol b/contracts/prebuilts/interface/IPackVRFDirect.sol index 324ff48dd..116c9ec30 100644 --- a/contracts/prebuilts/interface/IPackVRFDirect.sol +++ b/contracts/prebuilts/interface/IPackVRFDirect.sol @@ -50,7 +50,7 @@ interface IPackVRFDirect is ITokenBundle { * @param amountDistributedPerOpen The number of reward units distributed per open. * @param recipient The recipient of the packs created. * - * @return packId The unique identifer of the created set of packs. + * @return packId The unique identifier of the created set of packs. * @return packTotalSupply The total number of packs created. */ function createPack( diff --git a/contracts/prebuilts/interface/airdrop/IAirdropERC1155.sol b/contracts/prebuilts/interface/airdrop/IAirdropERC1155.sol index aa2daae7e..7350e1029 100644 --- a/contracts/prebuilts/interface/airdrop/IAirdropERC1155.sol +++ b/contracts/prebuilts/interface/airdrop/IAirdropERC1155.sol @@ -38,12 +38,8 @@ interface IAirdropERC1155 { * which acts as operator for the tokens. * * @param tokenAddress The contract address of the tokens to transfer. - * @param tokenOwner The owner of the the tokens to transfer. + * @param tokenOwner The owner of the tokens to transfer. * @param contents List containing recipient, tokenId to airdrop. */ - function airdropERC1155( - address tokenAddress, - address tokenOwner, - AirdropContent[] calldata contents - ) external; + function airdropERC1155(address tokenAddress, address tokenOwner, AirdropContent[] calldata contents) external; } diff --git a/contracts/prebuilts/interface/airdrop/IAirdropERC20.sol b/contracts/prebuilts/interface/airdrop/IAirdropERC20.sol index 64566356a..86964d74d 100644 --- a/contracts/prebuilts/interface/airdrop/IAirdropERC20.sol +++ b/contracts/prebuilts/interface/airdrop/IAirdropERC20.sol @@ -35,7 +35,7 @@ interface IAirdropERC20 { * which acts as operator for the tokens. * * @param tokenAddress The contract address of the tokens to transfer. - * @param tokenOwner The owner of the the tokens to transfer. + * @param tokenOwner The owner of the tokens to transfer. * @param contents List containing recipient, tokenId to airdrop. */ function airdropERC20( diff --git a/contracts/prebuilts/interface/airdrop/IAirdropERC721.sol b/contracts/prebuilts/interface/airdrop/IAirdropERC721.sol index ffae1e605..3d94d422c 100644 --- a/contracts/prebuilts/interface/airdrop/IAirdropERC721.sol +++ b/contracts/prebuilts/interface/airdrop/IAirdropERC721.sol @@ -35,12 +35,8 @@ interface IAirdropERC721 { * which acts as operator for the tokens. * * @param tokenAddress The contract address of the tokens to transfer. - * @param tokenOwner The owner of the the tokens to transfer. + * @param tokenOwner The owner of the tokens to transfer. * @param contents List containing recipient, tokenId to airdrop. */ - function airdropERC721( - address tokenAddress, - address tokenOwner, - AirdropContent[] calldata contents - ) external; + function airdropERC721(address tokenAddress, address tokenOwner, AirdropContent[] calldata contents) external; } diff --git a/contracts/prebuilts/interface/drop/IDropERC1155.sol b/contracts/prebuilts/interface/drop/IDropERC1155.sol index 319d12074..2c3d4181a 100644 --- a/contracts/prebuilts/interface/drop/IDropERC1155.sol +++ b/contracts/prebuilts/interface/drop/IDropERC1155.sol @@ -88,9 +88,5 @@ interface IDropERC1155 is IERC1155Upgradeable, IDropClaimCondition { * `limitMerkleProofClaim` values when setting new * claim conditions. */ - function setClaimConditions( - uint256 tokenId, - ClaimCondition[] calldata phases, - bool resetClaimEligibility - ) external; + function setClaimConditions(uint256 tokenId, ClaimCondition[] calldata phases, bool resetClaimEligibility) external; } diff --git a/contracts/prebuilts/interface/drop/IDropERC721.sol b/contracts/prebuilts/interface/drop/IDropERC721.sol index 47140a670..e115de49c 100644 --- a/contracts/prebuilts/interface/drop/IDropERC721.sol +++ b/contracts/prebuilts/interface/drop/IDropERC721.sol @@ -60,11 +60,7 @@ interface IDropERC721 is IERC721Upgradeable, IDropClaimCondition { * result of encrypting the URI of the NFTs in the revealed * state. */ - function lazyMint( - uint256 amount, - string calldata baseURIForTokens, - bytes calldata encryptedBaseURI - ) external; + function lazyMint(uint256 amount, string calldata baseURIForTokens, bytes calldata encryptedBaseURI) external; /** * @notice Lets an account claim a given quantity of NFTs. diff --git a/contracts/prebuilts/interface/marketplace/IMarketplace.sol b/contracts/prebuilts/interface/marketplace/IMarketplace.sol index 15dcba229..d485451d4 100644 --- a/contracts/prebuilts/interface/marketplace/IMarketplace.sol +++ b/contracts/prebuilts/interface/marketplace/IMarketplace.sol @@ -24,7 +24,7 @@ interface IMarketplace is IThirdwebContract, IPlatformFee { /** * @notice The information related to either (1) an offer on a direct listing, or (2) a bid in an auction. * - * @dev The type of the listing at ID `lisingId` determins how the `Offer` is interpreted. + * @dev The type of the listing at ID `lisingId` determines how the `Offer` is interpreted. * If the listing is of type `Direct`, the `Offer` is interpreted as an offer to a direct listing. * If the listing is of type `Auction`, the `Offer` is interpreted as a bid in an auction. * @@ -209,9 +209,9 @@ interface IMarketplace is IThirdwebContract, IPlatformFee { * @notice Lets a listing's creator edit the listing's parameters. A direct listing can be edited whenever. * An auction listing cannot be edited after the auction has started. * - * @param _listingId The uid of the lisitng to edit. + * @param _listingId The uid of the listing to edit. * - * @param _quantityToList The amount of NFTs to list for sale in the listing. For direct lisitngs, the contract + * @param _quantityToList The amount of NFTs to list for sale in the listing. For direct listings, the contract * only checks whether the listing creator owns and has approved Marketplace to transfer * `_quantityToList` amount of NFTs to list for sale. For auction listings, the contract * ensures that exactly `_quantityToList` amount of NFTs to list are escrowed. @@ -248,14 +248,14 @@ interface IMarketplace is IThirdwebContract, IPlatformFee { /** * @notice Lets a direct listing creator cancel their listing. * - * @param _listingId The unique Id of the lisitng to cancel. + * @param _listingId The unique Id of the listing to cancel. */ function cancelDirectListing(uint256 _listingId) external; /** * @notice Lets someone buy a given quantity of tokens from a direct listing by paying the fixed price. * - * @param _listingId The uid of the direct lisitng to buy from. + * @param _listingId The uid of the direct listing to buy from. * @param _buyFor The receiver of the NFT being bought. * @param _quantity The amount of NFTs to buy from the direct listing. * @param _currency The currency to pay the price in. @@ -265,7 +265,7 @@ interface IMarketplace is IThirdwebContract, IPlatformFee { * (1) buyer does not own or has not approved Marketplace to transfer the appropriate * amount of currency (or hasn't sent the appropriate amount of native tokens) * - * (2) the lister does not own or has removed Markeplace's + * (2) the lister does not own or has removed Marketplace's * approval to transfer the tokens listed for sale. */ function buy( @@ -283,7 +283,7 @@ interface IMarketplace is IThirdwebContract, IPlatformFee { * makes two offers to the same direct listing, the last offer is counted as the buyer's * offer to that listing. * - * @param _listingId The unique ID of the lisitng to make an offer/bid to. + * @param _listingId The unique ID of the listing to make an offer/bid to. * * @param _quantityWanted For auction listings: the 'quantity wanted' is the total amount of NFTs * being auctioned, regardless of the value of `_quantityWanted` passed. @@ -297,7 +297,7 @@ interface IMarketplace is IThirdwebContract, IPlatformFee { * @param _pricePerToken For direct listings: offered price per token. For auction listings: the bid * amount per token. The total offer/bid amount is `_quantityWanted * _pricePerToken`. * - * @param _expirationTimestamp For aution listings: inapplicable. For direct listings: The timestamp after which + * @param _expirationTimestamp For auction listings: inapplicable. For direct listings: The timestamp after which * the seller can no longer accept the offer. */ function offer( @@ -315,16 +315,11 @@ interface IMarketplace is IThirdwebContract, IPlatformFee { * @param _currency The currency of the offer that is to be accepted. * @param _totalPrice The total price of the offer that is to be accepted. */ - function acceptOffer( - uint256 _listingId, - address _offeror, - address _currency, - uint256 _totalPrice - ) external; + function acceptOffer(uint256 _listingId, address _offeror, address _currency, uint256 _totalPrice) external; /** * @notice Lets any account close an auction on behalf of either the (1) auction's creator, or (2) winning bidder. - * For (1): The auction creator is sent the the winning bid amount. + * For (1): The auction creator is sent the winning bid amount. * For (2): The winning bidder is sent the auctioned NFTs. * * @param _listingId The uid of the listing (the auction to close). diff --git a/contracts/prebuilts/interface/token/ITokenERC1155.sol b/contracts/prebuilts/interface/token/ITokenERC1155.sol index 322f7d8ec..656878796 100644 --- a/contracts/prebuilts/interface/token/ITokenERC1155.sol +++ b/contracts/prebuilts/interface/token/ITokenERC1155.sol @@ -58,10 +58,10 @@ interface ITokenERC1155 is IERC1155Upgradeable { * * returns (success, signer) Result of verification and the recovered address. */ - function verify(MintRequest calldata req, bytes calldata signature) - external - view - returns (bool success, address signer); + function verify( + MintRequest calldata req, + bytes calldata signature + ) external view returns (bool success, address signer); /** * @notice Lets an account with MINTER_ROLE mint an NFT. @@ -72,12 +72,7 @@ interface ITokenERC1155 is IERC1155Upgradeable { * @param amount The number of copies of the NFT to mint. * */ - function mintTo( - address to, - uint256 tokenId, - string calldata uri, - uint256 amount - ) external; + function mintTo(address to, uint256 tokenId, string calldata uri, uint256 amount) external; /** * @notice Mints an NFT according to the provided mint request. diff --git a/contracts/prebuilts/interface/token/ITokenERC20.sol b/contracts/prebuilts/interface/token/ITokenERC20.sol index 13ed053e7..4a97956df 100644 --- a/contracts/prebuilts/interface/token/ITokenERC20.sol +++ b/contracts/prebuilts/interface/token/ITokenERC20.sol @@ -42,10 +42,10 @@ interface ITokenERC20 is IERC20MetadataUpgradeable { * * returns (success, signer) Result of verification and the recovered address. */ - function verify(MintRequest calldata req, bytes calldata signature) - external - view - returns (bool success, address signer); + function verify( + MintRequest calldata req, + bytes calldata signature + ) external view returns (bool success, address signer); /** * @dev Creates `amount` new tokens for `to`. diff --git a/contracts/prebuilts/interface/token/ITokenERC721.sol b/contracts/prebuilts/interface/token/ITokenERC721.sol index 22e2f0b63..caf0016d9 100644 --- a/contracts/prebuilts/interface/token/ITokenERC721.sol +++ b/contracts/prebuilts/interface/token/ITokenERC721.sol @@ -52,10 +52,10 @@ interface ITokenERC721 is IERC721Upgradeable { * * returns (success, signer) Result of verification and the recovered address. */ - function verify(MintRequest calldata req, bytes calldata signature) - external - view - returns (bool success, address signer); + function verify( + MintRequest calldata req, + bytes calldata signature + ) external view returns (bool success, address signer); /** * @notice Lets an account with MINTER_ROLE mint an NFT. diff --git a/contracts/prebuilts/loyalty/LoyaltyCard.sol b/contracts/prebuilts/loyalty/LoyaltyCard.sol index 0ff5fc2b8..a796855e0 100644 --- a/contracts/prebuilts/loyalty/LoyaltyCard.sol +++ b/contracts/prebuilts/loyalty/LoyaltyCard.sol @@ -126,13 +126,9 @@ contract LoyaltyCard is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721AUpgradeable, IERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721AUpgradeable, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || type(IERC2981).interfaceId == interfaceId; } @@ -141,12 +137,10 @@ contract LoyaltyCard is //////////////////////////////////////////////////////////////*/ /// @dev Mints an NFT according to the provided mint request. Always mints 1 NFT. - function mintWithSignature(MintRequest calldata _req, bytes calldata _signature) - external - payable - nonReentrant - returns (address signer) - { + function mintWithSignature( + MintRequest calldata _req, + bytes calldata _signature + ) external payable nonReentrant returns (address signer) { require(_req.quantity == 1, "LoyaltyCard: only 1 NFT can be minted at a time."); signer = _processRequest(_req, _signature); diff --git a/contracts/prebuilts/marketplace-legacy/Marketplace.sol b/contracts/prebuilts/marketplace-legacy/Marketplace.sol index a8a8d50c3..ba136e58a 100644 --- a/contracts/prebuilts/marketplace-legacy/Marketplace.sol +++ b/contracts/prebuilts/marketplace-legacy/Marketplace.sol @@ -26,7 +26,6 @@ import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; // ========== Internal imports ========== @@ -34,6 +33,7 @@ import { IMarketplace } from "../interface/marketplace/IMarketplace.sol"; import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "../../lib/CurrencyTransferLib.sol"; import "../../lib/FeeType.sol"; @@ -42,7 +42,7 @@ contract Marketplace is IMarketplace, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, AccessControlEnumerableUpgradeable, IERC721ReceiverUpgradeable, IERC1155ReceiverUpgradeable @@ -190,22 +190,13 @@ contract Marketplace is return this.onERC1155BatchReceived.selector; } - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external pure override returns (bytes4) { + function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) { return this.onERC721Received.selector; } - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(AccessControlEnumerableUpgradeable, IERC165Upgradeable) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(AccessControlEnumerableUpgradeable, IERC165Upgradeable) returns (bool) { return interfaceId == type(IERC1155ReceiverUpgradeable).interfaceId || interfaceId == type(IERC721ReceiverUpgradeable).interfaceId || @@ -604,12 +595,10 @@ contract Marketplace is //////////////////////////////////////////////////////////////*/ /// @dev Lets an account close an auction for either the (1) winning bidder, or (2) auction creator. - function closeAuction(uint256 _listingId, address _closeFor) - external - override - nonReentrant - onlyExistingListing(_listingId) - { + function closeAuction( + uint256 _listingId, + address _closeFor + ) external override nonReentrant onlyExistingListing(_listingId) { Listing memory targetListing = listings[_listingId]; require(targetListing.listingType == ListingType.Auction, "not an auction."); @@ -695,12 +684,7 @@ contract Marketplace is //////////////////////////////////////////////////////////////*/ /// @dev Transfers tokens listed for sale in a direct or auction listing. - function transferListingTokens( - address _from, - address _to, - uint256 _quantity, - Listing memory _listing - ) internal { + function transferListingTokens(address _from, address _to, uint256 _quantity, Listing memory _listing) internal { if (_listing.tokenType == TokenType.ERC1155) { IERC1155Upgradeable(_listing.assetContract).safeTransferFrom(_from, _to, _listing.tokenId, _quantity, ""); } else if (_listing.tokenType == TokenType.ERC721) { @@ -838,11 +822,10 @@ contract Marketplace is //////////////////////////////////////////////////////////////*/ /// @dev Enforces quantity == 1 if tokenType is TokenType.ERC721. - function getSafeQuantity(TokenType _tokenType, uint256 _quantityToCheck) - internal - pure - returns (uint256 safeQuantity) - { + function getSafeQuantity( + TokenType _tokenType, + uint256 _quantityToCheck + ) internal pure returns (uint256 safeQuantity) { if (_quantityToCheck == 0) { safeQuantity = 0; } else { @@ -871,10 +854,10 @@ contract Marketplace is //////////////////////////////////////////////////////////////*/ /// @dev Lets a contract admin update platform fee recipient and bps. - function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setPlatformFeeInfo( + address _platformFeeRecipient, + uint256 _platformFeeBps + ) external onlyRole(DEFAULT_ADMIN_ROLE) { require(_platformFeeBps <= MAX_BPS, "bps <= 10000."); platformFeeBps = uint64(_platformFeeBps); diff --git a/contracts/prebuilts/marketplace/IMarketplace.sol b/contracts/prebuilts/marketplace/IMarketplace.sol index 07e3e5ad4..0b9e05bcf 100644 --- a/contracts/prebuilts/marketplace/IMarketplace.sol +++ b/contracts/prebuilts/marketplace/IMarketplace.sol @@ -144,11 +144,7 @@ interface IDirectListings { * @param _buyer The address of the buyer to approve to buy from the listing. * @param _toApprove Whether to approve the buyer to buy from the listing. */ - function approveBuyerForListing( - uint256 _listingId, - address _buyer, - bool _toApprove - ) external; + function approveBuyerForListing(uint256 _listingId, address _buyer, bool _toApprove) external; /** * @notice Approve a currency as a form of payment for the listing. @@ -393,14 +389,9 @@ interface IEnglishAuctions { function getAllValidAuctions(uint256 _startId, uint256 _endId) external view returns (Auction[] memory auctions); /// @notice Returns the winning bid of an active auction. - function getWinningBid(uint256 _auctionId) - external - view - returns ( - address bidder, - address currency, - uint256 bidAmount - ); + function getWinningBid( + uint256 _auctionId + ) external view returns (address bidder, address currency, uint256 bidAmount); /// @notice Returns whether an auction is active. function isAuctionExpired(uint256 _auctionId) external view returns (bool); diff --git a/contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol b/contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol index 2a24982ec..32ec02abf 100644 --- a/contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol +++ b/contracts/prebuilts/marketplace/direct-listings/DirectListingsLogic.sol @@ -86,12 +86,9 @@ contract DirectListingsLogic is IDirectListings, ReentrancyGuard, ERC2771Context //////////////////////////////////////////////////////////////*/ /// @notice List NFTs (ERC721 or ERC1155) for sale at a fixed price. - function createListing(ListingParameters calldata _params) - external - onlyListerRole - onlyAssetRole(_params.assetContract) - returns (uint256 listingId) - { + function createListing( + ListingParameters calldata _params + ) external onlyListerRole onlyAssetRole(_params.assetContract) returns (uint256 listingId) { listingId = _getNextListingId(); address listingCreator = _msgSender(); TokenType tokenType = _getTokenType(_params.assetContract); @@ -131,12 +128,10 @@ contract DirectListingsLogic is IDirectListings, ReentrancyGuard, ERC2771Context } /// @notice Update parameters of a listing of NFTs. - function updateListing(uint256 _listingId, ListingParameters memory _params) - external - onlyExistingListing(_listingId) - onlyAssetRole(_params.assetContract) - onlyListingCreator(_listingId) - { + function updateListing( + uint256 _listingId, + ListingParameters memory _params + ) external onlyExistingListing(_listingId) onlyAssetRole(_params.assetContract) onlyListingCreator(_listingId) { address listingCreator = _msgSender(); Listing memory listing = _directListingsStorage().listings[_listingId]; TokenType tokenType = _getTokenType(_params.assetContract); @@ -355,11 +350,10 @@ contract DirectListingsLogic is IDirectListings, ReentrancyGuard, ERC2771Context * A valid listing is where the listing creator still owns and has approved Marketplace * to transfer the listed NFTs. */ - function getAllValidListings(uint256 _startId, uint256 _endId) - external - view - returns (Listing[] memory _validListings) - { + function getAllValidListings( + uint256 _startId, + uint256 _endId + ) external view returns (Listing[] memory _validListings) { require(_startId <= _endId && _endId < _directListingsStorage().totalListings, "invalid range"); Listing[] memory _listings = new Listing[](_endId - _startId + 1); @@ -476,11 +470,7 @@ contract DirectListingsLogic is IDirectListings, ReentrancyGuard, ERC2771Context } /// @dev Validates that `_tokenOwner` owns and has approved Markeplace to transfer the appropriate amount of currency - function _validateERC20BalAndAllowance( - address _tokenOwner, - address _currency, - uint256 _amount - ) internal view { + function _validateERC20BalAndAllowance(address _tokenOwner, address _currency, uint256 _amount) internal view { require( IERC20(_currency).balanceOf(_tokenOwner) >= _amount && IERC20(_currency).allowance(_tokenOwner, address(this)) >= _amount, @@ -489,12 +479,7 @@ contract DirectListingsLogic is IDirectListings, ReentrancyGuard, ERC2771Context } /// @dev Transfers tokens listed for sale in a direct or auction listing. - function _transferListingTokens( - address _from, - address _to, - uint256 _quantity, - Listing memory _listing - ) internal { + function _transferListingTokens(address _from, address _to, uint256 _quantity, Listing memory _listing) internal { if (_listing.tokenType == TokenType.ERC1155) { IERC1155(_listing.assetContract).safeTransferFrom(_from, _to, _listing.tokenId, _quantity, ""); } else if (_listing.tokenType == TokenType.ERC721) { diff --git a/contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol b/contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol index f7164fdd2..66574a050 100644 --- a/contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol +++ b/contracts/prebuilts/marketplace/english-auctions/EnglishAuctionsLogic.sol @@ -86,12 +86,9 @@ contract EnglishAuctionsLogic is IEnglishAuctions, ReentrancyGuard, ERC2771Conte //////////////////////////////////////////////////////////////*/ /// @notice Auction ERC721 or ERC1155 NFTs. - function createAuction(AuctionParameters calldata _params) - external - onlyListerRole - onlyAssetRole(_params.assetContract) - returns (uint256 auctionId) - { + function createAuction( + AuctionParameters calldata _params + ) external onlyListerRole onlyAssetRole(_params.assetContract) returns (uint256 auctionId) { auctionId = _getNextAuctionId(); address auctionCreator = _msgSender(); TokenType tokenType = _getTokenType(_params.assetContract); @@ -122,12 +119,10 @@ contract EnglishAuctionsLogic is IEnglishAuctions, ReentrancyGuard, ERC2771Conte emit NewAuction(auctionCreator, auctionId, _params.assetContract, auction); } - function bidInAuction(uint256 _auctionId, uint256 _bidAmount) - external - payable - nonReentrant - onlyExistingAuction(_auctionId) - { + function bidInAuction( + uint256 _auctionId, + uint256 _bidAmount + ) external payable nonReentrant onlyExistingAuction(_auctionId) { Auction memory _targetAuction = _englishAuctionsStorage().auctions[_auctionId]; require( @@ -203,12 +198,10 @@ contract EnglishAuctionsLogic is IEnglishAuctions, ReentrancyGuard, ERC2771Conte View functions //////////////////////////////////////////////////////////////*/ - function isNewWinningBid(uint256 _auctionId, uint256 _bidAmount) - external - view - onlyExistingAuction(_auctionId) - returns (bool) - { + function isNewWinningBid( + uint256 _auctionId, + uint256 _bidAmount + ) external view onlyExistingAuction(_auctionId) returns (bool) { Auction memory _targetAuction = _englishAuctionsStorage().auctions[_auctionId]; Bid memory _currentWinningBid = _englishAuctionsStorage().winningBid[_auctionId]; @@ -239,11 +232,10 @@ contract EnglishAuctionsLogic is IEnglishAuctions, ReentrancyGuard, ERC2771Conte } } - function getAllValidAuctions(uint256 _startId, uint256 _endId) - external - view - returns (Auction[] memory _validAuctions) - { + function getAllValidAuctions( + uint256 _startId, + uint256 _endId + ) external view returns (Auction[] memory _validAuctions) { require(_startId <= _endId && _endId < _englishAuctionsStorage().totalAuctions, "invalid range"); Auction[] memory _auctions = new Auction[](_endId - _startId + 1); @@ -277,15 +269,9 @@ contract EnglishAuctionsLogic is IEnglishAuctions, ReentrancyGuard, ERC2771Conte } } - function getWinningBid(uint256 _auctionId) - external - view - returns ( - address _bidder, - address _currency, - uint256 _bidAmount - ) - { + function getWinningBid( + uint256 _auctionId + ) external view returns (address _bidder, address _currency, uint256 _bidAmount) { Auction memory _targetAuction = _englishAuctionsStorage().auctions[_auctionId]; Bid memory _currentWinningBid = _englishAuctionsStorage().winningBid[_auctionId]; @@ -458,11 +444,7 @@ contract EnglishAuctionsLogic is IEnglishAuctions, ReentrancyGuard, ERC2771Conte } /// @dev Transfers tokens for auction. - function _transferAuctionTokens( - address _from, - address _to, - Auction memory _auction - ) internal { + function _transferAuctionTokens(address _from, address _to, Auction memory _auction) internal { if (_auction.tokenType == TokenType.ERC1155) { IERC1155(_auction.assetContract).safeTransferFrom(_from, _to, _auction.tokenId, _auction.quantity, ""); } else if (_auction.tokenType == TokenType.ERC721) { diff --git a/contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol b/contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol index b7ba591fd..cfa4939a4 100644 --- a/contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol +++ b/contracts/prebuilts/marketplace/entrypoint/MarketplaceV3.sol @@ -69,10 +69,9 @@ contract MarketplaceV3 is address nativeTokenWrapper; } - constructor(MarketplaceConstructorParams memory _marketplaceV3Params) - BaseRouter(_marketplaceV3Params.extensions) - RoyaltyPaymentsLogic(_marketplaceV3Params.royaltyEngineAddress) - { + constructor( + MarketplaceConstructorParams memory _marketplaceV3Params + ) BaseRouter(_marketplaceV3Params.extensions) RoyaltyPaymentsLogic(_marketplaceV3Params.royaltyEngineAddress) { nativeTokenWrapper = _marketplaceV3Params.nativeTokenWrapper; _disableInitializers(); } @@ -127,13 +126,9 @@ contract MarketplaceV3 is ERC 165 / 721 / 1155 logic //////////////////////////////////////////////////////////////*/ - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC165, IERC165, ERC1155Receiver) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC165, IERC165, ERC1155Receiver) returns (bool) { return interfaceId == type(IERC1155Receiver).interfaceId || interfaceId == type(IERC721Receiver).interfaceId || diff --git a/contracts/prebuilts/marketplace/marketplace-v3.md b/contracts/prebuilts/marketplace/marketplace-v3.md index 7319e5cb2..d224b95fe 100644 --- a/contracts/prebuilts/marketplace/marketplace-v3.md +++ b/contracts/prebuilts/marketplace/marketplace-v3.md @@ -77,7 +77,7 @@ Marketplace also honors [ERC2981](https://eips.ethereum.org/EIPS/eip-2981) for t ### Events emitted -All events emitted by the contract, as well as when they're emitted, can be found in the interface of the contract, [here](https://github.com/thirdweb-dev/contracts/blob/main/contracts/marketplace/IMarketplace.sol). In general, events are emitted whenever there is a state change in the contract. +All events emitted by the contract, as well as when they're emitted, can be found in the interface of the contract, [here](https://github.com/thirdweb-dev/contracts/blob/main/contracts/prebuilts/marketplace/IMarketplace.sol). In general, events are emitted whenever there is a state change in the contract. ### Currency transfers diff --git a/contracts/prebuilts/marketplace/offers/OffersLogic.sol b/contracts/prebuilts/marketplace/offers/OffersLogic.sol index 05590921c..8f8724d89 100644 --- a/contracts/prebuilts/marketplace/offers/OffersLogic.sol +++ b/contracts/prebuilts/marketplace/offers/OffersLogic.sol @@ -66,11 +66,9 @@ contract OffersLogic is IOffers, ReentrancyGuard, ERC2771ContextConsumer { External functions //////////////////////////////////////////////////////////////*/ - function makeOffer(OfferParams memory _params) - external - onlyAssetRole(_params.assetContract) - returns (uint256 _offerId) - { + function makeOffer( + OfferParams memory _params + ) external onlyAssetRole(_params.assetContract) returns (uint256 _offerId) { _offerId = _getNextOfferId(); address _offeror = _msgSender(); TokenType _tokenType = _getTokenType(_params.assetContract); @@ -267,12 +265,7 @@ contract OffersLogic is IOffers, ReentrancyGuard, ERC2771ContextConsumer { } /// @dev Transfers tokens. - function _transferOfferTokens( - address _from, - address _to, - uint256 _quantity, - Offer memory _offer - ) internal { + function _transferOfferTokens(address _from, address _to, uint256 _quantity, Offer memory _offer) internal { if (_offer.tokenType == TokenType.ERC1155) { IERC1155(_offer.assetContract).safeTransferFrom(_from, _to, _offer.tokenId, _quantity, ""); } else if (_offer.tokenType == TokenType.ERC721) { diff --git a/contracts/prebuilts/multiwrap/Multiwrap.sol b/contracts/prebuilts/multiwrap/Multiwrap.sol index 626f4e2ed..0a6d59d6f 100644 --- a/contracts/prebuilts/multiwrap/Multiwrap.sol +++ b/contracts/prebuilts/multiwrap/Multiwrap.sol @@ -14,14 +14,13 @@ pragma solidity ^0.8.11; // ========== External imports ========== import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; - -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; // ========== Internal imports ========== import "../interface/IMultiwrap.sol"; +import "../../extension/Multicall.sol"; import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; // ========== Features ========== @@ -41,7 +40,7 @@ contract Multiwrap is TokenStore, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, ERC721EnumerableUpgradeable, IMultiwrap { @@ -142,13 +141,9 @@ contract Multiwrap is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC1155Receiver, ERC721EnumerableUpgradeable, IERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC1155Receiver, ERC721EnumerableUpgradeable, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || interfaceId == type(IERC721Upgradeable).interfaceId || @@ -236,9 +231,10 @@ contract Multiwrap is function _beforeTokenTransfer( address from, address to, - uint256 tokenId + uint256 tokenId, + uint256 batchSize ) internal virtual override { - super._beforeTokenTransfer(from, to, tokenId); + super._beforeTokenTransfer(from, to, tokenId, batchSize); // 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)) { diff --git a/contracts/prebuilts/multiwrap/multiwrap.md b/contracts/prebuilts/multiwrap/multiwrap.md index 015fbdce6..9d49b82e9 100644 --- a/contracts/prebuilts/multiwrap/multiwrap.md +++ b/contracts/prebuilts/multiwrap/multiwrap.md @@ -20,7 +20,7 @@ The single wrapped token received on bundling up multiple assets, as mentioned a A token owner should be able to wrap any combination of *n* ERC20, ERC721 or ERC1155 tokens as a wrapped NFT. When wrapping, the token owner should be able to specify a recipient for the wrapped NFT. At the time of wrapping, the token owner should be able to set the metadata of the wrapped NFT that will be minted. -The wrapped NFT owner should be able to unwrap the the NFT to retrieve the underlying tokens of the wrapped NFT. At the time of unwrapping, the wrapped NFT owner should be able to specify a recipient for the underlying tokens of the wrapped NFT. +The wrapped NFT owner should be able to unwrap the NFT to retrieve the underlying tokens of the wrapped NFT. At the time of unwrapping, the wrapped NFT owner should be able to specify a recipient for the underlying tokens of the wrapped NFT. The `Multiwrap` contract creator should be able to apply the following role-based restrictions: @@ -136,4 +136,4 @@ What does **Type (Switch / !Switch)** mean? ## Authors - [nkrishang](https://github.com/nkrishang) -- [thirdweb team](https://github.com/thirdweb-dev) \ No newline at end of file +- [thirdweb team](https://github.com/thirdweb-dev) diff --git a/contracts/prebuilts/open-edition/OpenEditionERC721.sol b/contracts/prebuilts/open-edition/OpenEditionERC721.sol index f1788c744..3b7287ab8 100644 --- a/contracts/prebuilts/open-edition/OpenEditionERC721.sol +++ b/contracts/prebuilts/open-edition/OpenEditionERC721.sol @@ -106,13 +106,9 @@ contract OpenEditionERC721 is //////////////////////////////////////////////////////////////*/ /// @dev Returns the URI for a given tokenId. - function tokenURI(uint256 _tokenId) - public - view - virtual - override(ERC721AUpgradeable, IERC721AUpgradeable) - returns (string memory) - { + function tokenURI( + uint256 _tokenId + ) public view virtual override(ERC721AUpgradeable, IERC721AUpgradeable) returns (string memory) { if (!_exists(_tokenId)) { revert("!ID"); } @@ -121,13 +117,9 @@ contract OpenEditionERC721 is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721AUpgradeable, IERC165, IERC721AUpgradeable) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721AUpgradeable, IERC165, IERC721AUpgradeable) returns (bool) { return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId; } @@ -172,11 +164,10 @@ contract OpenEditionERC721 is } /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - override - returns (uint256 startTokenId_) - { + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal override returns (uint256 startTokenId_) { startTokenId_ = _nextTokenId(); _safeMint(_to, _quantityBeingClaimed); } diff --git a/contracts/prebuilts/pack/Pack.sol b/contracts/prebuilts/pack/Pack.sol index 9abef00fd..956459cf2 100644 --- a/contracts/prebuilts/pack/Pack.sol +++ b/contracts/prebuilts/pack/Pack.sol @@ -17,7 +17,6 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155PausableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; import "@openzeppelin/contracts/interfaces/IERC721Receiver.sol"; @@ -26,6 +25,7 @@ import { IERC1155Receiver } from "@openzeppelin/contracts/interfaces/IERC1155Rec // ========== Internal imports ========== import "../interface/IPack.sol"; +import "../../extension/Multicall.sol"; import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; // ========== Features ========== @@ -45,7 +45,7 @@ contract Pack is TokenStore, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, ERC1155Upgradeable, IPack { @@ -183,13 +183,9 @@ contract Pack is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC1155Receiver, ERC1155Upgradeable, IERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC1155Receiver, ERC1155Upgradeable, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId || @@ -386,11 +382,9 @@ contract Pack is //////////////////////////////////////////////////////////////*/ /// @dev Returns the underlying contents of a pack. - function getPackContents(uint256 _packId) - public - view - returns (Token[] memory contents, uint256[] memory perUnitAmounts) - { + function getPackContents( + uint256 _packId + ) public view returns (Token[] memory contents, uint256[] memory perUnitAmounts) { PackInfo memory pack = packInfo[_packId]; uint256 total = getTokenCountOfBundle(_packId); contents = new Token[](total); diff --git a/contracts/prebuilts/pack/PackVRFDirect.sol b/contracts/prebuilts/pack/PackVRFDirect.sol index 68178f764..d0c17fbde 100644 --- a/contracts/prebuilts/pack/PackVRFDirect.sol +++ b/contracts/prebuilts/pack/PackVRFDirect.sol @@ -17,7 +17,6 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155PausableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; import "@openzeppelin/contracts/interfaces/IERC721Receiver.sol"; @@ -28,6 +27,7 @@ import "../../external-deps/chainlink/VRFV2WrapperConsumerBase.sol"; // ========== Internal imports ========== import "../interface/IPackVRFDirect.sol"; +import "../../extension/Multicall.sol"; import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; // ========== Features ========== @@ -52,7 +52,7 @@ contract PackVRFDirect is TokenStore, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, ERC1155Upgradeable, IPackVRFDirect { @@ -186,13 +186,9 @@ contract PackVRFDirect is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC1155Receiver, ERC1155Upgradeable, IERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC1155Receiver, ERC1155Upgradeable, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId || @@ -433,11 +429,9 @@ contract PackVRFDirect is //////////////////////////////////////////////////////////////*/ /// @dev Returns the underlying contents of a pack. - function getPackContents(uint256 _packId) - public - view - returns (Token[] memory contents, uint256[] memory perUnitAmounts) - { + function getPackContents( + uint256 _packId + ) public view returns (Token[] memory contents, uint256[] memory perUnitAmounts) { PackInfo memory pack = packInfo[_packId]; uint256 total = getTokenCountOfBundle(_packId); contents = new Token[](total); diff --git a/contracts/prebuilts/pack/pack.md b/contracts/prebuilts/pack/pack.md index 8ab9cf0cd..986805bf2 100644 --- a/contracts/prebuilts/pack/pack.md +++ b/contracts/prebuilts/pack/pack.md @@ -54,7 +54,7 @@ We’ll now go over the technical details of the `Pack` contract, with reference ## What can be packed in packs? -You can create a set of packs with any combination of any number of ERC20, ERC721 and ERC1155 tokens. For example, you can create a set of packs with 10,000 [USDC](https://www.circle.com/en/usdc) (ERC20), 1 [Bored Ape Yatch Club](https://opensea.io/collection/boredapeyachtclub) NFT (ERC721), and 50 of [adidas originals’ first NFT](https://opensea.io/assets/0x28472a58a490c5e09a238847f66a68a47cc76f0f/0) (ERC1155). +You can create a set of packs with any combination of any number of ERC20, ERC721 and ERC1155 tokens. For example, you can create a set of packs with 10,000 [USDC](https://www.circle.com/en/usdc) (ERC20), 1 [Bored Ape Yacht Club](https://opensea.io/collection/boredapeyachtclub) NFT (ERC721), and 50 of [adidas originals’ first NFT](https://opensea.io/assets/0x28472a58a490c5e09a238847f66a68a47cc76f0f/0) (ERC1155). With strictly non-fungible tokens i.e. ERC721 NFTs, each NFT has a supply of 1. This means if a pack is opened and an ERC721 NFT is selected by the `Pack` contract to be distributed to the opener, that 1 NFT will be distributed to the opener. @@ -79,7 +79,7 @@ uint256 perUnitAmount; | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | assetContract | The contract address of the token. | | tokenType | The type of the token -- ERC20 / ERC721 / ERC1155 | -| tokenId | The tokenId of the the token. (Not applicable for ERC20 tokens. The contract will ignore this value for ERC20 tokens.) | +| tokenId | The tokenId of the token. (Not applicable for ERC20 tokens. The contract will ignore this value for ERC20 tokens.) | | totalAmount | The total amount of this token packed in the pack. (Not applicable for ERC721 tokens. The contract will always consider this as 1 for ERC721 tokens.) | | perUnitAmount | The amount of this token to distribute as a unit, on opening a pack. (Not applicable for ERC721 tokens. The contract will always consider this as 1 for ERC721 tokens.) | @@ -120,7 +120,7 @@ Since packs are ERC1155 tokens, you can publish multiple sets of packs using the ### Supply of packs -When creating packs, you can specify the numer of reward units to distribute to the opener on opening a pack. And so, when creating a set of packs, the total number of packs in that set is calculated as: +When creating packs, you can specify the number of reward units to distribute to the opener on opening a pack. And so, when creating a set of packs, the total number of packs in that set is calculated as: `total_supply_of_packs = (total_reward_units) / (reward_units_to_distribute_per_open)` diff --git a/contracts/prebuilts/signature-drop/SignatureDrop.sol b/contracts/prebuilts/signature-drop/SignatureDrop.sol index 13f65fb7b..b601aed2d 100644 --- a/contracts/prebuilts/signature-drop/SignatureDrop.sol +++ b/contracts/prebuilts/signature-drop/SignatureDrop.sol @@ -14,7 +14,7 @@ pragma solidity ^0.8.11; // ========== External imports ========== -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; @@ -28,9 +28,9 @@ import "../../lib/CurrencyTransferLib.sol"; // ========== Features ========== import "../../extension/ContractMetadata.sol"; -import "../../extension/PlatformFee.sol"; +import "../../legacy-contracts/extension/PlatformFee_V1.sol"; import "../../extension/Royalty.sol"; -import "../../extension/PrimarySale.sol"; +import "../../legacy-contracts/extension/PrimarySale_V1.sol"; import "../../extension/Ownable.sol"; import "../../extension/DelayedReveal.sol"; import "../../extension/LazyMint.sol"; @@ -51,7 +51,7 @@ contract SignatureDrop is DropSinglePhase, SignatureMintERC721Upgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, ERC721AUpgradeable { using StringsUpgradeable for uint256; @@ -126,13 +126,9 @@ contract SignatureDrop is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721AUpgradeable, IERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721AUpgradeable, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId; } @@ -168,11 +164,10 @@ contract SignatureDrop is } /// @dev Lets an account with `MINTER_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs. - function reveal(uint256 _index, bytes calldata _key) - external - onlyRole(minterRole) - returns (string memory revealedURI) - { + function reveal( + uint256 _index, + bytes calldata _key + ) external onlyRole(minterRole) returns (string memory revealedURI) { uint256 batchId = getBatchIdAtIndex(_index); revealedURI = getRevealURI(batchId, _key); @@ -187,11 +182,10 @@ contract SignatureDrop is //////////////////////////////////////////////////////////////*/ /// @dev Claim lazy minted tokens via signature. - function mintWithSignature(MintRequest calldata _req, bytes calldata _signature) - external - payable - returns (address signer) - { + function mintWithSignature( + MintRequest calldata _req, + bytes calldata _signature + ) external payable returns (address signer) { uint256 tokenIdToMint = _currentIndex; if (tokenIdToMint + _req.quantity > nextTokenIdToLazyMint) { revert("!Tokens"); @@ -262,11 +256,10 @@ contract SignatureDrop is } /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - override - returns (uint256 startTokenId) - { + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal override returns (uint256 startTokenId) { startTokenId = _currentIndex; _safeMint(_to, _quantityBeingClaimed); } diff --git a/contracts/prebuilts/split/Split.sol b/contracts/prebuilts/split/Split.sol index a4d18cfb9..96d539fc6 100644 --- a/contracts/prebuilts/split/Split.sol +++ b/contracts/prebuilts/split/Split.sol @@ -23,13 +23,13 @@ import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol"; // Utils -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "../../lib/FeeType.sol"; contract Split is IThirdwebContract, Initializable, - MulticallUpgradeable, + Multicall, ERC2771ContextUpgradeable, AccessControlEnumerableUpgradeable, PaymentSplitterUpgradeable diff --git a/contracts/prebuilts/staking/EditionStake.sol b/contracts/prebuilts/staking/EditionStake.sol index dbd026db9..35fc483f9 100644 --- a/contracts/prebuilts/staking/EditionStake.sol +++ b/contracts/prebuilts/staking/EditionStake.sol @@ -21,7 +21,7 @@ import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155ReceiverUpgrad import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; // Utils -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "../../lib/CurrencyTransferLib.sol"; // ========== Features ========== @@ -36,7 +36,7 @@ contract EditionStake is ContractMetadata, PermissionsEnumerable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, Staking1155Upgradeable, ERC165Upgradeable, IERC1155ReceiverUpgradeable, @@ -141,13 +141,7 @@ contract EditionStake is ERC 165 / 721 logic //////////////////////////////////////////////////////////////*/ - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes calldata - ) external view returns (bytes4) { + function onERC1155Received(address, address, uint256, uint256, bytes calldata) external view returns (bytes4) { require(isStaking == 2, "Direct transfer"); return this.onERC1155Received.selector; } @@ -160,12 +154,9 @@ contract EditionStake is bytes calldata data ) external returns (bytes4) {} - function supportsInterface(bytes4 interfaceId) - public - view - override(ERC165Upgradeable, IERC165Upgradeable) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view override(ERC165Upgradeable, IERC165Upgradeable) returns (bool) { return interfaceId == type(IERC1155ReceiverUpgradeable).interfaceId || super.supportsInterface(interfaceId); } diff --git a/contracts/prebuilts/staking/NFTStake.sol b/contracts/prebuilts/staking/NFTStake.sol index c0aa9ed63..df6c6b3a1 100644 --- a/contracts/prebuilts/staking/NFTStake.sol +++ b/contracts/prebuilts/staking/NFTStake.sol @@ -21,7 +21,7 @@ import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradea import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; // Utils -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "../../lib/CurrencyTransferLib.sol"; // ========== Features ========== @@ -36,7 +36,7 @@ contract NFTStake is ContractMetadata, PermissionsEnumerable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, Staking721Upgradeable, ERC165Upgradeable, IERC721ReceiverUpgradeable, @@ -141,12 +141,7 @@ contract NFTStake is ERC 165 / 721 logic //////////////////////////////////////////////////////////////*/ - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external view override returns (bytes4) { + function onERC721Received(address, address, uint256, bytes calldata) external view override returns (bytes4) { require(isStaking == 2, "Direct transfer"); return this.onERC721Received.selector; } diff --git a/contracts/prebuilts/staking/TokenStake.sol b/contracts/prebuilts/staking/TokenStake.sol index 5856820df..5d6c5f888 100644 --- a/contracts/prebuilts/staking/TokenStake.sol +++ b/contracts/prebuilts/staking/TokenStake.sol @@ -19,7 +19,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; // Utils -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import { CurrencyTransferLib } from "../../lib/CurrencyTransferLib.sol"; import "../../eip/interface/IERC20Metadata.sol"; @@ -35,7 +35,7 @@ contract TokenStake is ContractMetadata, PermissionsEnumerable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, Staking20Upgradeable, ITokenStake { diff --git a/contracts/prebuilts/tiered-drop/TieredDrop.sol b/contracts/prebuilts/tiered-drop/TieredDrop.sol index 6d92f36f9..9d5571ca5 100644 --- a/contracts/prebuilts/tiered-drop/TieredDrop.sol +++ b/contracts/prebuilts/tiered-drop/TieredDrop.sol @@ -14,7 +14,7 @@ pragma solidity ^0.8.11; // ========== External imports ========== -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; @@ -51,7 +51,7 @@ contract TieredDrop is PermissionsEnumerable, SignatureActionUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, ERC721AUpgradeable { using StringsUpgradeable for uint256; @@ -183,13 +183,9 @@ contract TieredDrop is } /// @dev See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721AUpgradeable, IERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721AUpgradeable, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId; } @@ -225,11 +221,10 @@ contract TieredDrop is } /// @dev Lets an account with `MINTER_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs. - function reveal(uint256 _index, bytes calldata _key) - external - onlyRole(minterRole) - returns (string memory revealedURI) - { + function reveal( + uint256 _index, + bytes calldata _key + ) external onlyRole(minterRole) returns (string memory revealedURI) { uint256 batchId = getBatchIdAtIndex(_index); revealedURI = getRevealURI(batchId, _key); @@ -246,11 +241,10 @@ contract TieredDrop is //////////////////////////////////////////////////////////////*/ /// @dev Claim lazy minted tokens via signature. - function claimWithSignature(GenericRequest calldata _req, bytes calldata _signature) - external - payable - returns (address signer) - { + function claimWithSignature( + GenericRequest calldata _req, + bytes calldata _signature + ) external payable returns (address signer) { ( string[] memory tiersInPriority, address to, @@ -292,11 +286,7 @@ contract TieredDrop is Internal functions //////////////////////////////////////////////////////////////*/ /// @dev Collects and distributes the primary sale value of NFTs being claimed. - function collectPriceOnClaim( - address _primarySaleRecipient, - address _currency, - uint256 _totalPrice - ) internal { + function collectPriceOnClaim(address _primarySaleRecipient, address _currency, uint256 _totalPrice) internal { if (_totalPrice == 0) { require(msg.value == 0, "!Value"); return; @@ -316,11 +306,7 @@ contract TieredDrop is } /// @dev Transfers the NFTs being claimed. - function transferTokensOnClaim( - address _to, - uint256 _totalQuantityBeingClaimed, - string[] memory _tiers - ) internal { + function transferTokensOnClaim(address _to, uint256 _totalQuantityBeingClaimed, string[] memory _tiers) internal { uint256 startTokenIdToMint = _currentIndex; uint256 startIdToMap = startTokenIdToMint; @@ -355,11 +341,7 @@ contract TieredDrop is } /// @dev Maps lazy minted metadata to NFT tokenIds. - function _mapTokensToTier( - string memory _tier, - uint256 _startIdToMap, - uint256 _quantity - ) private { + function _mapTokensToTier(string memory _tier, uint256 _startIdToMap, uint256 _quantity) private { uint256 nextIdFromTier = nextMetadataIdToMapFromTier[_tier]; uint256 startTokenId = _startIdToMap; @@ -408,11 +390,10 @@ contract TieredDrop is } /// @dev Returns how much of the total-quantity-to-distribute can come from the given tier. - function _getQuantityFulfilledByTier(string memory _tier, uint256 _quantity) - private - view - returns (uint256 fulfilled) - { + function _getQuantityFulfilledByTier( + string memory _tier, + uint256 _quantity + ) private view returns (uint256 fulfilled) { uint256 total = totalRemainingInTier[_tier]; if (total >= _quantity) { diff --git a/contracts/prebuilts/token/TokenERC1155.sol b/contracts/prebuilts/token/TokenERC1155.sol index 8a8939295..a3db15910 100644 --- a/contracts/prebuilts/token/TokenERC1155.sol +++ b/contracts/prebuilts/token/TokenERC1155.sol @@ -36,7 +36,7 @@ import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable. // Utils import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "../../lib/CurrencyTransferLib.sol"; import "../../lib/FeeType.sol"; import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; @@ -54,7 +54,7 @@ contract TokenERC1155 is EIP712Upgradeable, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, AccessControlEnumerableUpgradeable, ERC1155Upgradeable, ITokenERC1155, @@ -228,12 +228,10 @@ contract TokenERC1155 is /// ===== External functions ===== /// @dev See EIP-2981 - function royaltyInfo(uint256 tokenId, uint256 salePrice) - external - view - virtual - returns (address receiver, uint256 royaltyAmount) - { + function royaltyInfo( + uint256 tokenId, + uint256 salePrice + ) external view virtual returns (address receiver, uint256 royaltyAmount) { (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId); receiver = recipient; royaltyAmount = (salePrice * bps) / MAX_BPS; @@ -276,10 +274,10 @@ contract TokenERC1155 is } /// @dev Lets a module admin update the royalty bps and recipient. - function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setDefaultRoyaltyInfo( + address _royaltyRecipient, + uint256 _royaltyBps + ) external onlyRole(DEFAULT_ADMIN_ROLE) { require(_royaltyBps <= MAX_BPS, "exceed royalty bps"); royaltyRecipient = _royaltyRecipient; @@ -302,10 +300,10 @@ contract TokenERC1155 is } /// @dev Lets a module admin update the fees on primary sales. - function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setPlatformFeeInfo( + address _platformFeeRecipient, + uint256 _platformFeeBps + ) external onlyRole(DEFAULT_ADMIN_ROLE) { require(_platformFeeBps <= MAX_BPS, "exceeds MAX_BPS"); platformFeeBps = uint64(_platformFeeBps); @@ -315,10 +313,10 @@ contract TokenERC1155 is } /// @dev Lets a module admin set a flat fee on primary sales. - function setFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setFlatPlatformFeeInfo( + address _platformFeeRecipient, + uint256 _flatFee + ) external onlyRole(DEFAULT_ADMIN_ROLE) { flatPlatformFee = _flatFee; platformFeeRecipient = _platformFeeRecipient; @@ -381,12 +379,7 @@ contract TokenERC1155 is /// ===== Internal functions ===== /// @dev Mints an NFT to `to` - function _mintTo( - address _to, - string calldata _uri, - uint256 _tokenId, - uint256 _amount - ) internal { + function _mintTo(address _to, string calldata _uri, uint256 _tokenId, uint256 _amount) internal { if (bytes(_tokenURI[_tokenId]).length == 0) { _setTokenURI(_tokenId, _uri); } @@ -472,11 +465,7 @@ contract TokenERC1155 is /// ===== Low-level overrides ===== /// @dev Lets a token owner burn the tokens they own (i.e. destroy for good) - function burn( - address account, - uint256 id, - uint256 value - ) public virtual { + function burn(address account, uint256 id, uint256 value) public virtual { require( account == _msgSender() || isApprovedForAll(account, _msgSender()), "ERC1155: caller is not owner nor approved." @@ -486,11 +475,7 @@ contract TokenERC1155 is } /// @dev Lets a token owner burn multiple tokens they own at once (i.e. destroy for good) - function burnBatch( - address account, - uint256[] memory ids, - uint256[] memory values - ) public virtual { + function burnBatch(address account, uint256[] memory ids, uint256[] memory values) public virtual { require( account == _msgSender() || isApprovedForAll(account, _msgSender()), "ERC1155: caller is not owner nor approved." @@ -530,7 +515,9 @@ contract TokenERC1155 is } } - function supportsInterface(bytes4 interfaceId) + function supportsInterface( + bytes4 interfaceId + ) public view virtual diff --git a/contracts/prebuilts/token/TokenERC20.sol b/contracts/prebuilts/token/TokenERC20.sol index 69ac93c33..9fe640ce4 100644 --- a/contracts/prebuilts/token/TokenERC20.sol +++ b/contracts/prebuilts/token/TokenERC20.sol @@ -35,7 +35,7 @@ import "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgra import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; // Utils -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "../../lib/CurrencyTransferLib.sol"; import "../../lib/FeeType.sol"; @@ -46,7 +46,7 @@ contract TokenERC20 is IPlatformFee, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, ERC20BurnableUpgradeable, ERC20VotesUpgradeable, ITokenERC20, @@ -133,11 +133,7 @@ contract TokenERC20 is } /// @dev Runs on every transfer. - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal override { + function _beforeTokenTransfer(address from, address to, uint256 amount) internal override { super._beforeTokenTransfer(from, to, amount); if (!hasRole(TRANSFER_ROLE, address(0)) && from != address(0) && to != address(0)) { @@ -192,10 +188,10 @@ contract TokenERC20 is } /// @dev Lets a module admin update the fees on primary sales. - function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setPlatformFeeInfo( + address _platformFeeRecipient, + uint256 _platformFeeBps + ) external onlyRole(DEFAULT_ADMIN_ROLE) { require(_platformFeeBps <= MAX_BPS, "exceeds MAX_BPS"); platformFeeBps = uint64(_platformFeeBps); diff --git a/contracts/prebuilts/token/TokenERC721.sol b/contracts/prebuilts/token/TokenERC721.sol index 22c00caf5..4cfd93a73 100644 --- a/contracts/prebuilts/token/TokenERC721.sol +++ b/contracts/prebuilts/token/TokenERC721.sol @@ -40,7 +40,7 @@ import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; // Utils import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../extension/Multicall.sol"; import "../../lib/CurrencyTransferLib.sol"; import "../../lib/FeeType.sol"; @@ -57,7 +57,7 @@ contract TokenERC721 is ReentrancyGuardUpgradeable, EIP712Upgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, AccessControlEnumerableUpgradeable, ERC721EnumerableUpgradeable, ITokenERC721, @@ -195,24 +195,20 @@ contract TokenERC721 is /// ===== External functions ===== /// @dev See EIP-2981 - function royaltyInfo(uint256 tokenId, uint256 salePrice) - external - view - virtual - returns (address receiver, uint256 royaltyAmount) - { + function royaltyInfo( + uint256 tokenId, + uint256 salePrice + ) external view virtual returns (address receiver, uint256 royaltyAmount) { (address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId); receiver = recipient; royaltyAmount = (salePrice * bps) / MAX_BPS; } /// @dev Mints an NFT according to the provided mint request. - function mintWithSignature(MintRequest calldata _req, bytes calldata _signature) - external - payable - nonReentrant - returns (uint256 tokenIdMinted) - { + function mintWithSignature( + MintRequest calldata _req, + bytes calldata _signature + ) external payable nonReentrant returns (uint256 tokenIdMinted) { address signer = verifyRequest(_req, _signature); address receiver = _req.to; @@ -239,10 +235,10 @@ contract TokenERC721 is } /// @dev Lets a module admin update the royalty bps and recipient. - function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setDefaultRoyaltyInfo( + address _royaltyRecipient, + uint256 _royaltyBps + ) external onlyRole(DEFAULT_ADMIN_ROLE) { require(_royaltyBps <= MAX_BPS, "exceed royalty bps"); royaltyRecipient = _royaltyRecipient; @@ -265,10 +261,10 @@ contract TokenERC721 is } /// @dev Lets a module admin update the fees on primary sales. - function setPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) - external - onlyRole(DEFAULT_ADMIN_ROLE) - { + function setPlatformFeeInfo( + address _platformFeeRecipient, + uint256 _platformFeeBps + ) external onlyRole(DEFAULT_ADMIN_ROLE) { require(_platformFeeBps <= MAX_BPS, "exceeds MAX_BPS"); platformFeeBps = uint64(_platformFeeBps); @@ -404,9 +400,10 @@ contract TokenERC721 is function _beforeTokenTransfer( address from, address to, - uint256 tokenId + uint256 tokenId, + uint256 batchSize ) internal virtual override(ERC721EnumerableUpgradeable) { - super._beforeTokenTransfer(from, to, tokenId); + super._beforeTokenTransfer(from, to, tokenId, batchSize); // 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)) { @@ -424,7 +421,9 @@ contract TokenERC721 is return hasRole(METADATA_ROLE, _msgSender()); } - function supportsInterface(bytes4 interfaceId) + function supportsInterface( + bytes4 interfaceId + ) public view virtual diff --git a/contracts/prebuilts/unaudited/airdrop/AirdropERC1155.sol b/contracts/prebuilts/unaudited/airdrop/AirdropERC1155.sol index f20b8d8e0..a07588a7a 100644 --- a/contracts/prebuilts/unaudited/airdrop/AirdropERC1155.sol +++ b/contracts/prebuilts/unaudited/airdrop/AirdropERC1155.sol @@ -16,7 +16,7 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../../extension/Multicall.sol"; // ========== Internal imports ========== @@ -33,7 +33,7 @@ contract AirdropERC1155 is PermissionsEnumerable, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, IAirdropERC1155 { /*/////////////////////////////////////////////////////////////// @@ -47,7 +47,9 @@ contract AirdropERC1155 is Constructor + initializer logic //////////////////////////////////////////////////////////////*/ - constructor() initializer {} + constructor() { + _disableInitializers(); + } /// @dev Initializes the contract, like a constructor. function initialize( @@ -86,7 +88,7 @@ contract AirdropERC1155 is * which acts as operator for the tokens. * * @param _tokenAddress The contract address of the tokens to transfer. - * @param _tokenOwner The owner of the the tokens to transfer. + * @param _tokenOwner The owner of the tokens to transfer. * @param _contents List containing recipient, tokenId and amounts to airdrop. */ function airdropERC1155( diff --git a/contracts/prebuilts/unaudited/airdrop/AirdropERC1155Claimable.sol b/contracts/prebuilts/unaudited/airdrop/AirdropERC1155Claimable.sol index 35fb2f929..a7bd4966f 100644 --- a/contracts/prebuilts/unaudited/airdrop/AirdropERC1155Claimable.sol +++ b/contracts/prebuilts/unaudited/airdrop/AirdropERC1155Claimable.sol @@ -16,33 +16,28 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import { Multicall } from "../../../extension/Multicall.sol"; // ========== Internal imports ========== import "../../interface/airdrop/IAirdropERC1155Claimable.sol"; // ========== Features ========== -import "../../../extension/Ownable.sol"; import "../../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; import "../../../lib/MerkleProof.sol"; contract AirdropERC1155Claimable is Initializable, - Ownable, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, IAirdropERC1155Claimable { /*/////////////////////////////////////////////////////////////// State variables //////////////////////////////////////////////////////////////*/ - bytes32 private constant MODULE_TYPE = bytes32("AirdropERC1155Claimable"); - uint256 private constant VERSION = 1; - /// @dev address of token being airdropped. address public airdropTokenAddress; @@ -62,8 +57,8 @@ contract AirdropERC1155Claimable is /// @dev Mapping from tokenId and claimer address to total number of tokens claimed. mapping(uint256 => mapping(address => uint256)) public supplyClaimedByWallet; - /// @dev general claim limit for a tokenId if claimer not in allowlist. - mapping(uint256 => uint256) public maxWalletClaimCount; + /// @dev claim limit for open/public claiming without allowlist. + mapping(uint256 => uint256) public openClaimLimitPerWallet; /// @dev number tokens available to claim for a tokenId. mapping(uint256 => uint256) public availableAmount; @@ -75,21 +70,21 @@ contract AirdropERC1155Claimable is Constructor + initializer logic //////////////////////////////////////////////////////////////*/ - constructor() initializer {} + constructor() { + _disableInitializers(); + } /// @dev Initializes the contract, like a constructor. function initialize( - address _defaultAdmin, address[] memory _trustedForwarders, address _tokenOwner, address _airdropTokenAddress, uint256[] memory _tokenIds, uint256[] memory _availableAmounts, uint256 _expirationTimestamp, - uint256[] memory _maxWalletClaimCount, + uint256[] memory _openClaimLimitPerWallet, bytes32[] memory _merkleRoot ) external initializer { - _setupOwner(_defaultAdmin); __ReentrancyGuard_init(); __ERC2771Context_init(_trustedForwarders); @@ -99,7 +94,7 @@ contract AirdropERC1155Claimable is expirationTimestamp = _expirationTimestamp; require( - _maxWalletClaimCount.length == _tokenIds.length && + _openClaimLimitPerWallet.length == _tokenIds.length && _merkleRoot.length == _tokenIds.length && _availableAmounts.length == _tokenIds.length, "length mismatch." @@ -107,25 +102,11 @@ contract AirdropERC1155Claimable is for (uint256 i = 0; i < _tokenIds.length; i++) { merkleRoot[_tokenIds[i]] = _merkleRoot[i]; - maxWalletClaimCount[_tokenIds[i]] = _maxWalletClaimCount[i]; + openClaimLimitPerWallet[_tokenIds[i]] = _openClaimLimitPerWallet[i]; availableAmount[_tokenIds[i]] = _availableAmounts[i]; } } - /*/////////////////////////////////////////////////////////////// - Generic contract logic - //////////////////////////////////////////////////////////////*/ - - /// @dev Returns the type of the contract. - function contractType() external pure returns (bytes32) { - return MODULE_TYPE; - } - - /// @dev Returns the version of the contract. - function contractVersion() external pure returns (uint8) { - return uint8(VERSION); - } - /*/////////////////////////////////////////////////////////////// Claim logic //////////////////////////////////////////////////////////////*/ @@ -158,11 +139,7 @@ contract AirdropERC1155Claimable is } /// @dev Transfers the tokens being claimed. - function _transferClaimedTokens( - address _to, - uint256 _quantityBeingClaimed, - uint256 _tokenId - ) internal { + function _transferClaimedTokens(address _to, uint256 _quantityBeingClaimed, uint256 _tokenId) internal { // if transfer claimed tokens is called when `to != msg.sender`, it'd use msg.sender's limits. // behavior would be similar to `msg.sender` mint for itself, then transfer to `_to`. supplyClaimedByWallet[_tokenId][_msgSender()] += _quantityBeingClaimed; @@ -181,6 +158,10 @@ contract AirdropERC1155Claimable is ) public view { bool isOverride; + /* + * Here `isOverride` implies that if the merkle proof verification fails, + * the claimer would claim through open claim limit instead of allowlisted limit. + */ bytes32 mroot = merkleRoot[_tokenId]; if (mroot != bytes32(0)) { (isOverride, ) = MerkleProof.verify( @@ -198,7 +179,7 @@ contract AirdropERC1155Claimable is uint256 expTimestamp = expirationTimestamp; require(expTimestamp == 0 || block.timestamp < expTimestamp, "airdrop expired."); - uint256 claimLimitForWallet = isOverride ? _proofMaxQuantityForWallet : maxWalletClaimCount[_tokenId]; + uint256 claimLimitForWallet = isOverride ? _proofMaxQuantityForWallet : openClaimLimitPerWallet[_tokenId]; require(_quantity + supplyClaimedAlready <= claimLimitForWallet, "invalid quantity."); } @@ -206,11 +187,6 @@ contract AirdropERC1155Claimable is Miscellaneous //////////////////////////////////////////////////////////////*/ - /// @dev Returns whether owner can be set in the given execution context. - function _canSetOwner() internal view virtual override returns (bool) { - return _msgSender() == owner(); - } - function _msgSender() internal view virtual override returns (address sender) { return ERC2771ContextUpgradeable._msgSender(); } diff --git a/contracts/prebuilts/unaudited/airdrop/AirdropERC20.sol b/contracts/prebuilts/unaudited/airdrop/AirdropERC20.sol index fb3e23709..cf412467d 100644 --- a/contracts/prebuilts/unaudited/airdrop/AirdropERC20.sol +++ b/contracts/prebuilts/unaudited/airdrop/AirdropERC20.sol @@ -15,7 +15,7 @@ pragma solidity ^0.8.11; // ========== External imports ========== import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../../extension/Multicall.sol"; // ========== Internal imports ========== @@ -34,7 +34,7 @@ contract AirdropERC20 is PermissionsEnumerable, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, IAirdropERC20 { /*/////////////////////////////////////////////////////////////// @@ -48,7 +48,9 @@ contract AirdropERC20 is Constructor + initializer logic //////////////////////////////////////////////////////////////*/ - constructor() initializer {} + constructor() { + _disableInitializers(); + } /// @dev Initializes the contract, like a constructor. function initialize( @@ -87,7 +89,7 @@ contract AirdropERC20 is * which acts as operator for the tokens. * * @param _tokenAddress The contract address of the tokens to transfer. - * @param _tokenOwner The owner of the the tokens to transfer. + * @param _tokenOwner The owner of the tokens to transfer. * @param _contents List containing recipient, tokenId and amounts to airdrop. */ function airdropERC20( diff --git a/contracts/prebuilts/unaudited/airdrop/AirdropERC20Claimable.sol b/contracts/prebuilts/unaudited/airdrop/AirdropERC20Claimable.sol index 3bb676f6a..a247ece6e 100644 --- a/contracts/prebuilts/unaudited/airdrop/AirdropERC20Claimable.sol +++ b/contracts/prebuilts/unaudited/airdrop/AirdropERC20Claimable.sol @@ -16,47 +16,42 @@ pragma solidity ^0.8.11; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../../extension/Multicall.sol"; // ========== Internal imports ========== import "../../interface/airdrop/IAirdropERC20Claimable.sol"; // ========== Features ========== -import "../../../extension/Ownable.sol"; import "../../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; import "../../../lib/MerkleProof.sol"; contract AirdropERC20Claimable is Initializable, - Ownable, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, IAirdropERC20Claimable { /*/////////////////////////////////////////////////////////////// State variables //////////////////////////////////////////////////////////////*/ - bytes32 private constant MODULE_TYPE = bytes32("AirdropERC20Claimable"); - uint256 private constant VERSION = 1; - /// @dev address of token being airdropped. address public airdropTokenAddress; /// @dev address of owner of tokens being airdropped. address public tokenOwner; - /// @dev number tokens available to claim in tokenIds[]. + /// @dev number tokens available to claim. uint256 public availableAmount; /// @dev airdrop expiration timestamp. uint256 public expirationTimestamp; - /// @dev general claim limit if claimer not in allowlist. - uint256 public maxWalletClaimCount; + /// @dev claim limit for open/public claiming without allowlist. + uint256 public openClaimLimitPerWallet; /// @dev merkle root of the allowlist of addresses eligible to claim. bytes32 public merkleRoot; @@ -72,20 +67,20 @@ contract AirdropERC20Claimable is Constructor + initializer logic //////////////////////////////////////////////////////////////*/ - constructor() initializer {} + constructor() { + _disableInitializers(); + } /// @dev Initializes the contract, like a constructor. function initialize( - address _defaultAdmin, address[] memory _trustedForwarders, address _tokenOwner, address _airdropTokenAddress, uint256 _airdropAmount, uint256 _expirationTimestamp, - uint256 _maxWalletClaimCount, + uint256 _openClaimLimitPerWallet, bytes32 _merkleRoot ) external initializer { - _setupOwner(_defaultAdmin); __ReentrancyGuard_init(); __ERC2771Context_init(_trustedForwarders); @@ -93,24 +88,10 @@ contract AirdropERC20Claimable is airdropTokenAddress = _airdropTokenAddress; availableAmount = _airdropAmount; expirationTimestamp = _expirationTimestamp; - maxWalletClaimCount = _maxWalletClaimCount; + openClaimLimitPerWallet = _openClaimLimitPerWallet; merkleRoot = _merkleRoot; } - /*/////////////////////////////////////////////////////////////// - Generic contract logic - //////////////////////////////////////////////////////////////*/ - - /// @dev Returns the type of the contract. - function contractType() external pure returns (bytes32) { - return MODULE_TYPE; - } - - /// @dev Returns the version of the contract. - function contractVersion() external pure returns (uint8) { - return uint8(VERSION); - } - /*/////////////////////////////////////////////////////////////// Claim logic //////////////////////////////////////////////////////////////*/ @@ -148,6 +129,11 @@ contract AirdropERC20Claimable is uint256 _proofMaxQuantityForWallet ) public view { bool isOverride; + + /* + * Here `isOverride` implies that if the merkle proof verification fails, + * the claimer would claim through open claim limit instead of allowlisted limit. + */ if (merkleRoot != bytes32(0)) { (isOverride, ) = MerkleProof.verify( _proofs, @@ -164,7 +150,7 @@ contract AirdropERC20Claimable is uint256 expTimestamp = expirationTimestamp; require(expTimestamp == 0 || block.timestamp < expTimestamp, "airdrop expired."); - uint256 claimLimitForWallet = isOverride ? _proofMaxQuantityForWallet : maxWalletClaimCount; + uint256 claimLimitForWallet = isOverride ? _proofMaxQuantityForWallet : openClaimLimitPerWallet; require(_quantity + supplyClaimedAlready <= claimLimitForWallet, "invalid quantity."); } @@ -182,11 +168,6 @@ contract AirdropERC20Claimable is Miscellaneous //////////////////////////////////////////////////////////////*/ - /// @dev Returns whether owner can be set in the given execution context. - function _canSetOwner() internal view virtual override returns (bool) { - return _msgSender() == owner(); - } - function _msgSender() internal view virtual override returns (address sender) { return ERC2771ContextUpgradeable._msgSender(); } diff --git a/contracts/prebuilts/unaudited/airdrop/AirdropERC721.sol b/contracts/prebuilts/unaudited/airdrop/AirdropERC721.sol index 92adba356..c36c2bb3d 100644 --- a/contracts/prebuilts/unaudited/airdrop/AirdropERC721.sol +++ b/contracts/prebuilts/unaudited/airdrop/AirdropERC721.sol @@ -16,7 +16,7 @@ pragma solidity ^0.8.11; import "../../../eip/interface/IERC721.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../../extension/Multicall.sol"; // ========== Internal imports ========== @@ -33,7 +33,7 @@ contract AirdropERC721 is PermissionsEnumerable, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, IAirdropERC721 { /*/////////////////////////////////////////////////////////////// @@ -47,7 +47,9 @@ contract AirdropERC721 is Constructor + initializer logic //////////////////////////////////////////////////////////////*/ - constructor() initializer {} + constructor() { + _disableInitializers(); + } /// @dev Initializes the contract, like a constructor. function initialize( @@ -86,7 +88,7 @@ contract AirdropERC721 is * which acts as operator for the tokens. * * @param _tokenAddress The contract address of the tokens to transfer. - * @param _tokenOwner The owner of the the tokens to transfer. + * @param _tokenOwner The owner of the tokens to transfer. * @param _contents List containing recipient, tokenId to airdrop. */ function airdropERC721( diff --git a/contracts/prebuilts/unaudited/airdrop/AirdropERC721Claimable.sol b/contracts/prebuilts/unaudited/airdrop/AirdropERC721Claimable.sol index 22c0744d6..a76f08225 100644 --- a/contracts/prebuilts/unaudited/airdrop/AirdropERC721Claimable.sol +++ b/contracts/prebuilts/unaudited/airdrop/AirdropERC721Claimable.sol @@ -16,33 +16,28 @@ pragma solidity ^0.8.11; import "../../../eip/interface/IERC721.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/utils/MulticallUpgradeable.sol"; +import "../../../extension/Multicall.sol"; // ========== Internal imports ========== import "../../interface/airdrop/IAirdropERC721Claimable.sol"; // ========== Features ========== -import "../../../extension/Ownable.sol"; import "../../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; import "../../../lib/MerkleProof.sol"; contract AirdropERC721Claimable is Initializable, - Ownable, ReentrancyGuardUpgradeable, ERC2771ContextUpgradeable, - MulticallUpgradeable, + Multicall, IAirdropERC721Claimable { /*/////////////////////////////////////////////////////////////// State variables //////////////////////////////////////////////////////////////*/ - bytes32 private constant MODULE_TYPE = bytes32("AirdropERC721Claimable"); - uint256 private constant VERSION = 1; - /// @dev address of token being airdropped. address public airdropTokenAddress; @@ -61,8 +56,8 @@ contract AirdropERC721Claimable is /// @dev airdrop expiration timestamp. uint256 public expirationTimestamp; - /// @dev general claim limit if claimer not in allowlist. - uint256 public maxWalletClaimCount; + /// @dev claim limit for open/public claiming without allowlist. + uint256 public openClaimLimitPerWallet; /// @dev merkle root of the allowlist of addresses eligible to claim. bytes32 public merkleRoot; @@ -78,20 +73,20 @@ contract AirdropERC721Claimable is Constructor + initializer logic //////////////////////////////////////////////////////////////*/ - constructor() initializer {} + constructor() { + _disableInitializers(); + } /// @dev Initializes the contract, like a constructor. function initialize( - address _defaultAdmin, address[] memory _trustedForwarders, address _tokenOwner, address _airdropTokenAddress, uint256[] memory _tokenIds, uint256 _expirationTimestamp, - uint256 _maxWalletClaimCount, + uint256 _openClaimLimitPerWallet, bytes32 _merkleRoot ) external initializer { - _setupOwner(_defaultAdmin); __ReentrancyGuard_init(); __ERC2771Context_init(_trustedForwarders); @@ -99,26 +94,12 @@ contract AirdropERC721Claimable is airdropTokenAddress = _airdropTokenAddress; tokenIds = _tokenIds; expirationTimestamp = _expirationTimestamp; - maxWalletClaimCount = _maxWalletClaimCount; + openClaimLimitPerWallet = _openClaimLimitPerWallet; merkleRoot = _merkleRoot; availableAmount = _tokenIds.length; } - /*/////////////////////////////////////////////////////////////// - Generic contract logic - //////////////////////////////////////////////////////////////*/ - - /// @dev Returns the type of the contract. - function contractType() external pure returns (bytes32) { - return MODULE_TYPE; - } - - /// @dev Returns the version of the contract. - function contractVersion() external pure returns (uint8) { - return uint8(VERSION); - } - /*/////////////////////////////////////////////////////////////// Claim logic //////////////////////////////////////////////////////////////*/ @@ -156,6 +137,11 @@ contract AirdropERC721Claimable is uint256 _proofMaxQuantityForWallet ) public view { bool isOverride; + + /* + * Here `isOverride` implies that if the merkle proof verification fails, + * the claimer would claim through open claim limit instead of allowlisted limit. + */ if (merkleRoot != bytes32(0)) { (isOverride, ) = MerkleProof.verify( _proofs, @@ -172,7 +158,7 @@ contract AirdropERC721Claimable is uint256 expTimestamp = expirationTimestamp; require(expTimestamp == 0 || block.timestamp < expTimestamp, "airdrop expired."); - uint256 claimLimitForWallet = isOverride ? _proofMaxQuantityForWallet : maxWalletClaimCount; + uint256 claimLimitForWallet = isOverride ? _proofMaxQuantityForWallet : openClaimLimitPerWallet; require(_quantity + supplyClaimedAlready <= claimLimitForWallet, "invalid quantity."); } @@ -199,11 +185,6 @@ contract AirdropERC721Claimable is Miscellaneous //////////////////////////////////////////////////////////////*/ - /// @dev Returns whether owner can be set in the given execution context. - function _canSetOwner() internal view virtual override returns (bool) { - return _msgSender() == owner(); - } - function _msgSender() internal view virtual override 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 index f252e3e1a..77a14a5b9 100644 --- a/contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Logic.sol +++ b/contracts/prebuilts/unaudited/burn-to-claim-drop/extension/BurnToClaimDrop721Logic.sol @@ -14,7 +14,7 @@ pragma solidity ^0.8.11; import { BurnToClaimDrop721Storage } from "./BurnToClaimDrop721Storage.sol"; -import "../../../../lib/TWStrings.sol"; +import "../../../../lib/Strings.sol"; import "../../../../lib/CurrencyTransferLib.sol"; import { IERC2981 } from "../../../../eip/interface/IERC2981.sol"; @@ -47,7 +47,7 @@ contract BurnToClaimDrop721Logic is ERC2771ContextUpgradeable, ERC721AUpgradeable { - using TWStrings for uint256; + using Strings for uint256; /*/////////////////////////////////////////////////////////////// Constants @@ -90,13 +90,9 @@ contract BurnToClaimDrop721Logic is } /// @notice See ERC 165 - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(ERC721AUpgradeable, IERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721AUpgradeable, IERC165) returns (bool) { return super.supportsInterface(interfaceId) || type(IERC2981).interfaceId == interfaceId; } @@ -242,11 +238,10 @@ contract BurnToClaimDrop721Logic is } /// @dev Transfers the NFTs being claimed. - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - override - returns (uint256 startTokenId) - { + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal override returns (uint256 startTokenId) { ERC721AStorage.Data storage data = ERC721AStorage.erc721AStorage(); startTokenId = data._currentIndex; _safeMint(_to, _quantityBeingClaimed); diff --git a/contracts/prebuilts/unaudited/loyalty/LoyaltyPoints.sol b/contracts/prebuilts/unaudited/loyalty/LoyaltyPoints.sol index 0bf0508e4..81e32da1a 100644 --- a/contracts/prebuilts/unaudited/loyalty/LoyaltyPoints.sol +++ b/contracts/prebuilts/unaudited/loyalty/LoyaltyPoints.sol @@ -116,12 +116,10 @@ contract LoyaltyPoints is //////////////////////////////////////////////////////////////*/ /// @notice Mints tokens to a recipient using a signature from an authorized party. - function mintWithSignature(MintRequest calldata _req, bytes calldata _signature) - external - payable - nonReentrant - returns (address signer) - { + function mintWithSignature( + MintRequest calldata _req, + bytes calldata _signature + ) external payable nonReentrant returns (address signer) { signer = _processRequest(_req, _signature); address receiver = _req.to; @@ -162,11 +160,7 @@ contract LoyaltyPoints is } /// @dev Collects and distributes the primary sale value of tokens being minted. - function _collectPriceOnClaim( - address _primarySaleRecipient, - address _currency, - uint256 _price - ) internal { + function _collectPriceOnClaim(address _primarySaleRecipient, address _currency, uint256 _price) internal { if (_price == 0) { require(msg.value == 0, "!Value"); return; @@ -201,11 +195,7 @@ contract LoyaltyPoints is } /// @dev Runs on every transfer. - function _beforeTokenTransfer( - address from, - address to, - uint256 amount - ) internal override { + function _beforeTokenTransfer(address from, address to, uint256 amount) internal override { super._beforeTokenTransfer(from, to, amount); if (!hasRole(TRANSFER_ROLE, address(0)) && from != address(0) && to != address(0)) { diff --git a/echidna.config.yml b/echidna.config.yml deleted file mode 100644 index 0bdc173bd..000000000 --- a/echidna.config.yml +++ /dev/null @@ -1,15 +0,0 @@ -#corpusDir: 'corpus' -#initialize: contracts/crytic/init.json -#testMode: benchmark -#testMode: optimization -#testMode: assertion -psender: "0x40000" -deployer: "0x40000" -#sender: ["0x40000", "0x50000", "0x50001"] -sender: ["0x40000"] -testLimit: 5000 -shrinkLimit: 500 - -cryticArgs: ["--solc-remaps", "@openzeppelin=node_modules/@openzeppelin @chainlink=node_modules/@chainlink", "--solc-args", "optimize optimize-runs=800 metadata-hash=none"] -#cryticArgs: ["--compile-force-framework", "hardhat"] -#codeSize: 0xfffffffffff diff --git a/foundry.toml b/foundry.toml index 406a17df9..a86fc9b13 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,5 +1,5 @@ [profile.default] -solc-version = "0.8.12" +solc-version = "0.8.23" #auto_detect_solc = false cache = true evm_version = 'london' @@ -21,6 +21,7 @@ gas_reports = [ "TokenStakeBenchmark", "PackBenchmark", "PackVRFDirectBenchmark", + "AccountBenchmark", ] libraries = [] libs = ['lib'] @@ -37,7 +38,9 @@ remappings = [ 'erc721a-upgradeable/=lib/ERC721A-Upgradeable/', 'erc721a/=lib/ERC721A/', '@thirdweb-dev/dynamic-contracts/=lib/dynamic-contracts/', + 'lib/sstore2=lib/dynamic-contracts/lib/sstore2/', ] +fs_permissions = [{ access = "read-write", path = "./src/test/smart-wallet/utils"}] src = 'contracts' test = 'src/test' verbosity = 0 diff --git a/gasreport.txt b/gasreport.txt index 4e7192f52..ea8d57ffc 100644 --- a/gasreport.txt +++ b/gasreport.txt @@ -1,99 +1,181 @@ -Compiling 638 files with 0.8.12 -Solc 0.8.12 finished in 161.31s -Compiler run successful! +No files changed, compilation skipped + +Running 2 tests for src/test/benchmark/MultiwrapBenchmark.t.sol:MultiwrapBenchmarkTest +[PASS] test_benchmark_multiwrap_unwrap() (gas: 88950) +[PASS] test_benchmark_multiwrap_wrap() (gas: 473462) +Test result: ok. 2 passed; 0 failed; 0 skipped; finished in 236.08ms Running 3 tests for src/test/benchmark/EditionStakeBenchmark.t.sol:EditionStakeBenchmarkTest -[PASS] test_benchmark_editionStake_claimRewards() (gas: 65558) -[PASS] test_benchmark_editionStake_stake() (gas: 185123) -[PASS] test_benchmark_editionStake_withdraw() (gas: 46304) -Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 867.54ms +[PASS] test_benchmark_editionStake_claimRewards() (gas: 65081) +[PASS] test_benchmark_editionStake_stake() (gas: 185144) +[PASS] test_benchmark_editionStake_withdraw() (gas: 46364) +Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 238.67ms -Running 3 tests for src/test/benchmark/PackBenchmark.t.sol:PackBenchmarkTest -[PASS] test_benchmark_pack_addPackContents() (gas: 219427) -[PASS] test_benchmark_pack_createPack() (gas: 1425210) -[PASS] test_benchmark_pack_openPack() (gas: 154414) -Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 871.94ms +Running 3 tests for src/test/benchmark/TokenStakeBenchmark.t.sol:TokenStakeBenchmarkTest +[PASS] test_benchmark_tokenStake_claimRewards() (gas: 67554) +[PASS] test_benchmark_tokenStake_stake() (gas: 177180) +[PASS] test_benchmark_tokenStake_withdraw() (gas: 47396) +Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 241.12ms -Running 5 tests for src/test/benchmark/SignatureDropBenchmark.t.sol:SignatureDropBenchmarkTest -[PASS] test_bechmark_signatureDrop_claim_five_tokens() (gas: 141434) -[PASS] test_bechmark_signatureDrop_setClaimConditions() (gas: 73752) -[PASS] test_benchmark_signatureDrop_lazyMint() (gas: 124617) -[PASS] test_benchmark_signatureDrop_lazyMint_for_delayed_reveal() (gas: 226206) -[PASS] test_benchmark_signatureDrop_reveal() (gas: 9165) -Test result: ok. 5 passed; 0 failed; 0 skipped; finished in 872.08ms +Running 4 tests for src/test/benchmark/TokenERC1155Benchmark.t.sol:TokenERC1155BenchmarkTest +[PASS] test_benchmark_tokenERC1155_burn() (gas: 5728) +[PASS] test_benchmark_tokenERC1155_mintTo() (gas: 122286) +[PASS] test_benchmark_tokenERC1155_mintWithSignature_pay_with_ERC20() (gas: 267175) +[PASS] test_benchmark_tokenERC1155_mintWithSignature_pay_with_native_token() (gas: 296172) +Test result: ok. 4 passed; 0 failed; 0 skipped; finished in 240.92ms -Running 2 tests for src/test/benchmark/MultiwrapBenchmark.t.sol:MultiwrapBenchmarkTest -[PASS] test_benchmark_multiwrap_unwrap() (gas: 92386) -[PASS] test_benchmark_multiwrap_wrap() (gas: 475292) -Test result: ok. 2 passed; 0 failed; 0 skipped; finished in 873.82ms +Running 1 test for src/test/benchmark/AirdropERC1155Benchmark.t.sol:AirdropERC1155BenchmarkTest +[PASS] test_benchmark_airdropERC1155_airdrop() (gas: 38083572) +Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 269.27ms Running 1 test for src/test/benchmark/AirdropERC20Benchmark.t.sol:AirdropERC20BenchmarkTest -[PASS] test_benchmark_airdropERC20_airdrop() (gas: 32173688) -Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 886.88ms - -Running 1 test for src/test/benchmark/AirdropERC1155Benchmark.t.sol:AirdropERC1155BenchmarkTest -[PASS] test_benchmark_airdropERC1155_airdrop() (gas: 38078671) -Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 891.33ms +[PASS] test_benchmark_airdropERC20_airdrop() (gas: 32068413) +Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 270.94ms -Running 4 tests for src/test/benchmark/TokenERC1155Benchmark.t.sol:TokenERC1155BenchmarkTest -[PASS] test_benchmark_tokenERC1155_burn() (gas: 5726) -[PASS] test_benchmark_tokenERC1155_mintTo() (gas: 121006) -[PASS] test_benchmark_tokenERC1155_mintWithSignature_pay_with_ERC20() (gas: 263735) -[PASS] test_benchmark_tokenERC1155_mintWithSignature_pay_with_native_token() (gas: 292233) -Test result: ok. 4 passed; 0 failed; 0 skipped; finished in 894.20ms +Running 1 test for src/test/smart-wallet/utils/AABenchmarkPrepare.sol:AABenchmarkPrepare +[PASS] test_prepareBenchmarkFile() (gas: 2926370) +Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 272.43ms Running 1 test for src/test/benchmark/AirdropERC721Benchmark.t.sol:AirdropERC721BenchmarkTest -[PASS] test_benchmark_airdropERC721_airdrop() (gas: 43892679) -Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 902.28ms +[PASS] test_benchmark_airdropERC721_airdrop() (gas: 41912536) +Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 297.69ms -Running 3 tests for src/test/benchmark/PackVRFDirectBenchmark.t.sol:PackVRFDirectBenchmarkTest -[PASS] test_benchmark_packvrf_createPack() (gas: 1391844) -[PASS] test_benchmark_packvrf_openPack() (gas: 119959) -[PASS] test_benchmark_packvrf_openPackAndClaimRewards() (gas: 3599) -Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 212.61ms - -Running 3 tests for src/test/benchmark/TokenStakeBenchmark.t.sol:TokenStakeBenchmarkTest -[PASS] test_benchmark_tokenStake_claimRewards() (gas: 68043) -[PASS] test_benchmark_tokenStake_stake() (gas: 177989) -[PASS] test_benchmark_tokenStake_withdraw() (gas: 47868) -Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 205.79ms +Running 4 tests for src/test/benchmark/TokenERC721Benchmark.t.sol:TokenERC721BenchmarkTest +[PASS] test_benchmark_tokenERC721_burn() (gas: 8954) +[PASS] test_benchmark_tokenERC721_mintTo() (gas: 151552) +[PASS] test_benchmark_tokenERC721_mintWithSignature_pay_with_ERC20() (gas: 262344) +[PASS] test_benchmark_tokenERC721_mintWithSignature_pay_with_native_token() (gas: 286914) +Test result: ok. 4 passed; 0 failed; 0 skipped; finished in 242.17ms Running 3 tests for src/test/benchmark/NFTStakeBenchmark.t.sol:NFTStakeBenchmarkTest -[PASS] test_benchmark_nftStake_claimRewards() (gas: 68804) -[PASS] test_benchmark_nftStake_stake_five_tokens() (gas: 549399) -[PASS] test_benchmark_nftStake_withdraw() (gas: 39631) -Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 207.68ms +[PASS] test_benchmark_nftStake_claimRewards() (gas: 68287) +[PASS] test_benchmark_nftStake_stake_five_tokens() (gas: 539145) +[PASS] test_benchmark_nftStake_withdraw() (gas: 38076) +Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 279.71ms -Running 4 tests for src/test/benchmark/TokenERC721Benchmark.t.sol:TokenERC721BenchmarkTest -[PASS] test_benchmark_tokenERC721_burn() (gas: 10393) -[PASS] test_benchmark_tokenERC721_mintTo() (gas: 149955) -[PASS] test_benchmark_tokenERC721_mintWithSignature_pay_with_ERC20() (gas: 259360) -[PASS] test_benchmark_tokenERC721_mintWithSignature_pay_with_native_token() (gas: 283702) -Test result: ok. 4 passed; 0 failed; 0 skipped; finished in 228.01ms +Running 5 tests for src/test/benchmark/SignatureDropBenchmark.t.sol:SignatureDropBenchmarkTest +[PASS] test_benchmark_signatureDrop_claim_five_tokens() (gas: 140517) +[PASS] test_benchmark_signatureDrop_lazyMint() (gas: 124311) +[PASS] test_benchmark_signatureDrop_lazyMint_for_delayed_reveal() (gas: 225891) +[PASS] test_benchmark_signatureDrop_reveal() (gas: 10647) +[PASS] test_benchmark_signatureDrop_setClaimConditions() (gas: 73699) +Test result: ok. 5 passed; 0 failed; 0 skipped; finished in 251.39ms Running 3 tests for src/test/benchmark/TokenERC20Benchmark.t.sol:TokenERC20BenchmarkTest -[PASS] test_benchmark_tokenERC20_mintTo() (gas: 118511) -[PASS] test_benchmark_tokenERC20_mintWithSignature_pay_with_ERC20() (gas: 181716) -[PASS] test_benchmark_tokenERC20_mintWithSignature_pay_with_native_token() (gas: 206128) -Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 215.25ms +[PASS] test_benchmark_tokenERC20_mintTo() (gas: 118586) +[PASS] test_benchmark_tokenERC20_mintWithSignature_pay_with_ERC20() (gas: 183032) +[PASS] test_benchmark_tokenERC20_mintWithSignature_pay_with_native_token() (gas: 207694) +Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 287.73ms + +Running 14 tests for src/test/benchmark/AccountBenchmark.t.sol:AccountBenchmarkTest +[PASS] test_state_accountReceivesNativeTokens() (gas: 11037) +[PASS] test_state_addAndWithdrawDeposit() (gas: 83332) +[PASS] test_state_contractMetadata() (gas: 56507) +[PASS] test_state_createAccount_viaEntrypoint() (gas: 432040) +[PASS] test_state_createAccount_viaFactory() (gas: 334122) +[PASS] test_state_executeBatchTransaction() (gas: 39874) +[PASS] test_state_executeBatchTransaction_viaAccountSigner() (gas: 392782) +[PASS] test_state_executeBatchTransaction_viaEntrypoint() (gas: 82915) +[PASS] test_state_executeTransaction() (gas: 35735) +[PASS] test_state_executeTransaction_viaAccountSigner() (gas: 378632) +[PASS] test_state_executeTransaction_viaEntrypoint() (gas: 75593) +[PASS] test_state_receiveERC1155NFT() (gas: 39343) +[PASS] test_state_receiveERC721NFT() (gas: 78624) +[PASS] test_state_transferOutsNativeTokens() (gas: 81713) +Test result: ok. 14 passed; 0 failed; 0 skipped; finished in 364.96ms -Running 2 tests for src/test/benchmark/DropERC20Benchmark.t.sol:DropERC20BenchmarkTest -[PASS] test_bechmark_dropERC20_claim() (gas: 230419) -[PASS] test_bechmark_dropERC20_setClaimConditions_five_conditions() (gas: 501023) -Test result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.21s +Running 3 tests for src/test/benchmark/PackBenchmark.t.sol:PackBenchmarkTest +[PASS] test_benchmark_pack_addPackContents() (gas: 219188) +[PASS] test_benchmark_pack_createPack() (gas: 1412868) +[PASS] test_benchmark_pack_openPack() (gas: 141860) +Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 258.39ms + +Running 3 tests for src/test/benchmark/PackVRFDirectBenchmark.t.sol:PackVRFDirectBenchmarkTest +[PASS] test_benchmark_packvrf_createPack() (gas: 1379604) +[PASS] test_benchmark_packvrf_openPack() (gas: 119953) +[PASS] test_benchmark_packvrf_openPackAndClaimRewards() (gas: 3621) +Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 285.64ms Running 3 tests for src/test/benchmark/DropERC1155Benchmark.t.sol:DropERC1155BenchmarkTest -[PASS] test_bechmark_dropERC1155_claim() (gas: 186646) -[PASS] test_bechmark_dropERC1155_setClaimConditions_five_conditions() (gas: 492309) -[PASS] test_benchmark_dropERC1155_lazyMint() (gas: 123851) -Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 1.22s +[PASS] test_benchmark_dropERC1155_claim() (gas: 185032) +[PASS] test_benchmark_dropERC1155_lazyMint() (gas: 123913) +[PASS] test_benchmark_dropERC1155_setClaimConditions_five_conditions() (gas: 492121) +Test result: ok. 3 passed; 0 failed; 0 skipped; finished in 735.56ms Running 5 tests for src/test/benchmark/DropERC721Benchmark.t.sol:DropERC721BenchmarkTest -[PASS] test_bechmark_dropERC721_claim_five_tokens() (gas: 211945) -[PASS] test_bechmark_dropERC721_setClaimConditions_five_conditions() (gas: 500837) -[PASS] test_benchmark_dropERC721_lazyMint() (gas: 124522) -[PASS] test_benchmark_dropERC721_lazyMint_for_delayed_reveal() (gas: 226102) -[PASS] test_benchmark_dropERC721_reveal() (gas: 8783) -Test result: ok. 5 passed; 0 failed; 0 skipped; finished in 636.48ms - -Ran 16 test suites: 46 tests passed, 0 failed, 0 skipped (46 total tests) +[PASS] test_benchmark_dropERC721_claim_five_tokens() (gas: 210967) +[PASS] test_benchmark_dropERC721_lazyMint() (gas: 124540) +[PASS] test_benchmark_dropERC721_lazyMint_for_delayed_reveal() (gas: 226149) +[PASS] test_benchmark_dropERC721_reveal() (gas: 13732) +[PASS] test_benchmark_dropERC721_setClaimConditions_five_conditions() (gas: 500494) +Test result: ok. 5 passed; 0 failed; 0 skipped; finished in 742.03ms + +Running 2 tests for src/test/benchmark/DropERC20Benchmark.t.sol:DropERC20BenchmarkTest +[PASS] test_benchmark_dropERC20_claim() (gas: 230505) +[PASS] test_benchmark_dropERC20_setClaimConditions_five_conditions() (gas: 500858) +Test result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.12s + + +Ran 18 test suites: 61 tests passed, 0 failed, 0 skipped (61 total tests) +test_state_accountReceivesNativeTokens() (gas: 0 (0.000%)) +test_state_addAndWithdrawDeposit() (gas: 0 (0.000%)) +test_state_contractMetadata() (gas: 0 (0.000%)) +test_state_createAccount_viaEntrypoint() (gas: 0 (0.000%)) +test_state_createAccount_viaFactory() (gas: 0 (0.000%)) +test_state_executeBatchTransaction() (gas: 0 (0.000%)) +test_state_executeBatchTransaction_viaAccountSigner() (gas: 0 (0.000%)) +test_state_executeBatchTransaction_viaEntrypoint() (gas: 0 (0.000%)) +test_state_executeTransaction() (gas: 0 (0.000%)) +test_state_executeTransaction_viaAccountSigner() (gas: 0 (0.000%)) +test_state_executeTransaction_viaEntrypoint() (gas: 0 (0.000%)) +test_state_receiveERC1155NFT() (gas: 0 (0.000%)) +test_state_receiveERC721NFT() (gas: 0 (0.000%)) +test_state_transferOutsNativeTokens() (gas: 0 (0.000%)) +test_benchmark_airdropERC1155_airdrop() (gas: 0 (0.000%)) +test_benchmark_airdropERC20_airdrop() (gas: 0 (0.000%)) +test_benchmark_airdropERC721_airdrop() (gas: 0 (0.000%)) +test_benchmark_dropERC1155_claim() (gas: 0 (0.000%)) +test_benchmark_dropERC1155_lazyMint() (gas: 0 (0.000%)) +test_benchmark_dropERC1155_setClaimConditions_five_conditions() (gas: 0 (0.000%)) +test_benchmark_dropERC20_claim() (gas: 0 (0.000%)) +test_benchmark_dropERC20_setClaimConditions_five_conditions() (gas: 0 (0.000%)) +test_benchmark_dropERC721_claim_five_tokens() (gas: 0 (0.000%)) +test_benchmark_dropERC721_lazyMint() (gas: 0 (0.000%)) +test_benchmark_dropERC721_lazyMint_for_delayed_reveal() (gas: 0 (0.000%)) +test_benchmark_dropERC721_reveal() (gas: 0 (0.000%)) +test_benchmark_dropERC721_setClaimConditions_five_conditions() (gas: 0 (0.000%)) +test_benchmark_editionStake_claimRewards() (gas: 0 (0.000%)) +test_benchmark_editionStake_stake() (gas: 0 (0.000%)) +test_benchmark_editionStake_withdraw() (gas: 0 (0.000%)) +test_benchmark_multiwrap_unwrap() (gas: 0 (0.000%)) +test_benchmark_multiwrap_wrap() (gas: 0 (0.000%)) +test_benchmark_nftStake_claimRewards() (gas: 0 (0.000%)) +test_benchmark_nftStake_stake_five_tokens() (gas: 0 (0.000%)) +test_benchmark_nftStake_withdraw() (gas: 0 (0.000%)) +test_benchmark_pack_addPackContents() (gas: 0 (0.000%)) +test_benchmark_pack_createPack() (gas: 0 (0.000%)) +test_benchmark_pack_openPack() (gas: 0 (0.000%)) +test_benchmark_packvrf_createPack() (gas: 0 (0.000%)) +test_benchmark_packvrf_openPack() (gas: 0 (0.000%)) +test_benchmark_packvrf_openPackAndClaimRewards() (gas: 0 (0.000%)) +test_benchmark_signatureDrop_claim_five_tokens() (gas: 0 (0.000%)) +test_benchmark_signatureDrop_lazyMint() (gas: 0 (0.000%)) +test_benchmark_signatureDrop_lazyMint_for_delayed_reveal() (gas: 0 (0.000%)) +test_benchmark_signatureDrop_reveal() (gas: 0 (0.000%)) +test_benchmark_signatureDrop_setClaimConditions() (gas: 0 (0.000%)) +test_benchmark_tokenERC1155_burn() (gas: 0 (0.000%)) +test_benchmark_tokenERC1155_mintTo() (gas: 0 (0.000%)) +test_benchmark_tokenERC1155_mintWithSignature_pay_with_ERC20() (gas: 0 (0.000%)) +test_benchmark_tokenERC1155_mintWithSignature_pay_with_native_token() (gas: 0 (0.000%)) +test_benchmark_tokenERC20_mintTo() (gas: 0 (0.000%)) +test_benchmark_tokenERC20_mintWithSignature_pay_with_ERC20() (gas: 0 (0.000%)) +test_benchmark_tokenERC20_mintWithSignature_pay_with_native_token() (gas: 0 (0.000%)) +test_benchmark_tokenERC721_burn() (gas: 0 (0.000%)) +test_benchmark_tokenERC721_mintTo() (gas: 0 (0.000%)) +test_benchmark_tokenERC721_mintWithSignature_pay_with_ERC20() (gas: 0 (0.000%)) +test_benchmark_tokenERC721_mintWithSignature_pay_with_native_token() (gas: 0 (0.000%)) +test_benchmark_tokenStake_claimRewards() (gas: 0 (0.000%)) +test_benchmark_tokenStake_stake() (gas: 0 (0.000%)) +test_benchmark_tokenStake_withdraw() (gas: 0 (0.000%)) +test_prepareBenchmarkFile() (gas: 0 (0.000%)) +Overall gas change: 0 (0.000%) diff --git a/lib/chainlink b/lib/chainlink index 9d5ec20aa..5d44bd4e8 160000 --- a/lib/chainlink +++ b/lib/chainlink @@ -1 +1 @@ -Subproject commit 9d5ec20aa7c03c5f08722fa88f621075d300dcc1 +Subproject commit 5d44bd4e8fa2bdc80228a0df891960d72246b645 diff --git a/lib/ds-test b/lib/ds-test index 6da7dd8f7..e282159d5 160000 --- a/lib/ds-test +++ b/lib/ds-test @@ -1 +1 @@ -Subproject commit 6da7dd8f7395f83e1fb6fa88a64ba9a030f85d4f +Subproject commit e282159d5170298eb2455a6c05280ab5a73a4ef0 diff --git a/lib/forge-std b/lib/forge-std index c0c6a4206..2f1126975 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit c0c6a4206531a3f785538240412ea2467ef58ebf +Subproject commit 2f112697506eab12d433a65fdc31a639548fe365 diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts index ecd2ca2cd..fd81a96f0 160000 --- a/lib/openzeppelin-contracts +++ b/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit ecd2ca2cd7cac116f7a37d0e474bbb3d7d5e1c4d +Subproject commit fd81a96f01cc42ef1c9a5399364968d0e07e9e90 diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable index 0a2cb9a44..3d4c0d574 160000 --- a/lib/openzeppelin-contracts-upgradeable +++ b/lib/openzeppelin-contracts-upgradeable @@ -1 +1 @@ -Subproject commit 0a2cb9a445c365870ed7a8ab461b12acf3e27d63 +Subproject commit 3d4c0d5741b131c231e558d7a6213392ab3672a5 diff --git a/manticore/weth.py b/manticore/weth.py deleted file mode 100644 index 65407a900..000000000 --- a/manticore/weth.py +++ /dev/null @@ -1,62 +0,0 @@ -from manticore.ethereum import ManticoreEVM, ABI -from manticore.core.smtlib import Operators, Z3Solver -from manticore.utils import config -from manticore.core.plugin import Plugin - -m = ManticoreEVM() - -# Disable the gas tracking -consts_evm = config.get_group("evm") -consts_evm.oog = "ignore" - -# Increase the solver timeout -config.get_group("smt").defaultunsat = False -config.get_group("smt").timeout = 3600 - -ETHER = 10 ** 18 - -deployer = m.create_account(balance=100 * ETHER, name="deployer") -user = m.create_account(balance=100 * ETHER, name="user") -attacker = m.create_account(balance=100 * ETHER, name="attacker") -print(f'[+] Created user wallet. deployer: {hex(deployer.address)}, user: {hex(user.address)}, attacker: {hex(attacker.address)}') - -contract = m.solidity_create_contract('src/test/mocks/WETH9.sol', contract_name='WETH9', owner=deployer, compile_args={ - "solc_remaps": "@openzeppelin=node_modules/@openzeppelin @chainlink=node_modules/@chainlink", - "solc_args": "optimize optimize-runs=800 metadata-hash=none" -}) -print(f'[+] Deployed contract. address: {hex(contract.address)}') - -print(f'[+] Declaring symbolic variables.') -x = m.make_symbolic_value() -v = m.make_symbolic_value() - -print(f'[+] Calling contract functions sequences.') - -# contrainsts -m.constrain(x > 0) - -# outline the transactions -m.transaction(caller=attacker, address=contract, value=v, data=m.make_symbolic_buffer(4+32*4)) -contract.withdraw(x, caller=attacker) - -print(f"[+] There are {m.count_all_states()} states. ({m.count_ready_states()} ready, {m.count_terminated_states()} terminated, {m.count_busy_states()} alive).") -m.take_snapshot() - -print(f'[+] Generating symbolic conditional transactions traces.') -num_state_found = 0 -for state in m.ready_states: - # withdrawing more than deposited - condition = x > v - if m.generate_testcase(state, only_if=condition, name="constraint"): - num_state_found += 1 -print(f"[+] {num_state_found} constraints test cases generated.") - -m.goto_snapshot() -m.take_snapshot() -print(f'[+] Finalizing transactions.') -m.finalize(only_alive_states=True) - -m.goto_snapshot() -m.take_snapshot() -print(f"[+] Global coverage: {contract.address:x} - {m.global_coverage(contract)}%") -print(f'[+] Results in workspace: {m.workspace}') diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..47354e928 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5827 @@ +{ + "name": "@thirdweb-dev/contracts", + "version": "2.3.0-6", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@thirdweb-dev/contracts", + "version": "2.3.0-6", + "license": "Apache-2.0", + "devDependencies": { + "@chainlink/contracts": "^0.8.0", + "@openzeppelin/contracts": "^4.9.3", + "@openzeppelin/contracts-upgradeable": "^4.9.3", + "@thirdweb-dev/chains": "^0.1.54", + "@thirdweb-dev/dynamic-contracts": "^1.2.4", + "@thirdweb-dev/sdk": "^4.0.16", + "@typechain/ethers-v5": "^10.2.1", + "@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", + "dotenv": "^16.3.1", + "erc721a": "3.3.0", + "erc721a-upgradeable": "^3.3.0", + "eslint": "^8.54.0", + "eslint-config-prettier": "^8.10.0", + "ethers": "^5.7.2", + "fs-extra": "^10.1.0", + "keccak256": "^1.0.6", + "mocha": "^9.2.2", + "prettier": "^2.8.8", + "prettier-plugin-solidity": "^1.2.0", + "solhint": "^3.6.2", + "solhint-plugin-prettier": "^0.0.5", + "ts-node": "^10.9.1", + "tslib": "^2.6.2", + "tsup": "^5.12.9", + "typechain": "^8.3.2", + "typescript": "^4.9.5" + }, + "peerDependencies": { + "ethers": "^5.0.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz", + "integrity": "sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@chainlink/contracts": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.8.0.tgz", + "integrity": "sha512-nUv1Uxw5Mn92wgLs2bgPYmo8hpdQ3s9jB/lcbdU0LmNOVu0hbfmouVnqwRLa28Ll50q6GczUA+eO0ikNIKLZsA==", + "dev": true, + "dependencies": { + "@eth-optimism/contracts": "^0.5.21", + "@openzeppelin/contracts": "~4.3.3", + "@openzeppelin/contracts-upgradeable-4.7.3": "npm:@openzeppelin/contracts-upgradeable@v4.7.3", + "@openzeppelin/contracts-v0.7": "npm:@openzeppelin/contracts@v3.4.2" + } + }, + "node_modules/@chainlink/contracts/node_modules/@openzeppelin/contracts": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.3.3.tgz", + "integrity": "sha512-tDBopO1c98Yk7Cv/PZlHqrvtVjlgK5R4J6jxLwoO7qxK4xqOiZG+zSkIvGFpPZ0ikc3QOED3plgdqjgNTnBc7g==", + "dev": true + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz", + "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.54.0.tgz", + "integrity": "sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@eth-optimism/contracts": { + "version": "0.5.40", + "resolved": "https://registry.npmjs.org/@eth-optimism/contracts/-/contracts-0.5.40.tgz", + "integrity": "sha512-MrzV0nvsymfO/fursTB7m/KunkPsCndltVgfdHaT1Aj5Vi6R/doKIGGkOofHX+8B6VMZpuZosKCMQ5lQuqjt8w==", + "dev": true, + "dependencies": { + "@eth-optimism/core-utils": "0.12.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0" + }, + "peerDependencies": { + "ethers": "^5" + } + }, + "node_modules/@eth-optimism/core-utils": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eth-optimism/core-utils/-/core-utils-0.12.0.tgz", + "integrity": "sha512-qW+7LZYCz7i8dRa7SRlUKIo1VBU8lvN0HeXCxJR+z+xtMzMQpPds20XJNCMclszxYQHkXY00fOT6GvFw9ZL6nw==", + "dev": true, + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/contracts": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/providers": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bufio": "^1.0.7", + "chai": "^4.3.4" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ] + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "node_modules/@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@multiformats/base-x": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@multiformats/base-x/-/base-x-4.0.1.tgz", + "integrity": "sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw==", + "dev": true + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@openzeppelin/contracts": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.3.tgz", + "integrity": "sha512-He3LieZ1pP2TNt5JbkPA4PNT9WC3gOTOlDcFGJW4Le4QKqwmiNJCRt44APfxMxvq7OugU/cqYuPcSBzOw38DAg==", + "dev": true + }, + "node_modules/@openzeppelin/contracts-upgradeable": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.3.tgz", + "integrity": "sha512-jjaHAVRMrE4UuZNfDwjlLGDxTHWIOwTJS2ldnc278a0gevfXfPr8hxKEVBGFBE96kl2G3VHDZhUimw/+G3TG2A==", + "dev": true + }, + "node_modules/@openzeppelin/contracts-upgradeable-4.7.3": { + "name": "@openzeppelin/contracts-upgradeable", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.7.3.tgz", + "integrity": "sha512-+wuegAMaLcZnLCJIvrVUDzA9z/Wp93f0Dla/4jJvIhijRrPabjQbZe6fWiECLaJyfn5ci9fqf9vTw3xpQOad2A==", + "dev": true + }, + "node_modules/@openzeppelin/contracts-v0.7": { + "name": "@openzeppelin/contracts", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-3.4.2.tgz", + "integrity": "sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA==", + "dev": true + }, + "node_modules/@solidity-parser/parser": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.2.tgz", + "integrity": "sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==", + "dev": true, + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "node_modules/@thirdweb-dev/chains": { + "version": "0.1.58", + "resolved": "https://registry.npmjs.org/@thirdweb-dev/chains/-/chains-0.1.58.tgz", + "integrity": "sha512-prSShAWoLODuZQcDBwNDqcXLzfevV2BOw54cDaHetSP+Sw/BP6SaPKIxojRQGsXARjn0JMWniG/NCtppUUHALQ==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@thirdweb-dev/contracts": { + "version": "3.10.3", + "resolved": "https://registry.npmjs.org/@thirdweb-dev/contracts/-/contracts-3.10.3.tgz", + "integrity": "sha512-wSVNaEoosn0AgUtnxlvv7rgK+3EUMzJm2ZasofPgJgqGS3gYH5nDBmK29VMquA2BLc38OAPyYMWc/iQCiCikMg==", + "dev": true, + "dependencies": { + "@chainlink/contracts": "^0.6.1", + "@openzeppelin/contracts": "4.7.3", + "@openzeppelin/contracts-upgradeable": "4.7.3", + "@thirdweb-dev/dynamic-contracts": "^1.1.2", + "erc721a-upgradeable": "^3.3.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@thirdweb-dev/contracts-js": { + "version": "1.3.16", + "resolved": "https://registry.npmjs.org/@thirdweb-dev/contracts-js/-/contracts-js-1.3.16.tgz", + "integrity": "sha512-EpLcD5mdm8b+tvSO7gD9cxSAqjLRr7ygktMp4Pe7Wvobl5ffq8O95futxdVsYc5pyciPZYr8apHUJFYMDlaTqA==", + "dev": true, + "dependencies": { + "@thirdweb-dev/contracts": "3.10.3" + }, + "peerDependencies": { + "ethers": "^5" + } + }, + "node_modules/@thirdweb-dev/contracts/node_modules/@chainlink/contracts": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.6.1.tgz", + "integrity": "sha512-EuwijGexttw0UjfrW+HygwhQIrGAbqpf1ue28R55HhWMHBzphEH0PhWm8DQmFfj5OZNy8Io66N4L0nStkZ3QKQ==", + "dev": true, + "dependencies": { + "@eth-optimism/contracts": "^0.5.21", + "@openzeppelin/contracts": "~4.3.3", + "@openzeppelin/contracts-upgradeable": "^4.7.3", + "@openzeppelin/contracts-v0.7": "npm:@openzeppelin/contracts@v3.4.2" + } + }, + "node_modules/@thirdweb-dev/contracts/node_modules/@chainlink/contracts/node_modules/@openzeppelin/contracts": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.3.3.tgz", + "integrity": "sha512-tDBopO1c98Yk7Cv/PZlHqrvtVjlgK5R4J6jxLwoO7qxK4xqOiZG+zSkIvGFpPZ0ikc3QOED3plgdqjgNTnBc7g==", + "dev": true + }, + "node_modules/@thirdweb-dev/contracts/node_modules/@openzeppelin/contracts": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.7.3.tgz", + "integrity": "sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw==", + "dev": true + }, + "node_modules/@thirdweb-dev/contracts/node_modules/@openzeppelin/contracts-upgradeable": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.7.3.tgz", + "integrity": "sha512-+wuegAMaLcZnLCJIvrVUDzA9z/Wp93f0Dla/4jJvIhijRrPabjQbZe6fWiECLaJyfn5ci9fqf9vTw3xpQOad2A==", + "dev": true + }, + "node_modules/@thirdweb-dev/crypto": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@thirdweb-dev/crypto/-/crypto-0.2.0.tgz", + "integrity": "sha512-hQwSCL/imqSCcnUXlGqJi6dfs4UOcJ91Eq/t1cPXyAb6nwvyaePZPVFqGDglZMQvkS/NWZhifXZINRiCfazn2w==", + "dev": true, + "dependencies": { + "@noble/hashes": "^1.3.2", + "js-sha3": "^0.9.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@thirdweb-dev/crypto/node_modules/js-sha3": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.9.2.tgz", + "integrity": "sha512-8kgvwd03wNGQG1GRvl3yy1Yt40sICAcIMsDU2ZLgoL0Z6z9rkRmf9Vd+bi/gYSzgAqMUGl/jiDKu0J8AWFd+BQ==", + "dev": true + }, + "node_modules/@thirdweb-dev/dynamic-contracts": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@thirdweb-dev/dynamic-contracts/-/dynamic-contracts-1.2.4.tgz", + "integrity": "sha512-cQtUznRXBDifzME3zmppVrfBM2Aw8C/okCLzsgcLU/Qr68TjLJTKTDGt2uGo/q5qAvRVJjQRD/bNvV1QTqjqSg==", + "dev": true + }, + "node_modules/@thirdweb-dev/generated-abis": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@thirdweb-dev/generated-abis/-/generated-abis-0.0.1.tgz", + "integrity": "sha512-vO9/3lSLO8smyyH1QVeYravSTzFwV1nf1C/Im1NBDPdH8//YvcbhtETGGiNfHWpyCvSi0vRYwvf+/7FKdwpDGQ==", + "dev": true + }, + "node_modules/@thirdweb-dev/merkletree": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@thirdweb-dev/merkletree/-/merkletree-0.2.0.tgz", + "integrity": "sha512-4KoH2EOCWKiaHfhDO5Tnf1HjeCXKVfLt31y0kcSG5C0gCldnhm7i1fGUB8e0hW3trfyPQAuSgyP67Ep3UwzClg==", + "dev": true, + "dependencies": { + "@thirdweb-dev/crypto": "0.2.0", + "buffer": "^6.0.3", + "buffer-reverse": "^1.0.1", + "treeify": "^1.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@thirdweb-dev/sdk": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@thirdweb-dev/sdk/-/sdk-4.0.16.tgz", + "integrity": "sha512-WTG3sJTtEUpbY1SUd62GU//5npkgVQNJHOUqDjGl1jrTa8Ml0w1VQNe9p/0xbv+3cCTGYOhMvM4HkOZnrQp+gg==", + "dev": true, + "dependencies": { + "@thirdweb-dev/chains": "0.1.58", + "@thirdweb-dev/contracts-js": "1.3.16", + "@thirdweb-dev/crypto": "0.2.0", + "@thirdweb-dev/generated-abis": "0.0.1", + "@thirdweb-dev/merkletree": "0.2.0", + "@thirdweb-dev/storage": "2.0.5", + "abitype": "^0.2.5", + "bn.js": "^5.2.1", + "bs58": "^5.0.0", + "buffer": "^6.0.3", + "eventemitter3": "^5.0.1", + "fast-deep-equal": "^3.1.3", + "tiny-invariant": "^1.2.0", + "tweetnacl": "^1.0.3", + "uuid": "^9.0.1", + "yaml": "^2.3.1", + "zod": "^3.22.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@aws-sdk/client-secrets-manager": "^3.215.0", + "ethers": "^5", + "ethers-aws-kms-signer": "^1.3.2", + "zksync-web3": "^0.14.3" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-secrets-manager": { + "optional": true + }, + "ethers-aws-kms-signer": { + "optional": true + }, + "zksync-web3": { + "optional": true + } + } + }, + "node_modules/@thirdweb-dev/storage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@thirdweb-dev/storage/-/storage-2.0.5.tgz", + "integrity": "sha512-I3DK/ZNWOMa/XE2hfJnGKVfc9INn5c3if1qavyK/1fjJBxhUiUXjT59UYbuoWhHLEq0rS/QZVOGS/9qcOs/DAQ==", + "dev": true, + "dependencies": { + "@thirdweb-dev/crypto": "0.2.0", + "cid-tool": "^3.0.0", + "form-data": "^4.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@typechain/ethers-v5": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-10.2.1.tgz", + "integrity": "sha512-n3tQmCZjRE6IU4h6lqUGiQ1j866n5MTCBJreNEHHVWXa2u9GJTaeYyU1/k+1qLutkyw+sS6VAN+AbeiTqsxd/A==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15", + "ts-essentials": "^7.0.1" + }, + "peerDependencies": { + "@ethersproject/abi": "^5.0.0", + "@ethersproject/providers": "^5.0.0", + "ethers": "^5.1.3", + "typechain": "^8.1.1", + "typescript": ">=4.3.0" + } + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/abitype": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-0.2.5.tgz", + "integrity": "sha512-t1iiokWYpkrziu4WL2Gb6YdGvaP9ZKs7WnA39TI8TsW2E99GVRgDPW/xOKhzoCdyxOYt550CNYEFluCwGaFHaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wagmi-dev" + } + ], + "engines": { + "pnpm": ">=7" + }, + "peerDependencies": { + "typescript": ">=4.7.4", + "zod": ">=3.19.1" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", + "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", + "dev": true + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antlr4": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.13.1.tgz", + "integrity": "sha512-kiXTspaRYvnIArgE97z5YVVf/cDVQABr3abFRR6mE7yesLMkgu4ujuyV/sgxafQ8wgve0DJQUJ38Z8tkgA2izA==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/ast-parents": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/ast-parents/-/ast-parents-0.0.1.tgz", + "integrity": "sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==", + "dev": true + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base-x": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "dev": true + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/bs58": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", + "dev": true, + "dependencies": { + "base-x": "^4.0.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-reverse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz", + "integrity": "sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg==", + "dev": true + }, + "node_modules/bufio": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.2.1.tgz", + "integrity": "sha512-9oR3zNdupcg/Ge2sSHQF3GX+kmvL/fTPvD0nd5AGLq8SjUYnTz+SlFjK/GXidndbZtIj+pVKXiWeR9w6e9wKCA==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/bundle-require": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-3.1.2.tgz", + "integrity": "sha512-Of6l6JBAxiyQ5axFxUM6dYeP/W7X2Sozeo/4EYB9sJhL+dqL7TKjg+shwxp6jlu/6ZSERfsYtIpSJ1/x3XkAEA==", + "dev": true, + "dependencies": { + "load-tsconfig": "^0.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "esbuild": ">=0.13" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "dev": true, + "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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "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" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cid-tool": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cid-tool/-/cid-tool-3.0.0.tgz", + "integrity": "sha512-rgpV/LzuxUsGCJvUHe9+OuOAENVCiTn+mgGT8Nee1qDLS3xFGBUvZQdsY9MEpUi0YOFy6oz1pybHErcvE4SlGw==", + "dev": true, + "dependencies": { + "cids": "^1.0.0", + "explain-error": "^1.0.4", + "multibase": "^4.0.2", + "multihashes": "^4.0.2", + "split2": "^3.1.1", + "uint8arrays": "^2.1.3", + "yargs": "^16.2.0" + }, + "bin": { + "cid": "src/cli/bin.js" + } + }, + "node_modules/cids": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/cids/-/cids-1.1.9.tgz", + "integrity": "sha512-l11hWRfugIcbGuTZwAM5PwpjPPjyb6UZOGwlHSnOBV5o07XhQ4gNpBN67FbODvpjyHtd+0Xs6KNvUcGBiDRsdg==", + "deprecated": "This module has been superseded by the multiformats module", + "dev": true, + "dependencies": { + "multibase": "^4.0.1", + "multicodec": "^3.0.1", + "multihashes": "^4.0.1", + "uint8arrays": "^3.0.0" + }, + "engines": { + "node": ">=4.0.0", + "npm": ">=3.0.0" + } + }, + "node_modules/cids/node_modules/uint8arrays": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.1.tgz", + "integrity": "sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==", + "dev": true, + "dependencies": { + "multiformats": "^9.4.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dev": true, + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz", + "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==", + "dev": true, + "dependencies": { + "array-back": "^4.0.2", + "chalk": "^2.4.2", + "table-layout": "^1.0.2", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/command-line-usage/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/command-line-usage/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/command-line-usage/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/command-line-usage/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/command-line-usage/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "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" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/erc721a": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/erc721a/-/erc721a-3.3.0.tgz", + "integrity": "sha512-LqwmMcDPS3H9y7ZO+9B7R9sEoWApra17d4PwodXuP1072jP653jdo0TYkJbK4G5pBUFDdB5TCZwmJ6EQbmrysQ==", + "dev": true, + "dependencies": { + "@openzeppelin/contracts": "^4.4.2" + } + }, + "node_modules/erc721a-upgradeable": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/erc721a-upgradeable/-/erc721a-upgradeable-3.3.0.tgz", + "integrity": "sha512-ILE0SjKuvhx+PABG0A/41QUp0MFiYmzrgo71htQ0Ov6JfDOmgUzGxDW8gZuYfKrdlYjNwSAqMpUFWBbyW3sWBA==", + "dev": true, + "dependencies": { + "@openzeppelin/contracts-upgradeable": "^4.4.2" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz", + "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/linux-loong64": "0.14.54", + "esbuild-android-64": "0.14.54", + "esbuild-android-arm64": "0.14.54", + "esbuild-darwin-64": "0.14.54", + "esbuild-darwin-arm64": "0.14.54", + "esbuild-freebsd-64": "0.14.54", + "esbuild-freebsd-arm64": "0.14.54", + "esbuild-linux-32": "0.14.54", + "esbuild-linux-64": "0.14.54", + "esbuild-linux-arm": "0.14.54", + "esbuild-linux-arm64": "0.14.54", + "esbuild-linux-mips64le": "0.14.54", + "esbuild-linux-ppc64le": "0.14.54", + "esbuild-linux-riscv64": "0.14.54", + "esbuild-linux-s390x": "0.14.54", + "esbuild-netbsd-64": "0.14.54", + "esbuild-openbsd-64": "0.14.54", + "esbuild-sunos-64": "0.14.54", + "esbuild-windows-32": "0.14.54", + "esbuild-windows-64": "0.14.54", + "esbuild-windows-arm64": "0.14.54" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz", + "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz", + "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz", + "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz", + "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz", + "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz", + "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz", + "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz", + "integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz", + "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz", + "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz", + "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz", + "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz", + "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz", + "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz", + "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz", + "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz", + "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz", + "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz", + "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz", + "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.54.0.tgz", + "integrity": "sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.54.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/explain-error": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/explain-error/-/explain-error-1.0.4.tgz", + "integrity": "sha512-/wSgNMxFusiYRy1rd19LT2SQlIXDppHpumpWo06wxjflD1OYxDLbl6rMVw+U3bxD5Nuhex4TKqv9Aem4D0lVzQ==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dev": true, + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dev": true, + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/keccak256": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/keccak256/-/keccak256-1.0.6.tgz", + "integrity": "sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw==", + "dev": true, + "dependencies": { + "bn.js": "^5.2.0", + "buffer": "^6.0.3", + "keccak": "^3.0.2" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/load-tsconfig": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "dev": true + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/multibase": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-4.0.6.tgz", + "integrity": "sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ==", + "deprecated": "This module has been superseded by the multiformats module", + "dev": true, + "dependencies": { + "@multiformats/base-x": "^4.0.1" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/multicodec": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-3.2.1.tgz", + "integrity": "sha512-+expTPftro8VAW8kfvcuNNNBgb9gPeNYV9dn+z1kJRWF2vih+/S79f2RVeIwmrJBUJ6NT9IUPWnZDQvegEh5pw==", + "deprecated": "This module has been superseded by the multiformats module", + "dev": true, + "dependencies": { + "uint8arrays": "^3.0.0", + "varint": "^6.0.0" + } + }, + "node_modules/multicodec/node_modules/uint8arrays": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.1.tgz", + "integrity": "sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==", + "dev": true, + "dependencies": { + "multiformats": "^9.4.2" + } + }, + "node_modules/multiformats": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", + "dev": true + }, + "node_modules/multihashes": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-4.0.3.tgz", + "integrity": "sha512-0AhMH7Iu95XjDLxIeuCOOE4t9+vQZsACyKZ9Fxw2pcsRmlX4iCn1mby0hS0bb+nQOVpdQYWPpnyusw4da5RPhA==", + "dev": true, + "dependencies": { + "multibase": "^4.0.1", + "uint8arrays": "^3.0.0", + "varint": "^5.0.2" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/multihashes/node_modules/uint8arrays": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.1.tgz", + "integrity": "sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==", + "dev": true, + "dependencies": { + "multiformats": "^9.4.2" + } + }, + "node_modules/multihashes/node_modules/varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true + }, + "node_modules/node-gyp-build": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.0.tgz", + "integrity": "sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg==", + "dev": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dev": true, + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/prettier-plugin-solidity": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.2.0.tgz", + "integrity": "sha512-fgxcUZpVAP+LlRfy5JI5oaAkXGkmsje2VJ5krv/YMm+rcTZbIUwFguSw5f+WFuttMjpDm6wB4UL7WVkArEfiVA==", + "dev": true, + "dependencies": { + "@solidity-parser/parser": "^0.16.2", + "semver": "^7.5.4", + "solidity-comments-extractor": "^0.0.7" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "prettier": ">=2.3.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reduce-flatten": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", + "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "dev": true + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/solhint": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-3.6.2.tgz", + "integrity": "sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ==", + "dev": true, + "dependencies": { + "@solidity-parser/parser": "^0.16.0", + "ajv": "^6.12.6", + "antlr4": "^4.11.0", + "ast-parents": "^0.0.1", + "chalk": "^4.1.2", + "commander": "^10.0.0", + "cosmiconfig": "^8.0.0", + "fast-diff": "^1.2.0", + "glob": "^8.0.3", + "ignore": "^5.2.4", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "pluralize": "^8.0.0", + "semver": "^7.5.2", + "strip-ansi": "^6.0.1", + "table": "^6.8.1", + "text-table": "^0.2.0" + }, + "bin": { + "solhint": "solhint.js" + }, + "optionalDependencies": { + "prettier": "^2.8.3" + } + }, + "node_modules/solhint-plugin-prettier": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz", + "integrity": "sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "peerDependencies": { + "prettier": "^1.15.0 || ^2.0.0", + "prettier-plugin-solidity": "^1.0.0-alpha.14" + } + }, + "node_modules/solhint/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/solhint/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/solhint/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/solidity-comments-extractor": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", + "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dev": true, + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", + "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", + "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "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" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table-layout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", + "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", + "dev": true, + "dependencies": { + "array-back": "^4.0.1", + "deep-extend": "~0.6.0", + "typical": "^5.2.0", + "wordwrapjs": "^4.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table-layout/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-command-line-args": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz", + "integrity": "sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "command-line-args": "^5.1.1", + "command-line-usage": "^6.1.0", + "string-format": "^2.0.0" + }, + "bin": { + "write-markdown": "dist/write-markdown.js" + } + }, + "node_modules/ts-essentials": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", + "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", + "dev": true, + "peerDependencies": { + "typescript": ">=3.7.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/tsup": { + "version": "5.12.9", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-5.12.9.tgz", + "integrity": "sha512-dUpuouWZYe40lLufo64qEhDpIDsWhRbr2expv5dHEMjwqeKJS2aXA/FPqs1dxO4T6mBojo7rvo3jP9NNzaKyDg==", + "dev": true, + "dependencies": { + "bundle-require": "^3.0.2", + "cac": "^6.7.12", + "chokidar": "^3.5.1", + "debug": "^4.3.1", + "esbuild": "^0.14.25", + "execa": "^5.0.0", + "globby": "^11.0.3", + "joycon": "^3.0.1", + "postcss-load-config": "^3.0.1", + "resolve-from": "^5.0.0", + "rollup": "^2.74.1", + "source-map": "0.8.0-beta.0", + "sucrase": "^3.20.3", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "peerDependencies": { + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": "^4.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tsup/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typechain": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.3.2.tgz", + "integrity": "sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==", + "dev": true, + "dependencies": { + "@types/prettier": "^2.1.1", + "debug": "^4.3.1", + "fs-extra": "^7.0.0", + "glob": "7.1.7", + "js-sha3": "^0.8.0", + "lodash": "^4.17.15", + "mkdirp": "^1.0.4", + "prettier": "^2.3.1", + "ts-command-line-args": "^2.2.0", + "ts-essentials": "^7.0.1" + }, + "bin": { + "typechain": "dist/cli/cli.js" + }, + "peerDependencies": { + "typescript": ">=4.3.0" + } + }, + "node_modules/typechain/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/typechain/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typechain/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/typechain/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uint8arrays": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-2.1.10.tgz", + "integrity": "sha512-Q9/hhJa2836nQfEJSZTmr+pg9+cDJS9XEAp7N2Vg5MzL3bK/mkMVfjscRGYruP9jNda6MAdf4QD/y78gSzkp6A==", + "dev": true, + "dependencies": { + "multiformats": "^9.4.2" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/varint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", + "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", + "dev": true + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrapjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", + "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", + "dev": true, + "dependencies": { + "reduce-flatten": "^2.0.0", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/wordwrapjs/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/workerpool": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json index f064f0f4a..8d19c33dd 100644 --- a/package.json +++ b/package.json @@ -8,58 +8,57 @@ "/contracts/**/*.sol" ], "devDependencies": { - "@chainlink/contracts": "^0.6.1", - "@openzeppelin/contracts": "4.7.3", - "@openzeppelin/contracts-upgradeable": "4.7.3", - "@thirdweb-dev/dynamic-contracts": "^1.1.2", - "@thirdweb-dev/sdk": "^4", + "@chainlink/contracts": "^0.8.0", + "@openzeppelin/contracts": "^4.9.3", + "@openzeppelin/contracts-upgradeable": "^4.9.3", "@thirdweb-dev/chains": "^0.1.54", - "@typechain/ethers-v5": "^10.0.0", + "@thirdweb-dev/dynamic-contracts": "^1.2.4", + "@thirdweb-dev/sdk": "^4.0.16", + "@typechain/ethers-v5": "^10.2.1", "@types/fs-extra": "^9.0.13", - "@types/mocha": "^9.1.0", - "@types/node": "^17.0.21", - "@typescript-eslint/eslint-plugin": "^5.13.0", - "@typescript-eslint/parser": "^5.13.0", - "dotenv": "^16.0.0", + "@types/mocha": "^9.1.1", + "@types/node": "^17.0.45", + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.62.0", + "dotenv": "^16.3.1", "erc721a": "3.3.0", "erc721a-upgradeable": "^3.3.0", - "eslint": "^8.10.0", - "eslint-config-prettier": "^8.5.0", - "ethers": "^5.0.0", - "fs-extra": "^10.0.1", + "eslint": "^8.54.0", + "eslint-config-prettier": "^8.10.0", + "ethers": "^5.7.2", + "fs-extra": "^10.1.0", "keccak256": "^1.0.6", - "merkletreejs": "^0.2.31", - "mocha": "^9.2.1", - "prettier": "^2.5.1", - "prettier-plugin-solidity": "^1.0.0-beta.19", - "solhint": "^3.3.7", + "mocha": "^9.2.2", + "prettier": "^2.8.8", + "prettier-plugin-solidity": "^1.2.0", + "solhint": "^3.6.2", "solhint-plugin-prettier": "^0.0.5", - "ts-node": "^10.6.0", - "tslib": "^2.3.1", - "tsup": "^5.11.11", - "typechain": "^8.0.0", - "typescript": "^4.4.4" + "ts-node": "^10.9.1", + "tslib": "^2.6.2", + "tsup": "^5.12.9", + "typechain": "^8.3.2", + "typescript": "^4.9.5" }, "peerDependencies": { "ethers": "^5.0.0" }, "resolutions": { - "typescript": "^4.4.4" + "typescript": "^5.3.2" }, "scripts": { "clean": "forge clean && rm -rf abi/ && rm -rf artifacts_forge/ && rm -rf contract_artifacts && rm -rf dist/ && rm -rf typechain/", "compile": "forge build && npx ts-node scripts/package-release.ts", "lint": "solhint \"contracts/**/*.sol\"", - "prettier": "prettier --config .prettierrc --write \"{contracts,src}/**/*.{js,json,sol,ts}\"", - "prettier:list-different": "prettier --config .prettierrc --list-different \"**/*.{js,json,sol,ts}\"", - "prettier:contracts": "prettier --config .prettierrc --list-different \"{contracts,src}/**/*.sol\"", + "prettier": "prettier --config .prettierrc --write --plugin=prettier-plugin-solidity '{contracts,src}/**/*.sol'", + "prettier:list-different": "prettier --config .prettierrc --plugin=prettier-plugin-solidity --list-different '**/*.sol'", + "prettier:contracts": "prettier --config .prettierrc --plugin=prettier-plugin-solidity --list-different '{contracts,src}/**/*.sol'", "test": "forge test", "typechain": "typechain --target ethers-v5 --out-dir ./typechain artifacts_forge/**/*.json", "build": "yarn clean && yarn compile", "forge:build": "forge build", "forge:test": "forge test", - "gas": "forge test --mc Benchmark --gas-report > gasreport.txt", - "forge:snapshot": "forge snapshot --check" - }, - "dependencies": {} + "gas": "forge snapshot --mc Benchmark --gas-report --diff .gas-snapshot > gasreport.txt", + "forge:snapshot": "forge snapshot --check", + "aabenchmark": "forge test --mc AABenchmarkPrepare && forge test --mc ProfileThirdwebAccount -vvv" + } } diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d298aa104..000000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -manticore==0.3.7 diff --git a/scripts/deploy-prebuilt-deterministic/deploy-deterministic-std-chains.ts b/scripts/deploy-prebuilt-deterministic/deploy-deterministic-std-chains.ts index d1bbafd42..2e961fa87 100644 --- a/scripts/deploy-prebuilt-deterministic/deploy-deterministic-std-chains.ts +++ b/scripts/deploy-prebuilt-deterministic/deploy-deterministic-std-chains.ts @@ -18,7 +18,7 @@ import { DEFAULT_CHAINS, apiMap, chainIdApiKey } from "./constants"; ////// To run this script: `npx ts-node scripts/deploy-prebuilt-deterministic/deploy-deterministic-std-chains.ts` ////// ///// MAKE SURE TO PUT IN THE RIGHT CONTRACT NAME HERE AFTER PUBLISHING IT ///// //// THE CONTRACT SHOULD BE PUBLISHED WITH THE NEW PUBLISH FLOW //// -const publishedContractName = "AccountExtension"; +const publishedContractName = "MarketplaceV3"; const publisherAddress: string = "deployer.thirdweb.eth"; const deployerKey: string = process.env.PRIVATE_KEY as string; const secretKey: string = process.env.THIRDWEB_SECRET_KEY as string; @@ -47,7 +47,14 @@ async function main() { // const chainId = (await sdk.getProvider().getNetwork()).chainId; try { - const implAddr = await getThirdwebContractAddress(publishedContractName, chain.chainId, sdk.storage); + const implAddr = await getThirdwebContractAddress( + publishedContractName, + chain.chainId, + sdk.storage, + "latest", + sdk.options.clientId, + sdk.options.secretKey, + ); if (implAddr) { console.log(`implementation ${implAddr} already deployed on chainId: ${chain.slug}`); console.log(); @@ -76,6 +83,8 @@ async function main() { sdk.getProvider(), sdk.storage, create2FactoryAddress, + sdk.options.clientId, + sdk.options.secretKey, ); if (await isContractDeployed(cloneFactoryAddress, sdk.getProvider())) { console.log(`-- TWCloneFactory already present at ${cloneFactoryAddress}`); @@ -87,6 +96,8 @@ async function main() { sdk.storage, sdk.getProvider(), create2FactoryAddress, + sdk.options.clientId, + sdk.options.secretKey, ); const implementationAddress = deploymentInfo.find(i => i.type === "implementation")?.transaction diff --git a/src/test/EvolvingNFT.t.sol b/src/test/EvolvingNFT.t.sol index 6adf5f2c4..8a695b420 100644 --- a/src/test/EvolvingNFT.t.sol +++ b/src/test/EvolvingNFT.t.sol @@ -12,20 +12,20 @@ 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/NFTMetadataRendererLib.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 "contracts/lib/TWStrings.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"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract EvolvingNFTTest is BaseTest { - using StringsUpgradeable for uint256; - using StringsUpgradeable for address; + using Strings for uint256; + using Strings for address; event SharedMetadataUpdated( bytes32 indexed id, @@ -288,7 +288,7 @@ contract EvolvingNFTTest is BaseTest { alp.pricePerToken = 0; alp.currency = address(erc20); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -382,9 +382,9 @@ contract EvolvingNFTTest is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(caller), 20), + Strings.toHexString(uint160(caller), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -402,9 +402,9 @@ contract EvolvingNFTTest is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(target), 20), + Strings.toHexString(uint160(target), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -565,7 +565,7 @@ contract EvolvingNFTTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -726,7 +726,7 @@ contract EvolvingNFTTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -779,7 +779,7 @@ contract EvolvingNFTTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -841,7 +841,7 @@ contract EvolvingNFTTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -899,7 +899,7 @@ contract EvolvingNFTTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -958,7 +958,7 @@ contract EvolvingNFTTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // bytes32[] memory proofs = new bytes32[](0); @@ -1176,7 +1176,7 @@ contract EvolvingNFTTest is BaseTest { alp.pricePerToken = 0; alp.currency = address(erc20); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist IDrop.ClaimCondition[] memory conditions = new IDrop.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; diff --git a/src/test/Forwarder.t.sol b/src/test/Forwarder.t.sol index 38fd7a1d2..8ff96ca51 100644 --- a/src/test/Forwarder.t.sol +++ b/src/test/Forwarder.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; -import "contracts/infra/forwarder/Forwarder.sol"; -import "contracts/infra/forwarder/ForwarderConsumer.sol"; +import { Forwarder } from "contracts/infra/forwarder/Forwarder.sol"; +import { ForwarderConsumer } from "contracts/infra/forwarder/ForwarderConsumer.sol"; import "./utils/BaseTest.sol"; @@ -47,11 +47,10 @@ contract ForwarderTest is BaseTest { Regular `Forwarder`: chainId in typehash //////////////////////////////////////////////////////////////*/ - function signForwarderRequest(Forwarder.ForwardRequest memory forwardRequest, uint256 privateKey) - internal - view - returns (bytes memory) - { + function signForwarderRequest( + Forwarder.ForwardRequest memory forwardRequest, + uint256 privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashForwardRequest, forwardRequest.from, diff --git a/src/test/ForwarderChainlessDomain.t.sol b/src/test/ForwarderChainlessDomain.t.sol index 4863316d1..e293b7419 100644 --- a/src/test/ForwarderChainlessDomain.t.sol +++ b/src/test/ForwarderChainlessDomain.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; -import "contracts/infra/forwarder/ForwarderConsumer.sol"; -import "contracts/infra/forwarder/ForwarderChainlessDomain.sol"; +import { ForwarderConsumer } from "contracts/infra/forwarder/ForwarderConsumer.sol"; +import { ForwarderChainlessDomain } from "contracts/infra/forwarder/ForwarderChainlessDomain.sol"; import "./utils/BaseTest.sol"; @@ -48,11 +48,10 @@ contract ForwarderChainlessDomainTest is BaseTest { Updated `Forwarder`: chainId in ForwardRequest, not typehash. //////////////////////////////////////////////////////////////*/ - function signForwarderRequest(ForwarderChainlessDomain.ForwardRequest memory forwardRequest, uint256 privateKey) - internal - view - returns (bytes memory) - { + function signForwarderRequest( + ForwarderChainlessDomain.ForwardRequest memory forwardRequest, + uint256 privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashForwardRequest, forwardRequest.from, diff --git a/src/test/LoyaltyCard.t.sol b/src/test/LoyaltyCard.t.sol index 89d8c69c3..9f0d1f022 100644 --- a/src/test/LoyaltyCard.t.sol +++ b/src/test/LoyaltyCard.t.sol @@ -6,11 +6,12 @@ import "@ds-test/test.sol"; import "./utils/BaseTest.sol"; import "contracts/infra/TWProxy.sol"; +import { Strings } from "contracts/lib/Strings.sol"; import { LoyaltyCard } from "contracts/prebuilts/loyalty/LoyaltyCard.sol"; contract LoyaltyCardTest is BaseTest { LoyaltyCard internal loyaltyCard; - using TWStrings for uint256; + using Strings for uint256; bytes32 internal typehashMintRequest; bytes32 internal nameHash; @@ -83,11 +84,10 @@ contract LoyaltyCardTest is BaseTest { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(LoyaltyCard.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + LoyaltyCard.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashMintRequest, _request.to, diff --git a/src/test/LoyaltyPoints.t.sol b/src/test/LoyaltyPoints.t.sol index 7a60be763..c557091bf 100644 --- a/src/test/LoyaltyPoints.t.sol +++ b/src/test/LoyaltyPoints.t.sol @@ -6,11 +6,12 @@ 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 TWStrings for uint256; + using Strings for uint256; bytes32 internal typehashMintRequest; bytes32 internal nameHash; @@ -78,11 +79,10 @@ contract LoyaltyPointsTest is BaseTest { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(LoyaltyPoints.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + LoyaltyPoints.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashMintRequest, _request.to, diff --git a/src/test/Multiwrap.t.sol b/src/test/Multiwrap.t.sol index 4b862f3b0..a8b34e923 100644 --- a/src/test/Multiwrap.t.sol +++ b/src/test/Multiwrap.t.sol @@ -5,8 +5,8 @@ import { Multiwrap } from "contracts/prebuilts/multiwrap/Multiwrap.sol"; import { ITokenBundle } from "contracts/extension/interface/ITokenBundle.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; import { MockERC20 } from "./mocks/MockERC20.sol"; +import { Strings } from "contracts/lib/Strings.sol"; import { Wallet } from "./utils/Wallet.sol"; import "./utils/BaseTest.sol"; @@ -18,11 +18,7 @@ contract MultiwrapReentrant is MockERC20, ITokenBundle { multiwrap = Multiwrap(_multiwrap); } - function transferFrom( - address from, - address to, - uint256 amount - ) public override returns (bool) { + function transferFrom(address from, address to, uint256 amount) public override returns (bool) { multiwrap.unwrap(0, address(this)); return super.transferFrom(from, to, amount); } @@ -119,9 +115,9 @@ contract MultiwrapTest is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(caller), 20), + Strings.toHexString(uint160(caller), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -139,9 +135,9 @@ contract MultiwrapTest is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(target), 20), + Strings.toHexString(uint160(target), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -446,7 +442,7 @@ contract MultiwrapTest is BaseTest { address recipient = address(0x123); vm.prank(address(tokenOwner)); - vm.expectRevert("ERC721: caller is not token owner nor approved"); + vm.expectRevert("ERC721: caller is not token owner or approved"); multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); } @@ -485,7 +481,7 @@ contract MultiwrapTest is BaseTest { address recipient = address(0x123); vm.prank(address(tokenOwner)); - vm.expectRevert("ERC721: caller is not token owner nor approved"); + vm.expectRevert("ERC721: caller is not token owner or approved"); multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); } @@ -498,7 +494,7 @@ contract MultiwrapTest is BaseTest { address recipient = address(0x123); vm.prank(address(tokenOwner)); - vm.expectRevert("ERC1155: caller is not token owner nor approved"); + vm.expectRevert("ERC1155: caller is not token owner or approved"); multiwrap.wrap(wrappedContent, uriForWrappedToken, recipient); } diff --git a/src/test/OpenEditionERC721.t.sol b/src/test/OpenEditionERC721.t.sol index 31a73b7c7..fb9310f30 100644 --- a/src/test/OpenEditionERC721.t.sol +++ b/src/test/OpenEditionERC721.t.sol @@ -2,19 +2,17 @@ pragma solidity ^0.8.0; import { ERC721AUpgradeable, OpenEditionERC721, ISharedMetadata } from "contracts/prebuilts/open-edition/OpenEditionERC721.sol"; -import { NFTMetadataRenderer } from "contracts/lib/NFTMetadataRendererLib.sol"; +import { NFTMetadataRenderer } from "contracts/lib/NFTMetadataRenderer.sol"; import { TWProxy } from "contracts/infra/TWProxy.sol"; // Test imports import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "contracts/lib/TWStrings.sol"; import "./utils/BaseTest.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract OpenEditionERC721Test is BaseTest { - using StringsUpgradeable for uint256; - using StringsUpgradeable for address; + using Strings for uint256; + using Strings for address; event SharedMetadataUpdated(string name, string description, string imageURI, string animationURI); @@ -77,9 +75,9 @@ contract OpenEditionERC721Test is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(caller), 20), + Strings.toHexString(uint160(caller), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -97,9 +95,9 @@ contract OpenEditionERC721Test is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(target), 20), + Strings.toHexString(uint160(target), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -258,7 +256,7 @@ contract OpenEditionERC721Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -420,7 +418,7 @@ contract OpenEditionERC721Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -470,7 +468,7 @@ contract OpenEditionERC721Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -529,7 +527,7 @@ contract OpenEditionERC721Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -584,7 +582,7 @@ contract OpenEditionERC721Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist OpenEditionERC721.ClaimCondition[] memory conditions = new OpenEditionERC721.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -640,7 +638,7 @@ contract OpenEditionERC721Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // bytes32[] memory proofs = new bytes32[](0); diff --git a/src/test/SignatureDrop.t.sol b/src/test/SignatureDrop.t.sol index c43df345b..ea47d9d64 100644 --- a/src/test/SignatureDrop.t.sol +++ b/src/test/SignatureDrop.t.sol @@ -5,14 +5,12 @@ import { SignatureDrop, IDropSinglePhase, IDelayedReveal, ISignatureMintERC721, // Test imports import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "contracts/lib/TWStrings.sol"; import "./utils/BaseTest.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract SignatureDropTest is BaseTest { - using StringsUpgradeable for uint256; - using StringsUpgradeable for address; + 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); @@ -69,9 +67,9 @@ contract SignatureDropTest is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(caller), 20), + Strings.toHexString(uint160(caller), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -89,9 +87,9 @@ contract SignatureDropTest is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(target), 20), + Strings.toHexString(uint160(target), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -535,9 +533,9 @@ contract SignatureDropTest is BaseTest { bytes memory errorMessage = abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(address(this)), 20), + Strings.toHexString(uint160(address(this)), 20), " is missing role ", - TWStrings.toHexString(uint256(keccak256("MINTER_ROLE")), 32) + Strings.toHexString(uint256(keccak256("MINTER_ROLE")), 32) ); vm.expectRevert(errorMessage); @@ -622,11 +620,10 @@ contract SignatureDropTest is BaseTest { Signature Mint Tests //////////////////////////////////////////////////////////////*/ - function signMintRequest(SignatureDrop.MintRequest memory mintrequest, uint256 privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + SignatureDrop.MintRequest memory mintrequest, + uint256 privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashMintRequest, mintrequest.to, @@ -1116,7 +1113,7 @@ contract SignatureDropTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // bytes32[] memory proofs = new bytes32[](0); @@ -1354,12 +1351,7 @@ contract MaliciousReceiver { alp = _alp; } - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external returns (bytes4) { + function onERC721Received(address, address, uint256, bytes calldata) external returns (bytes4) { if (claim && loop) { loop = false; claim = false; diff --git a/src/test/TWFactory.t.sol b/src/test/TWFactory.t.sol index 37e5307c1..474286352 100644 --- a/src/test/TWFactory.t.sol +++ b/src/test/TWFactory.t.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.11; // Test imports import "./utils/BaseTest.sol"; -import "contracts/infra/TWFactory.sol"; -import "contracts/infra/TWRegistry.sol"; +import { TWFactory } from "contracts/infra/TWFactory.sol"; +import { TWRegistry } from "contracts/infra/TWRegistry.sol"; // Helpers import "@openzeppelin/contracts/utils/Create2.sol"; diff --git a/src/test/TWFee.t.sol b/src/test/TWFee.t.sol deleted file mode 100644 index c5c042a3a..000000000 --- a/src/test/TWFee.t.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.11; - -// Test imports -import "./mocks/MockThirdwebContract.sol"; -import "./utils/BaseTest.sol"; -import "contracts/infra/TWFee.sol"; - -// Helpers -import "@openzeppelin/contracts/utils/Create2.sol"; -import "contracts/infra/TWRegistry.sol"; -import "contracts/infra/TWFactory.sol"; -import "contracts/infra/TWProxy.sol"; - -contract TWFeeTest is BaseTest { - // Target contract - TWFee internal twFee; - - // Helper contracts - TWRegistry internal twRegistry; - TWFactory internal twFactory; - MockThirdwebContract internal mockModule; - - // Actors - address internal mockModuleDeployer; - address internal moduleAdmin = address(0x1); - address internal feeAdmin = address(0x2); - address internal notFeeAdmin = address(0x3); - address internal payer = address(0x4); - - // Test params - address internal trustedForwarder = address(0x4); - address internal thirdwebTreasury = address(0x5); - - // ===== Set up ===== - - function setUp() public override {} -} diff --git a/src/test/TWMultichainRegistry.t.sol b/src/test/TWMultichainRegistry.t.sol index 8e27e15da..b51cabec7 100644 --- a/src/test/TWMultichainRegistry.t.sol +++ b/src/test/TWMultichainRegistry.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.11; // Test imports import "./utils/BaseTest.sol"; import "contracts/infra/interface/ITWMultichainRegistry.sol"; -import "contracts/infra/TWMultichainRegistry.sol"; +import { TWMultichainRegistry } from "contracts/infra/TWMultichainRegistry.sol"; import "./mocks/MockThirdwebContract.sol"; import "contracts/extension/interface/plugin/IPluginMap.sol"; diff --git a/src/test/TWRegistry.t.sol b/src/test/TWRegistry.t.sol index 5a691ea8b..8d9a8c79f 100644 --- a/src/test/TWRegistry.t.sol +++ b/src/test/TWRegistry.t.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.11; // Test imports import "./utils/BaseTest.sol"; -import "contracts/infra/TWRegistry.sol"; +import { TWRegistry } from "contracts/infra/TWRegistry.sol"; interface ITWRegistryData { event Added(address indexed deployer, address indexed moduleAddress); diff --git a/src/test/TieredDrop.t.sol b/src/test/TieredDrop.t.sol index e595cf6e3..9c04f8422 100644 --- a/src/test/TieredDrop.t.sol +++ b/src/test/TieredDrop.t.sol @@ -2,13 +2,12 @@ pragma solidity ^0.8.0; import "./utils/BaseTest.sol"; -import "contracts/lib/TWStrings.sol"; import { TieredDrop } from "contracts/prebuilts/tiered-drop/TieredDrop.sol"; import { TWProxy } from "contracts/infra/TWProxy.sol"; contract TieredDropTest is BaseTest { - using TWStrings for uint256; + using Strings for uint256; TieredDrop public tieredDrop; @@ -940,7 +939,7 @@ contract TieredDropTest is BaseTest { } contract TieredDropBechmarkTest is BaseTest { - using TWStrings for uint256; + using Strings for uint256; TieredDrop public tieredDrop; diff --git a/src/test/airdrop/AirdropERC1155.t.sol b/src/test/airdrop/AirdropERC1155.t.sol index 33b9b0b22..161e9d364 100644 --- a/src/test/airdrop/AirdropERC1155.t.sol +++ b/src/test/airdrop/AirdropERC1155.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; -import "contracts/prebuilts/unaudited/airdrop/AirdropERC1155.sol"; +import { AirdropERC1155, IAirdropERC1155 } from "contracts/prebuilts/unaudited/airdrop/AirdropERC1155.sol"; // Test imports import { Wallet } from "../utils/Wallet.sol"; @@ -129,13 +129,7 @@ contract AirdropERC1155GasTest is BaseTest { console.log(gasleft()); } - function onERC1155Received( - address, - address, - uint256, - uint256, - bytes calldata - ) external pure returns (bytes4) { + 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 index 4d703e74d..4ce582d16 100644 --- a/src/test/airdrop/AirdropERC1155Claimable.t.sol +++ b/src/test/airdrop/AirdropERC1155Claimable.t.sol @@ -1,202 +1,218 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; -// import "contracts/prebuilts/unaudited/airdrop/AirdropERC1155Claimable.sol"; +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); + } -// // Test imports -// import { Wallet } from "../utils/Wallet.sol"; -// import "../utils/BaseTest.sol"; + 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); -// contract AirdropERC1155ClaimableTest is BaseTest { -// AirdropERC1155Claimable internal drop; + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); + uint256 quantity = 5; + uint256 id = 0; -// function setUp() public override { -// super.setUp(); - -// drop = AirdropERC1155Claimable(getContract("AirdropERC1155Claimable")); - -// 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[](5); -// inputs[0] = "node"; -// inputs[1] = "src/test/scripts/getProof.ts"; -// inputs[2] = Strings.toString(5); -// inputs[3] = "0"; -// inputs[4] = "0x0000000000000000000000000000000000000000"; - -// bytes memory result = vm.ffi(inputs); -// bytes32[] memory proofs = abi.decode(result, (bytes32[])); - -// vm.warp(1); - -// address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); -// 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[](5); -// inputs[0] = "node"; -// inputs[1] = "src/test/scripts/getProof.ts"; -// inputs[2] = Strings.toString(4); // generate proof with incorrect amount -// inputs[3] = "0"; -// inputs[4] = "0x0000000000000000000000000000000000000000"; - -// bytes memory result = vm.ffi(inputs); -// bytes32[] memory proofs = abi.decode(result, (bytes32[])); - -// vm.warp(1); - -// address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); -// 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[](5); -// inputs[0] = "node"; -// inputs[1] = "src/test/scripts/getProof.ts"; -// inputs[2] = Strings.toString(5); -// inputs[3] = "0"; -// inputs[4] = "0x0000000000000000000000000000000000000000"; - -// bytes memory result = vm.ffi(inputs); -// bytes32[] memory proofs = abi.decode(result, (bytes32[])); - -// vm.warp(1); - -// address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); -// 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[](5); -// inputs[0] = "node"; -// inputs[1] = "src/test/scripts/getProof.ts"; -// inputs[2] = Strings.toString(5); -// inputs[3] = "0"; -// inputs[4] = "0x0000000000000000000000000000000000000000"; - -// bytes memory result = vm.ffi(inputs); -// bytes32[] memory proofs = abi.decode(result, (bytes32[])); - -// vm.warp(1); - -// address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); -// 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[](5); -// inputs[0] = "node"; -// inputs[1] = "src/test/scripts/getProof.ts"; -// inputs[2] = Strings.toString(5); -// inputs[3] = "0"; -// inputs[4] = "0x0000000000000000000000000000000000000000"; - -// bytes memory result = vm.ffi(inputs); -// bytes32[] memory proofs = abi.decode(result, (bytes32[])); - -// vm.warp(1001); - -// address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); -// 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); -// } -// } + 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/AirdropERC20Claimable.t.sol b/src/test/airdrop/AirdropERC20Claimable.t.sol index dde88bea2..efc5029a8 100644 --- a/src/test/airdrop/AirdropERC20Claimable.t.sol +++ b/src/test/airdrop/AirdropERC20Claimable.t.sol @@ -5,15 +5,39 @@ 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 CheckTest is BaseTest { +contract AirdropERC20ClaimableTest is BaseTest { + address public implementation; AirdropERC20Claimable internal drop; function setUp() public override { super.setUp(); - drop = AirdropERC20Claimable(getContract("AirdropERC20Claimable")); + 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); @@ -23,162 +47,153 @@ contract CheckTest is BaseTest { 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/getProof.ts"; - // inputs[2] = Strings.toString(5); + 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[])); + bytes memory result = vm.ffi(inputs); + bytes32[] memory proofs = abi.decode(result, (bytes32[])); - // vm.warp(1); + vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); - // uint256 quantity = 2; + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); + uint256 quantity = 2; - // uint256 _availableAmount = drop.availableAmount(); + uint256 _availableAmount = drop.availableAmount(); - // vm.prank(receiver); - // drop.claim(receiver, quantity, proofs, 5); + 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); - // } + 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/getProof.ts"; - // inputs[2] = Strings.toString(4); // generate proof with incorrect amount + 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[])); + bytes memory result = vm.ffi(inputs); + bytes32[] memory proofs = abi.decode(result, (bytes32[])); - // vm.warp(1); + vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); - // uint256 quantity = 2; + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); + uint256 quantity = 2; - // vm.prank(receiver); - // vm.expectRevert("not in whitelist."); - // drop.claim(receiver, quantity, proofs, 4); - // } + vm.prank(receiver); + vm.expectRevert("invalid quantity."); + drop.claim(receiver, quantity, proofs, 4); + } - // function test_state_claim_allowlistedClaimer_proofClaimed() public { - // string[] memory inputs = new string[](3); - // inputs[0] = "node"; - // inputs[1] = "src/test/scripts/getProof.ts"; - // inputs[2] = Strings.toString(5); + 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[])); + bytes memory result = vm.ffi(inputs); + bytes32[] memory proofs = abi.decode(result, (bytes32[])); - // vm.warp(1); + vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); - // uint256 quantity = 2; + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); + uint256 quantity = 2; - // vm.prank(receiver); - // drop.claim(receiver, quantity, proofs, 5); + vm.prank(receiver); + drop.claim(receiver, quantity, proofs, 5); - // quantity = 3; + quantity = 3; - // vm.prank(receiver); - // drop.claim(receiver, quantity, proofs, 5); + vm.prank(receiver); + drop.claim(receiver, quantity, proofs, 5); - // vm.prank(receiver); - // vm.expectRevert("proof claimed."); - // 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/getProof.ts"; - // inputs[2] = Strings.toString(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[])); + bytes memory result = vm.ffi(inputs); + bytes32[] memory proofs = abi.decode(result, (bytes32[])); - // vm.warp(1); + vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); - // uint256 quantity = 6; + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); + uint256 quantity = 6; - // vm.prank(receiver); - // vm.expectRevert("invalid quantity."); - // drop.claim(receiver, quantity, proofs, 5); - // } + 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/getProof.ts"; - // inputs[2] = Strings.toString(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[])); + bytes memory result = vm.ffi(inputs); + bytes32[] memory proofs = abi.decode(result, (bytes32[])); - // vm.warp(1001); + vm.warp(1001); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); - // uint256 quantity = 5; + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); + uint256 quantity = 5; - // vm.prank(receiver); - // vm.expectRevert("airdrop expired."); - // drop.claim(receiver, quantity, proofs, 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; + function test_state_claim_nonAllowlistedClaimer() public { + address receiver = address(0x123); + uint256 quantity = 1; + bytes32[] memory proofs; - // uint256 _availableAmount = drop.availableAmount(); + uint256 _availableAmount = drop.availableAmount(); - // vm.prank(receiver); - // drop.claim(receiver, quantity, proofs, 0); + 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); - // } + 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_exceedsAvailable() public { + function test_revert_claim_nonAllowlistedClaimer_invalidQuantity() public { + address receiver = address(0x123); + uint256 quantity = 2; + bytes32[] memory proofs; - // uint256 _availableAmount = drop.availableAmount(); - // bytes32[] memory proofs; + vm.prank(receiver); + vm.expectRevert("invalid quantity."); + drop.claim(receiver, quantity, proofs, 0); + } - // uint256 i = 0; - // for(; i < _availableAmount; i++) { - // address receiver = getActor(uint160(i)); - // vm.prank(receiver); - // drop.claim(receiver, 1, proofs, 0); - // } + function test_revert_claim_nonAllowlistedClaimer_exceedsAvailable() public { + uint256 _availableAmount = drop.availableAmount(); + bytes32[] memory proofs; - // address receiver = getActor(uint160(i)); - // vm.prank(receiver); - // vm.expectRevert("exceeds available tokens."); - // drop.claim(receiver, 1, proofs, 0); - // } + 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 index 3fd5be31b..6b579eb46 100644 --- a/src/test/airdrop/AirdropERC721.t.sol +++ b/src/test/airdrop/AirdropERC721.t.sol @@ -111,12 +111,7 @@ contract AirdropERC721GasTest is BaseTest { console.log(gasleft()); } - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external pure returns (bytes4) { + 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 index 0ef9e9546..917ac4043 100644 --- a/src/test/airdrop/AirdropERC721Claimable.t.sol +++ b/src/test/airdrop/AirdropERC721Claimable.t.sol @@ -1,205 +1,220 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; -// import "contracts/prebuilts/unaudited/airdrop/AirdropERC721Claimable.sol"; +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)); -// // Test imports -// import { Wallet } from "../utils/Wallet.sol"; -// import "../utils/BaseTest.sol"; + 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(); -// contract AirdropERC721ClaimableTest is BaseTest { -// AirdropERC721Claimable internal drop; + vm.prank(receiver); + drop.claim(receiver, quantity, proofs, 0); -// function setUp() public override { -// super.setUp(); + 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); + } -// drop = AirdropERC721Claimable(getContract("AirdropERC721Claimable")); - -// 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[](5); -// inputs[0] = "node"; -// inputs[1] = "src/test/scripts/getProof.ts"; -// inputs[2] = Strings.toString(5); -// inputs[3] = "0"; -// inputs[4] = "0x0000000000000000000000000000000000000000"; - -// bytes memory result = vm.ffi(inputs); -// bytes32[] memory proofs = abi.decode(result, (bytes32[])); - -// vm.warp(1); - -// address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); -// 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[](5); -// inputs[0] = "node"; -// inputs[1] = "src/test/scripts/getProof.ts"; -// inputs[2] = Strings.toString(4); // generate proof with incorrect amount -// inputs[3] = "0"; -// inputs[4] = "0x0000000000000000000000000000000000000000"; - -// bytes memory result = vm.ffi(inputs); -// bytes32[] memory proofs = abi.decode(result, (bytes32[])); - -// vm.warp(1); - -// address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); -// 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[](5); -// inputs[0] = "node"; -// inputs[1] = "src/test/scripts/getProof.ts"; -// inputs[2] = Strings.toString(5); -// inputs[3] = "0"; -// inputs[4] = "0x0000000000000000000000000000000000000000"; - -// bytes memory result = vm.ffi(inputs); -// bytes32[] memory proofs = abi.decode(result, (bytes32[])); - -// vm.warp(1); - -// address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); -// 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[](5); -// inputs[0] = "node"; -// inputs[1] = "src/test/scripts/getProof.ts"; -// inputs[2] = Strings.toString(5); -// inputs[3] = "0"; -// inputs[4] = "0x0000000000000000000000000000000000000000"; - -// bytes memory result = vm.ffi(inputs); -// bytes32[] memory proofs = abi.decode(result, (bytes32[])); - -// vm.warp(1); - -// address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); -// 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[](5); -// inputs[0] = "node"; -// inputs[1] = "src/test/scripts/getProof.ts"; -// inputs[2] = Strings.toString(5); -// inputs[3] = "0"; -// inputs[4] = "0x0000000000000000000000000000000000000000"; - -// bytes memory result = vm.ffi(inputs); -// bytes32[] memory proofs = abi.decode(result, (bytes32[])); - -// vm.warp(1001); - -// address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); -// 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); -// } -// } + 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 new file mode 100644 index 000000000..6a1db1171 --- /dev/null +++ b/src/test/benchmark/AccountBenchmark.t.sol @@ -0,0 +1,513 @@ +// 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 { UserOperation } from "contracts/prebuilts/account/utils/UserOperation.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 (UserOperation[] memory ops) { + uint256 nonce = entrypoint.getNonce(sender, 0); + + // Get user op fields + UserOperation memory op = UserOperation({ + sender: sender, + nonce: nonce, + initCode: _initCode, + callData: _callDataForEntrypoint, + callGasLimit: 500_000, + verificationGasLimit: 500_000, + preVerificationGas: 500_000, + maxFeePerGas: 0, + maxPriorityFeePerGas: 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 UserOperation[](1); + ops[0] = op; + } + + function _setupUserOpExecute( + uint256 _signerPKey, + bytes memory _initCode, + address _target, + uint256 _value, + bytes memory _callData + ) internal returns (UserOperation[] 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 (UserOperation[] 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(); + UserOperation[] 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); + + UserOperation[] 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(); + UserOperation[] 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(); + UserOperation[] 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); + + UserOperation[] 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); + + UserOperation[] 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(); + UserOperation[] 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(); + UserOperation[] 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/AirdropERC1155Benchmark.t.sol b/src/test/benchmark/AirdropERC1155Benchmark.t.sol index c409e2f26..389903565 100644 --- a/src/test/benchmark/AirdropERC1155Benchmark.t.sol +++ b/src/test/benchmark/AirdropERC1155Benchmark.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; -import "contracts/prebuilts/unaudited/airdrop/AirdropERC1155.sol"; +import { AirdropERC1155, IAirdropERC1155 } from "contracts/prebuilts/unaudited/airdrop/AirdropERC1155.sol"; // Test imports import { Wallet } from "../utils/Wallet.sol"; diff --git a/src/test/benchmark/DropERC1155Benchmark.t.sol b/src/test/benchmark/DropERC1155Benchmark.t.sol index ab29a6978..093be4089 100644 --- a/src/test/benchmark/DropERC1155Benchmark.t.sol +++ b/src/test/benchmark/DropERC1155Benchmark.t.sol @@ -4,14 +4,11 @@ pragma solidity ^0.8.0; import { DropERC1155, IPermissions, ILazyMint } from "contracts/prebuilts/drop/DropERC1155.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract DropERC1155BenchmarkTest is BaseTest { - using StringsUpgradeable for uint256; - using StringsUpgradeable for address; + 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); @@ -34,7 +31,7 @@ contract DropERC1155BenchmarkTest is BaseTest { DropERC1155 benchmark //////////////////////////////////////////////////////////////*/ - function test_bechmark_dropERC1155_claim() public { + function test_benchmark_dropERC1155_claim() public { vm.pauseGasMetering(); uint256 _tokenId = 0; string[] memory inputs = new string[](5); @@ -61,7 +58,7 @@ contract DropERC1155BenchmarkTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -84,7 +81,7 @@ contract DropERC1155BenchmarkTest is BaseTest { drop.claim(receiver, _tokenId, 100, address(erc20), 5, alp, ""); } - function test_bechmark_dropERC1155_setClaimConditions_five_conditions() public { + function test_benchmark_dropERC1155_setClaimConditions_five_conditions() public { vm.pauseGasMetering(); uint256 _tokenId = 0; string[] memory inputs = new string[](5); @@ -148,7 +145,7 @@ contract DropERC1155BenchmarkTest is BaseTest { drop.lazyMint(100, "ipfs://", emptyEncodedBytes); } - // function test_bechmark_dropERC1155_setClaimConditions_one_condition() public { + // function test_benchmark_dropERC1155_setClaimConditions_one_condition() public { // vm.pauseGasMetering(); // uint256 _tokenId = 0; // string[] memory inputs = new string[](5); @@ -175,7 +172,7 @@ contract DropERC1155BenchmarkTest is BaseTest { // vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist // DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); // conditions[0].maxClaimableSupply = 500; @@ -191,7 +188,7 @@ contract DropERC1155BenchmarkTest is BaseTest { // drop.setClaimConditions(_tokenId, conditions, false); // } - // function test_bechmark_dropERC1155_setClaimConditions_two_conditions() public { + // function test_benchmark_dropERC1155_setClaimConditions_two_conditions() public { // vm.pauseGasMetering(); // uint256 _tokenId = 0; // string[] memory inputs = new string[](5); @@ -218,7 +215,7 @@ contract DropERC1155BenchmarkTest is BaseTest { // vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist // DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](2); // conditions[0].maxClaimableSupply = 500; @@ -238,7 +235,7 @@ contract DropERC1155BenchmarkTest is BaseTest { // drop.setClaimConditions(_tokenId, conditions, false); // } - // function test_bechmark_dropERC1155_setClaimConditions_three_conditions() public { + // function test_benchmark_dropERC1155_setClaimConditions_three_conditions() public { // vm.pauseGasMetering(); // uint256 _tokenId = 0; // string[] memory inputs = new string[](5); @@ -265,7 +262,7 @@ contract DropERC1155BenchmarkTest is BaseTest { // vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist // DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](3); // conditions[0].maxClaimableSupply = 500; diff --git a/src/test/benchmark/DropERC20Benchmark.t.sol b/src/test/benchmark/DropERC20Benchmark.t.sol index d1be05da4..53f432604 100644 --- a/src/test/benchmark/DropERC20Benchmark.t.sol +++ b/src/test/benchmark/DropERC20Benchmark.t.sol @@ -4,14 +4,11 @@ pragma solidity ^0.8.0; import { DropERC20 } from "contracts/prebuilts/drop/DropERC20.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract DropERC20BenchmarkTest is BaseTest { - using StringsUpgradeable for uint256; - using StringsUpgradeable for address; + using Strings for uint256; + using Strings for address; DropERC20 public drop; @@ -29,14 +26,14 @@ contract DropERC20BenchmarkTest is BaseTest { DropERC20 benchmark //////////////////////////////////////////////////////////////*/ - function test_bechmark_dropERC20_setClaimConditions_five_conditions() public { + 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(300 ether); - inputs[3] = Strings.toString(1 ether); + 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); @@ -83,14 +80,14 @@ contract DropERC20BenchmarkTest is BaseTest { drop.setClaimConditions(conditions, false); } - function test_bechmark_dropERC20_claim() public { + 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(300 ether); - inputs[3] = Strings.toString(1 ether); + 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); @@ -109,7 +106,7 @@ contract DropERC20BenchmarkTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500 ether; @@ -130,7 +127,7 @@ contract DropERC20BenchmarkTest is BaseTest { drop.claim(receiver, 100 ether, address(erc20), 1 ether, alp, ""); } - // function test_bechmark_dropERC20_setClaimConditions_one_condition() public { + // function test_benchmark_dropERC20_setClaimConditions_one_condition() public { // vm.pauseGasMetering(); // string[] memory inputs = new string[](5); @@ -156,7 +153,7 @@ contract DropERC20BenchmarkTest is BaseTest { // vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist // DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); // conditions[0].maxClaimableSupply = 500 ether; @@ -170,7 +167,7 @@ contract DropERC20BenchmarkTest is BaseTest { // drop.setClaimConditions(conditions, false); // } - // function test_bechmark_dropERC20_setClaimConditions_two_conditions() public { + // function test_benchmark_dropERC20_setClaimConditions_two_conditions() public { // vm.pauseGasMetering(); // string[] memory inputs = new string[](5); @@ -196,7 +193,7 @@ contract DropERC20BenchmarkTest is BaseTest { // vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist // DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](2); // conditions[0].maxClaimableSupply = 500 ether; @@ -214,7 +211,7 @@ contract DropERC20BenchmarkTest is BaseTest { // drop.setClaimConditions(conditions, false); // } - // function test_bechmark_dropERC20_setClaimConditions_three_conditions() public { + // function test_benchmark_dropERC20_setClaimConditions_three_conditions() public { // vm.pauseGasMetering(); // string[] memory inputs = new string[](5); @@ -240,7 +237,7 @@ contract DropERC20BenchmarkTest is BaseTest { // vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist // DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](3); // conditions[0].maxClaimableSupply = 500 ether; diff --git a/src/test/benchmark/DropERC721Benchmark.t.sol b/src/test/benchmark/DropERC721Benchmark.t.sol index ecb8c4805..d11507c06 100644 --- a/src/test/benchmark/DropERC721Benchmark.t.sol +++ b/src/test/benchmark/DropERC721Benchmark.t.sol @@ -4,15 +4,12 @@ pragma solidity ^0.8.0; import { DropERC721, IDelayedReveal, ERC721AUpgradeable, IPermissions, ILazyMint } from "contracts/prebuilts/drop/DropERC721.sol"; // Test imports -import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "contracts/lib/TWStrings.sol"; +import { IERC721AUpgradeable } from "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract DropERC721BenchmarkTest is BaseTest { - using StringsUpgradeable for uint256; - using StringsUpgradeable for address; + using Strings for uint256; + using Strings for address; DropERC721 public drop; @@ -32,7 +29,7 @@ contract DropERC721BenchmarkTest is BaseTest { DropERC721 benchmark //////////////////////////////////////////////////////////////*/ - function test_bechmark_dropERC721_claim_five_tokens() public { + function test_benchmark_dropERC721_claim_five_tokens() public { vm.pauseGasMetering(); string[] memory inputs = new string[](5); @@ -58,7 +55,7 @@ contract DropERC721BenchmarkTest is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -83,7 +80,7 @@ contract DropERC721BenchmarkTest is BaseTest { drop.claim(receiver, 5, address(erc20), 5, alp, ""); } - function test_bechmark_dropERC721_setClaimConditions_five_conditions() public { + function test_benchmark_dropERC721_setClaimConditions_five_conditions() public { vm.pauseGasMetering(); string[] memory inputs = new string[](5); @@ -174,7 +171,7 @@ contract DropERC721BenchmarkTest is BaseTest { drop.reveal(0, key); } - // function test_bechmark_dropERC721_claim_one_token() public { + // function test_benchmark_dropERC721_claim_one_token() public { // vm.pauseGasMetering(); // string[] memory inputs = new string[](5); @@ -200,7 +197,7 @@ contract DropERC721BenchmarkTest is BaseTest { // vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist // DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); // conditions[0].maxClaimableSupply = 500; @@ -225,7 +222,7 @@ contract DropERC721BenchmarkTest is BaseTest { // drop.claim(receiver, 1, address(erc20), 5, alp, ""); // } - // function test_bechmark_dropERC721_claim_two_tokens() public { + // function test_benchmark_dropERC721_claim_two_tokens() public { // vm.pauseGasMetering(); // string[] memory inputs = new string[](5); @@ -251,7 +248,7 @@ contract DropERC721BenchmarkTest is BaseTest { // vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist // DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); // conditions[0].maxClaimableSupply = 500; @@ -276,7 +273,7 @@ contract DropERC721BenchmarkTest is BaseTest { // drop.claim(receiver, 2, address(erc20), 5, alp, ""); // } - // function test_bechmark_dropERC721_claim_three_tokens() public { + // function test_benchmark_dropERC721_claim_three_tokens() public { // vm.pauseGasMetering(); // string[] memory inputs = new string[](5); @@ -302,7 +299,7 @@ contract DropERC721BenchmarkTest is BaseTest { // vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist // DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); // conditions[0].maxClaimableSupply = 500; @@ -327,7 +324,7 @@ contract DropERC721BenchmarkTest is BaseTest { // drop.claim(receiver, 3, address(erc20), 5, alp, ""); // } - // function test_bechmark_dropERC721_setClaimConditions_one_condition() public { + // function test_benchmark_dropERC721_setClaimConditions_one_condition() public { // vm.pauseGasMetering(); // string[] memory inputs = new string[](5); @@ -353,7 +350,7 @@ contract DropERC721BenchmarkTest is BaseTest { // vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist // DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); // conditions[0].maxClaimableSupply = 500; @@ -367,7 +364,7 @@ contract DropERC721BenchmarkTest is BaseTest { // drop.setClaimConditions(conditions, false); // } - // function test_bechmark_dropERC721_setClaimConditions_two_conditions() public { + // function test_benchmark_dropERC721_setClaimConditions_two_conditions() public { // vm.pauseGasMetering(); // string[] memory inputs = new string[](5); @@ -393,7 +390,7 @@ contract DropERC721BenchmarkTest is BaseTest { // vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist // DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](2); // conditions[0].maxClaimableSupply = 500; @@ -411,7 +408,7 @@ contract DropERC721BenchmarkTest is BaseTest { // drop.setClaimConditions(conditions, false); // } - // function test_bechmark_dropERC721_setClaimConditions_three_conditions() public { + // function test_benchmark_dropERC721_setClaimConditions_three_conditions() public { // vm.pauseGasMetering(); // string[] memory inputs = new string[](5); @@ -437,7 +434,7 @@ contract DropERC721BenchmarkTest is BaseTest { // vm.warp(1); - // address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + // address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist // DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](3); // conditions[0].maxClaimableSupply = 500; diff --git a/src/test/benchmark/EditionStakeBenchmark.t.sol b/src/test/benchmark/EditionStakeBenchmark.t.sol index aa9d76218..f8b604dc4 100644 --- a/src/test/benchmark/EditionStakeBenchmark.t.sol +++ b/src/test/benchmark/EditionStakeBenchmark.t.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.0; import { EditionStake } from "contracts/prebuilts/staking/EditionStake.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; import "../utils/BaseTest.sol"; contract EditionStakeBenchmarkTest is BaseTest { diff --git a/src/test/benchmark/MultiwrapBenchmark.t.sol b/src/test/benchmark/MultiwrapBenchmark.t.sol index ca3c649f7..aef57eccc 100644 --- a/src/test/benchmark/MultiwrapBenchmark.t.sol +++ b/src/test/benchmark/MultiwrapBenchmark.t.sol @@ -5,7 +5,6 @@ import { Multiwrap } from "contracts/prebuilts/multiwrap/Multiwrap.sol"; import { ITokenBundle } from "contracts/extension/interface/ITokenBundle.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; import { MockERC20 } from "../mocks/MockERC20.sol"; import { Wallet } from "../utils/Wallet.sol"; import "../utils/BaseTest.sol"; diff --git a/src/test/benchmark/NFTStakeBenchmark.t.sol b/src/test/benchmark/NFTStakeBenchmark.t.sol index d8b5fe5f6..0f81eef3d 100644 --- a/src/test/benchmark/NFTStakeBenchmark.t.sol +++ b/src/test/benchmark/NFTStakeBenchmark.t.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.0; import { NFTStake } from "contracts/prebuilts/staking/NFTStake.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; import "../utils/BaseTest.sol"; contract NFTStakeBenchmarkTest is BaseTest { diff --git a/src/test/benchmark/SignatureDropBenchmark.t.sol b/src/test/benchmark/SignatureDropBenchmark.t.sol index 31bfa24de..1c6607374 100644 --- a/src/test/benchmark/SignatureDropBenchmark.t.sol +++ b/src/test/benchmark/SignatureDropBenchmark.t.sol @@ -5,14 +5,11 @@ import { SignatureDrop, IDropSinglePhase, IDelayedReveal, ISignatureMintERC721, // Test imports import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "contracts/lib/TWStrings.sol"; import "../utils/BaseTest.sol"; -import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract SignatureDropBenchmarkTest is BaseTest { - using StringsUpgradeable for uint256; - using StringsUpgradeable for address; + 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); @@ -58,7 +55,7 @@ contract SignatureDropBenchmarkTest is BaseTest { SignatureDrop benchmark //////////////////////////////////////////////////////////////*/ - function test_bechmark_signatureDrop_claim_five_tokens() public { + function test_benchmark_signatureDrop_claim_five_tokens() public { vm.pauseGasMetering(); vm.warp(1); @@ -83,7 +80,7 @@ contract SignatureDropBenchmarkTest is BaseTest { sigdrop.claim(receiver, 5, address(0), 0, alp, ""); } - function test_bechmark_signatureDrop_setClaimConditions() public { + function test_benchmark_signatureDrop_setClaimConditions() public { vm.pauseGasMetering(); vm.warp(1); bytes32[] memory proofs = new bytes32[](0); @@ -140,7 +137,7 @@ contract SignatureDropBenchmarkTest is BaseTest { sigdrop.reveal(0, key); } - // function test_bechmark_signatureDrop_claim_one_token() public { + // function test_benchmark_signatureDrop_claim_one_token() public { // vm.pauseGasMetering(); // vm.warp(1); @@ -165,7 +162,7 @@ contract SignatureDropBenchmarkTest is BaseTest { // sigdrop.claim(receiver, 1, address(0), 0, alp, ""); // } - // function test_bechmark_signatureDrop_claim_two_tokens() public { + // function test_benchmark_signatureDrop_claim_two_tokens() public { // vm.pauseGasMetering(); // vm.warp(1); @@ -190,7 +187,7 @@ contract SignatureDropBenchmarkTest is BaseTest { // sigdrop.claim(receiver, 2, address(0), 0, alp, ""); // } - // function test_bechmark_signatureDrop_claim_three_tokens() public { + // function test_benchmark_signatureDrop_claim_three_tokens() public { // vm.pauseGasMetering(); // vm.warp(1); diff --git a/src/test/benchmark/TokenERC1155Benchmark.t.sol b/src/test/benchmark/TokenERC1155Benchmark.t.sol index 47438f721..4cd2563d0 100644 --- a/src/test/benchmark/TokenERC1155Benchmark.t.sol +++ b/src/test/benchmark/TokenERC1155Benchmark.t.sol @@ -4,13 +4,11 @@ pragma solidity ^0.8.0; import { TokenERC1155, IPlatformFee } from "contracts/prebuilts/token/TokenERC1155.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; import "../utils/BaseTest.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract TokenERC1155BenchmarkTest is BaseTest { - using StringsUpgradeable for uint256; + using Strings for uint256; event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri, uint256 quantityMinted); event TokensMintedWithSignature( @@ -83,11 +81,10 @@ contract TokenERC1155BenchmarkTest is BaseTest { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(TokenERC1155.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + TokenERC1155.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = bytes.concat( abi.encode( typehashMintRequest, diff --git a/src/test/benchmark/TokenERC20Benchmark.t.sol b/src/test/benchmark/TokenERC20Benchmark.t.sol index 1932647f0..6b93bb25a 100644 --- a/src/test/benchmark/TokenERC20Benchmark.t.sol +++ b/src/test/benchmark/TokenERC20Benchmark.t.sol @@ -4,13 +4,11 @@ pragma solidity ^0.8.0; import { TokenERC20 } from "contracts/prebuilts/token/TokenERC20.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; import "../utils/BaseTest.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract TokenERC20BenchmarkTest is BaseTest { - using StringsUpgradeable for uint256; + using Strings for uint256; event TokensMinted(address indexed mintedTo, uint256 quantityMinted); event TokensMintedWithSignature( @@ -80,11 +78,10 @@ contract TokenERC20BenchmarkTest is BaseTest { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(TokenERC20.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + TokenERC20.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashMintRequest, _request.to, diff --git a/src/test/benchmark/TokenERC721Benchmark.t.sol b/src/test/benchmark/TokenERC721Benchmark.t.sol index 1408c4221..410709462 100644 --- a/src/test/benchmark/TokenERC721Benchmark.t.sol +++ b/src/test/benchmark/TokenERC721Benchmark.t.sol @@ -4,13 +4,11 @@ pragma solidity ^0.8.0; import { TokenERC721 } from "contracts/prebuilts/token/TokenERC721.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; import "../utils/BaseTest.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract TokenERC721BenchmarkTest is BaseTest { - using StringsUpgradeable for uint256; + using Strings for uint256; event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri); event TokensMintedWithSignature( @@ -81,11 +79,10 @@ contract TokenERC721BenchmarkTest is BaseTest { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(TokenERC721.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + TokenERC721.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashMintRequest, _request.to, diff --git a/src/test/benchmark/TokenStakeBenchmark.t.sol b/src/test/benchmark/TokenStakeBenchmark.t.sol index 1f90676ab..5f639614e 100644 --- a/src/test/benchmark/TokenStakeBenchmark.t.sol +++ b/src/test/benchmark/TokenStakeBenchmark.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import { TokenStake } from "contracts/prebuilts/staking/TokenStake.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; contract TokenStakeBenchmarkTest is BaseTest { diff --git a/src/test/burn-to-claim-drop-BTT/BurnToClaimDropERC721.t.sol b/src/test/burn-to-claim-drop-BTT/BurnToClaimDropERC721.t.sol new file mode 100644 index 000000000..bb294e413 --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/BurnToClaimDropERC721.t.sol @@ -0,0 +1,1885 @@ +// 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 testFail_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)); + + string memory revealedURI = drop.reveal(0, "keyy"); + assertEq(revealedURI, "ipfs://"); + + 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 new file mode 100644 index 000000000..eb188e56a --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/logic/burn-and-claim/burnAndClaim.t.sol @@ -0,0 +1,802 @@ +// 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 new file mode 100644 index 000000000..00e6af1b9 --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/logic/burn-and-claim/burnAndClaim.tree @@ -0,0 +1,83 @@ +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 new file mode 100644 index 000000000..e9b19f812 --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/logic/lazy-mint/lazyMint.t.sol @@ -0,0 +1,265 @@ +// 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 new file mode 100644 index 000000000..39b512286 --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/logic/lazy-mint/lazyMint.tree @@ -0,0 +1,38 @@ +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 new file mode 100644 index 000000000..0f99059b6 --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/logic/other-functions/other.t.sol @@ -0,0 +1,395 @@ +// 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 new file mode 100644 index 000000000..a7ed4a957 --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/logic/other-functions/other.tree @@ -0,0 +1,86 @@ +_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 new file mode 100644 index 000000000..f60d11351 --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/logic/reveal/reveal.t.sol @@ -0,0 +1,241 @@ +// 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 new file mode 100644 index 000000000..febcdb258 --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/logic/reveal/reveal.tree @@ -0,0 +1,16 @@ +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 new file mode 100644 index 000000000..cb1da0318 --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/router/initialize/initialize.t.sol @@ -0,0 +1,455 @@ +// 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 new file mode 100644 index 000000000..295d65120 --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/router/initialize/initialize.tree @@ -0,0 +1,44 @@ +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 new file mode 100644 index 000000000..d9cdb0656 --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/router/other-functions/other.t.sol @@ -0,0 +1,79 @@ +// 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 new file mode 100644 index 000000000..98e8df4fe --- /dev/null +++ b/src/test/burn-to-claim-drop-BTT/router/other-functions/other.tree @@ -0,0 +1,12 @@ +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 index e3ee1953a..036cd243f 100644 --- a/src/test/burn-to-claim-drop/BurnToClaimDropERC721.t.sol +++ b/src/test/burn-to-claim-drop/BurnToClaimDropERC721.t.sol @@ -15,11 +15,10 @@ import { TWProxy } from "contracts/infra/TWProxy.sol"; // Test imports import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "contracts/lib/TWStrings.sol"; contract BurnToClaimDropERC721Test is BaseTest, IExtension { - using TWStrings for uint256; - using TWStrings for address; + 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); @@ -249,9 +248,9 @@ contract BurnToClaimDropERC721Test is BaseTest, IExtension { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(caller), 20), + Strings.toHexString(uint160(caller), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -269,9 +268,9 @@ contract BurnToClaimDropERC721Test is BaseTest, IExtension { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(target), 20), + Strings.toHexString(uint160(target), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -1002,7 +1001,7 @@ contract BurnToClaimDropERC721Test is BaseTest, IExtension { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -1052,7 +1051,7 @@ contract BurnToClaimDropERC721Test is BaseTest, IExtension { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -1111,7 +1110,7 @@ contract BurnToClaimDropERC721Test is BaseTest, IExtension { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -1166,7 +1165,7 @@ contract BurnToClaimDropERC721Test is BaseTest, IExtension { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist BurnToClaimDrop721Logic.ClaimCondition[] memory conditions = new BurnToClaimDrop721Logic.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -1222,7 +1221,7 @@ contract BurnToClaimDropERC721Test is BaseTest, IExtension { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // bytes32[] memory proofs = new bytes32[](0); @@ -1873,9 +1872,9 @@ contract BurnToClaimDropERC721Test is BaseTest, IExtension { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(deployer), 20), + Strings.toHexString(uint160(deployer), 20), " is missing role ", - TWStrings.toHexString(uint256(keccak256("EXTENSION_ROLE")), 32) + Strings.toHexString(uint256(keccak256("EXTENSION_ROLE")), 32) ) ); Permissions(address(drop)).grantRole(keccak256("EXTENSION_ROLE"), address(0x12345)); diff --git a/src/test/drop/DropERC1155.t.sol b/src/test/drop/DropERC1155.t.sol index 7e83cd78c..8905ab80d 100644 --- a/src/test/drop/DropERC1155.t.sol +++ b/src/test/drop/DropERC1155.t.sol @@ -4,14 +4,13 @@ pragma solidity ^0.8.0; import { DropERC1155, IPermissions, ILazyMint } from "contracts/prebuilts/drop/DropERC1155.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract DropERC1155Test is BaseTest { - using StringsUpgradeable for uint256; - using StringsUpgradeable for address; + 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); @@ -46,9 +45,9 @@ contract DropERC1155Test is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(caller), 20), + Strings.toHexString(uint160(caller), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -66,9 +65,9 @@ contract DropERC1155Test is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(target), 20), + Strings.toHexString(uint160(target), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -529,7 +528,7 @@ contract DropERC1155Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -580,7 +579,7 @@ contract DropERC1155Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -640,7 +639,7 @@ contract DropERC1155Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -696,7 +695,7 @@ contract DropERC1155Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC1155.ClaimCondition[] memory conditions = new DropERC1155.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -753,7 +752,7 @@ contract DropERC1155Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // bytes32[] memory proofs = new bytes32[](0); diff --git a/src/test/drop/DropERC20.t.sol b/src/test/drop/DropERC20.t.sol index f0167c315..bfa52455d 100644 --- a/src/test/drop/DropERC20.t.sol +++ b/src/test/drop/DropERC20.t.sol @@ -4,14 +4,13 @@ pragma solidity ^0.8.0; import { DropERC20 } from "contracts/prebuilts/drop/DropERC20.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract DropERC20Test is BaseTest { - using StringsUpgradeable for uint256; - using StringsUpgradeable for address; + using Strings for uint256; + using Strings for address; DropERC20 public drop; @@ -40,9 +39,9 @@ contract DropERC20Test is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(caller), 20), + Strings.toHexString(uint160(caller), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -60,9 +59,9 @@ contract DropERC20Test is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(target), 20), + Strings.toHexString(uint160(target), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -363,7 +362,7 @@ contract DropERC20Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -391,8 +390,8 @@ contract DropERC20Test is BaseTest { inputs[0] = "node"; inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(300 ether); - inputs[3] = Strings.toString(1 ether); + 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); @@ -411,7 +410,7 @@ contract DropERC20Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500 ether; @@ -448,7 +447,7 @@ contract DropERC20Test is BaseTest { inputs[0] = "node"; inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(300 ether); + inputs[2] = Strings.toString(uint256(300 ether)); inputs[3] = Strings.toString(type(uint256).max); // this implies that general price is applicable inputs[4] = "0x0000000000000000000000000000000000000000"; @@ -468,7 +467,7 @@ contract DropERC20Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500 ether; @@ -502,7 +501,7 @@ contract DropERC20Test is BaseTest { inputs[0] = "node"; inputs[1] = "src/test/scripts/generateRoot.ts"; inputs[2] = "0"; // this implies that general limit is applicable - inputs[3] = Strings.toString(5 ether); + inputs[3] = Strings.toString(uint256(5 ether)); inputs[4] = "0x0000000000000000000000000000000000000000"; // general currency will be applicable bytes memory result = vm.ffi(inputs); @@ -521,7 +520,7 @@ contract DropERC20Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC20.ClaimCondition[] memory conditions = new DropERC20.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500 ether; @@ -575,7 +574,7 @@ contract DropERC20Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // bytes32[] memory proofs = new bytes32[](0); diff --git a/src/test/drop/DropERC721.t.sol b/src/test/drop/DropERC721.t.sol index 263568553..8be788e05 100644 --- a/src/test/drop/DropERC721.t.sol +++ b/src/test/drop/DropERC721.t.sol @@ -5,14 +5,13 @@ import { DropERC721, IDelayedReveal, ERC721AUpgradeable, IPermissions, ILazyMint // Test imports import "erc721a-upgradeable/contracts/IERC721AUpgradeable.sol"; -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract DropERC721Test is BaseTest { - using StringsUpgradeable for uint256; - using StringsUpgradeable for address; + 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); @@ -47,9 +46,9 @@ contract DropERC721Test is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(caller), 20), + Strings.toHexString(uint160(caller), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -67,9 +66,9 @@ contract DropERC721Test is BaseTest { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(target), 20), + Strings.toHexString(uint160(target), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); @@ -529,9 +528,9 @@ contract DropERC721Test is BaseTest { bytes memory errorMessage = abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(address(this)), 20), + Strings.toHexString(uint160(address(this)), 20), " is missing role ", - TWStrings.toHexString(uint256(keccak256("METADATA_ROLE")), 32) + Strings.toHexString(uint256(keccak256("METADATA_ROLE")), 32) ); vm.expectRevert(errorMessage); @@ -816,7 +815,7 @@ contract DropERC721Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -866,7 +865,7 @@ contract DropERC721Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -925,7 +924,7 @@ contract DropERC721Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -980,7 +979,7 @@ contract DropERC721Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); // in allowlist + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // in allowlist DropERC721.ClaimCondition[] memory conditions = new DropERC721.ClaimCondition[](1); conditions[0].maxClaimableSupply = 500; @@ -1036,7 +1035,7 @@ contract DropERC721Test is BaseTest { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // bytes32[] memory proofs = new bytes32[](0); diff --git a/src/test/drop/drop-erc1155/_beforeClaim/_beforeClaim.t.sol b/src/test/drop/drop-erc1155/_beforeClaim/_beforeClaim.t.sol new file mode 100644 index 000000000..8397c3195 --- /dev/null +++ b/src/test/drop/drop-erc1155/_beforeClaim/_beforeClaim.t.sol @@ -0,0 +1,71 @@ +// 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 new file mode 100644 index 000000000..a5a114d07 --- /dev/null +++ b/src/test/drop/drop-erc1155/_beforeClaim/_beforeClaim.tree @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..b4c32607c --- /dev/null +++ b/src/test/drop/drop-erc1155/_beforeTokenTransfer/_beforeTokenTransfer.sol @@ -0,0 +1,110 @@ +// 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 new file mode 100644 index 000000000..b5e147b76 --- /dev/null +++ b/src/test/drop/drop-erc1155/_beforeTokenTransfer/_beforeTokenTransfer.tree @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..2be22888c --- /dev/null +++ b/src/test/drop/drop-erc1155/_canSetFunctions/_canSetFunctions.sol @@ -0,0 +1,151 @@ +// 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 new file mode 100644 index 000000000..4b214c4e9 --- /dev/null +++ b/src/test/drop/drop-erc1155/_canSetFunctions/_canSetFunctions.tree @@ -0,0 +1,41 @@ +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 new file mode 100644 index 000000000..86e365f33 --- /dev/null +++ b/src/test/drop/drop-erc1155/burnBatch/burnBatch.t.sol @@ -0,0 +1,129 @@ +// 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 new file mode 100644 index 000000000..e8685085e --- /dev/null +++ b/src/test/drop/drop-erc1155/burnBatch/burnBatch.tree @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000..bd2e68b9a --- /dev/null +++ b/src/test/drop/drop-erc1155/collectPriceOnClaim/collectPriceOnClaim.t.sol @@ -0,0 +1,272 @@ +// 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 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 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 platformFeeRecipientBefore = address(platformFeeRecipient).balance; + proxy.collectPriceOnClaimHarness{ value: collectPrice_msgValue }( + 0, + saleRecipient, + collectPrice_quantityToClaim, + collectPrice_currency, + collectPrice_pricePerToken + ); + + uint256 balanceSaleRecipientAfter = address(saleRecipient).balance; + uint256 platformFeeRecipientAfter = address(platformFeeRecipient).balance; + uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; + uint256 expectedSaleRecipientProceed = collectPrice_msgValue - expectedPlatformFee; + + assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); + assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); + } + + function test_transferERC20ToSaleRecipient() public erc20Currency pricePerTokenNotZero { + uint256 balanceSaleRecipientBefore = erc20.balanceOf(saleRecipient); + 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 platformFeeRecipientAfter = erc20.balanceOf(platformFeeRecipient); + uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; + uint256 expectedSaleRecipientProceed = collectPrice_pricePerToken - expectedPlatformFee; + + assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); + assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); + } + + function test_transferNativeCurrencyToTokenIdSaleRecipient() + public + nativeCurrency + pricePerTokenNotZero + msgValueNotZero + saleRecipientSet + primarySaleRecipientZeroAddress + { + uint256 balanceSaleRecipientBefore = address(collectPrice_tokenSaleRecipient).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 platformFeeRecipientAfter = address(platformFeeRecipient).balance; + uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; + uint256 expectedSaleRecipientProceed = collectPrice_msgValue - expectedPlatformFee; + + assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); + assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); + } + + function test_transferERC20ToTokenIdSaleRecipient() public erc20Currency pricePerTokenNotZero saleRecipientSet { + uint256 balanceSaleRecipientBefore = erc20.balanceOf(collectPrice_tokenSaleRecipient); + 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 platformFeeRecipientAfter = erc20.balanceOf(platformFeeRecipient); + uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; + uint256 expectedSaleRecipientProceed = collectPrice_pricePerToken - expectedPlatformFee; + + assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); + assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); + } + + function test_transferNativeCurrencyToPrimarySaleRecipient() + public + nativeCurrency + pricePerTokenNotZero + msgValueNotZero + { + uint256 balanceSaleRecipientBefore = address(saleRecipient).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 platformFeeRecipientAfter = address(platformFeeRecipient).balance; + uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; + uint256 expectedSaleRecipientProceed = collectPrice_msgValue - expectedPlatformFee; + + assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); + assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); + } + + function test_transferERC20ToPrimarySaleRecipient() public erc20Currency pricePerTokenNotZero { + uint256 balanceSaleRecipientBefore = erc20.balanceOf(saleRecipient); + 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 platformFeeRecipientAfter = erc20.balanceOf(platformFeeRecipient); + uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; + uint256 expectedSaleRecipientProceed = collectPrice_pricePerToken - expectedPlatformFee; + + assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); + assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); + } +} diff --git a/src/test/drop/drop-erc1155/collectPriceOnClaim/collectPriceOnClaim.tree b/src/test/drop/drop-erc1155/collectPriceOnClaim/collectPriceOnClaim.tree new file mode 100644 index 000000000..6cb5cd180 --- /dev/null +++ b/src/test/drop/drop-erc1155/collectPriceOnClaim/collectPriceOnClaim.tree @@ -0,0 +1,44 @@ +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 new file mode 100644 index 000000000..6a54d0d3d --- /dev/null +++ b/src/test/drop/drop-erc1155/freezeBatchBaseURI/freezeBatchBaseURI.t.sol @@ -0,0 +1,85 @@ +// 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_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.encodePacked( + "Permissions: account ", + Strings.toHexString(uint160(unauthorized), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ); + drop.freezeBatchBaseURI(0); + } + + function test_revert_IndexTooHigh() public lazyMint callerWithMetadataRole { + vm.expectRevert("Invalid index"); + drop.freezeBatchBaseURI(1); + } + + function test_revert_EmptyBaseURI() public lazyMintEmptyUri callerWithMetadataRole { + vm.expectRevert("Invalid batch"); + 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 new file mode 100644 index 000000000..7a9a00b7f --- /dev/null +++ b/src/test/drop/drop-erc1155/freezeBatchBaseURI/freezeBatchBaseURI.tree @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..0dff9ca09 --- /dev/null +++ b/src/test/drop/drop-erc1155/initialize/initialize.t.sol @@ -0,0 +1,407 @@ +// 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"; + +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("Exceeds max bps"); + 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("Exceeds max bps"); + 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 new file mode 100644 index 000000000..76c8d15d0 --- /dev/null +++ b/src/test/drop/drop-erc1155/initialize/initialize.tree @@ -0,0 +1,50 @@ +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 new file mode 100644 index 000000000..661c0e3db --- /dev/null +++ b/src/test/drop/drop-erc1155/miscellaneous/miscellaneous.t.sol @@ -0,0 +1,69 @@ +// 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 new file mode 100644 index 000000000..e74189c71 --- /dev/null +++ b/src/test/drop/drop-erc1155/miscellaneous/miscellaneous.tree @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..8b694d0b0 --- /dev/null +++ b/src/test/drop/drop-erc1155/setMaxTotalSupply/setMaxTotalSupply.t.sol @@ -0,0 +1,64 @@ +// 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.encodePacked( + "Permissions: account ", + Strings.toHexString(uint160(unauthorized), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ); + 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 new file mode 100644 index 000000000..8aa6ad0ef --- /dev/null +++ b/src/test/drop/drop-erc1155/setMaxTotalSupply/setMaxTotalSupply.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..7c3ce82ae --- /dev/null +++ b/src/test/drop/drop-erc1155/setSaleRecipientForToken/setSaleRecipientForToken.t.sol @@ -0,0 +1,65 @@ +// 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.encodePacked( + "Permissions: account ", + Strings.toHexString(uint160(unauthorized), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ); + 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 new file mode 100644 index 000000000..72d7c2cad --- /dev/null +++ b/src/test/drop/drop-erc1155/setSaleRecipientForToken/setSaleRecipientForToken.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..411206cc5 --- /dev/null +++ b/src/test/drop/drop-erc1155/transferTokensOnClaim/transferTokensOnClaim.t.sol @@ -0,0 +1,113 @@ +// 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 new file mode 100644 index 000000000..eb600dbed --- /dev/null +++ b/src/test/drop/drop-erc1155/transferTokensOnClaim/transferTokensOnClaim.tree @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..c96599065 --- /dev/null +++ b/src/test/drop/drop-erc1155/updateBatchBaseURI/updateBatchBaseURI.t.sol @@ -0,0 +1,98 @@ +// 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_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.encodePacked( + "Permissions: account ", + Strings.toHexString(uint160(unauthorized), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ); + drop.updateBatchBaseURI(0, updatedBaseURI); + } + + function test_revert_IndexTooHigh() public lazyMint callerWithMetadataRole { + vm.expectRevert("Invalid index"); + drop.updateBatchBaseURI(1, updatedBaseURI); + } + + function test_revert_BatchFrozen() public lazyMint batchFrozen callerWithMetadataRole { + vm.expectRevert("Batch frozen"); + 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 new file mode 100644 index 000000000..75ffaadcf --- /dev/null +++ b/src/test/drop/drop-erc1155/updateBatchBaseURI/updateBatchBaseURI.tree @@ -0,0 +1,9 @@ +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 new file mode 100644 index 000000000..c6c3e681b --- /dev/null +++ b/src/test/drop/drop-erc20/_beforeClaim/_beforeClaim.t.sol @@ -0,0 +1,52 @@ +// 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 new file mode 100644 index 000000000..f1cf867a4 --- /dev/null +++ b/src/test/drop/drop-erc20/_beforeClaim/_beforeClaim.tree @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..2b17cdd08 --- /dev/null +++ b/src/test/drop/drop-erc20/_canSetFunctions/_canSetFunctions.t.sol @@ -0,0 +1,93 @@ +// 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 new file mode 100644 index 000000000..2f2da72e4 --- /dev/null +++ b/src/test/drop/drop-erc20/_canSetFunctions/_canSetFunctions.tree @@ -0,0 +1,23 @@ +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 new file mode 100644 index 000000000..b78b3eed9 --- /dev/null +++ b/src/test/drop/drop-erc20/_collectPriceOnClaim/_collectPriceOnClaim.t.sol @@ -0,0 +1,209 @@ +// 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; + + 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))); + } + + 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; + + proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 1 ether, currency, pricePerToken); + + uint256 afterBalancePrimarySaleRecipient = address(primarySaleRecipient).balance; + uint256 afterBalancePlatformFeeRecipient = address(platformFeeRecipient).balance; + + uint256 platformFeeVal = (msgValue * platformFeeBps) / MAX_BPS; + uint256 primarySaleRecipientVal = msgValue - platformFeeVal; + + assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); + assertEq(beforeBalancePlatformFeeRecipient + platformFeeVal, afterBalancePlatformFeeRecipient); + } + + 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 beforeBalancePlatformFeeRecipient = erc20.balanceOf(platformFeeRecipient); + + proxy.harness_collectPrice(primarySaleRecipient, pricePerToken, currency, pricePerToken); + + uint256 afterBalancePrimarySaleRecipient = erc20.balanceOf(primarySaleRecipient); + uint256 afterBalancePlatformFeeRecipient = erc20.balanceOf(platformFeeRecipient); + + uint256 platformFeeVal = (pricePerToken * platformFeeBps) / MAX_BPS; + uint256 primarySaleRecipientVal = 1 ether - platformFeeVal; + + assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); + assertEq(beforeBalancePlatformFeeRecipient + platformFeeVal, afterBalancePlatformFeeRecipient); + } + + 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 beforeBalancePlatformFeeRecipient = erc20.balanceOf(platformFeeRecipient); + + proxy.harness_collectPrice(primarySaleRecipient, pricePerToken, currency, pricePerToken); + + uint256 afterBalancePrimarySaleRecipient = erc20.balanceOf(storedPrimarySaleRecipient); + uint256 afterBalancePlatformFeeRecipient = erc20.balanceOf(platformFeeRecipient); + + uint256 platformFeeVal = (pricePerToken * platformFeeBps) / MAX_BPS; + uint256 primarySaleRecipientVal = 1 ether - platformFeeVal; + + assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); + assertEq(beforeBalancePlatformFeeRecipient + platformFeeVal, afterBalancePlatformFeeRecipient); + } + + 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; + + proxy.harness_collectPrice{ value: msgValue }(primarySaleRecipient, 1 ether, currency, pricePerToken); + + uint256 afterBalancePrimarySaleRecipient = address(storedPrimarySaleRecipient).balance; + uint256 afterBalancePlatformFeeRecipient = address(platformFeeRecipient).balance; + + uint256 platformFeeVal = (msgValue * platformFeeBps) / MAX_BPS; + uint256 primarySaleRecipientVal = msgValue - platformFeeVal; + + assertEq(beforeBalancePrimarySaleRecipient + primarySaleRecipientVal, afterBalancePrimarySaleRecipient); + assertEq(beforeBalancePlatformFeeRecipient + platformFeeVal, afterBalancePlatformFeeRecipient); + } +} diff --git a/src/test/drop/drop-erc20/_collectPriceOnClaim/_collectPriceOnClaim.tree b/src/test/drop/drop-erc20/_collectPriceOnClaim/_collectPriceOnClaim.tree new file mode 100644 index 000000000..933ae6877 --- /dev/null +++ b/src/test/drop/drop-erc20/_collectPriceOnClaim/_collectPriceOnClaim.tree @@ -0,0 +1,44 @@ +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 new file mode 100644 index 000000000..c6a6ddbd6 --- /dev/null +++ b/src/test/drop/drop-erc20/initialize/initialize.t.sol @@ -0,0 +1,233 @@ +// 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_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("Exceeds max bps"); + 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 new file mode 100644 index 000000000..6731bc81e --- /dev/null +++ b/src/test/drop/drop-erc20/initialize/intialize.tree @@ -0,0 +1,33 @@ +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 new file mode 100644 index 000000000..0845465ff --- /dev/null +++ b/src/test/drop/drop-erc20/miscellaneous/miscellaneous.t.sol @@ -0,0 +1,117 @@ +// 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 new file mode 100644 index 000000000..61c35271e --- /dev/null +++ b/src/test/drop/drop-erc20/miscellaneous/miscellaneous.tree @@ -0,0 +1,30 @@ +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 new file mode 100644 index 000000000..88c338e36 --- /dev/null +++ b/src/test/drop/drop-erc20/setMaxTotalSupply/setMaxTotalSupply.t.sol @@ -0,0 +1,52 @@ +// 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.encodePacked( + "Permissions: account ", + Strings.toHexString(uint160(address(this)), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ); + 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 new file mode 100644 index 000000000..a8b23e9ca --- /dev/null +++ b/src/test/drop/drop-erc20/setMaxTotalSupply/setMaxTotalSupply.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..f30885a51 --- /dev/null +++ b/src/test/drop/drop-erc721/_beforeClaim/_beforeClaim.t.sol @@ -0,0 +1,88 @@ +// 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 new file mode 100644 index 000000000..a225a8be4 --- /dev/null +++ b/src/test/drop/drop-erc721/_beforeClaim/_beforeClaim.tree @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..4dbdf57b1 --- /dev/null +++ b/src/test/drop/drop-erc721/_canSetFunctions/_canSetFunctions.t.sol @@ -0,0 +1,137 @@ +// 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_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("Not authorized"); + 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("Not authorized"); + 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("Not authorized"); + 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("Not authorized"); + 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("Not authorized"); + 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("Not authorized"); + 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("Not authorized"); + 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 new file mode 100644 index 000000000..72bfbe39a --- /dev/null +++ b/src/test/drop/drop-erc721/_canSetFunctions/_canSetFunctions.tree @@ -0,0 +1,41 @@ +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 new file mode 100644 index 000000000..cee3d2798 --- /dev/null +++ b/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.t.sol @@ -0,0 +1,142 @@ +// 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; + + 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))); + } + + 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; + proxy.collectionPriceOnClaim{ value: collectPrice_msgValue }( + saleRecipient, + collectPrice_quantityToClaim, + collectPrice_currency, + collectPrice_pricePerToken + ); + + uint256 balanceSaleRecipientAfter = address(saleRecipient).balance; + uint256 platformFeeRecipientAfter = address(platformFeeRecipient).balance; + uint256 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; + uint256 expectedSaleRecipientProceed = collectPrice_msgValue - expectedPlatformFee; + + assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); + assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); + } + + function test_transferERC20() public erc20Currency pricePerTokenNotZero { + uint256 balanceSaleRecipientBefore = erc20.balanceOf(saleRecipient); + uint256 platformFeeRecipientBefore = erc20.balanceOf(platformFeeRecipient); + 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 expectedPlatformFee = (collectPrice_pricePerToken * platformFeeBps) / MAX_BPS; + uint256 expectedSaleRecipientProceed = collectPrice_pricePerToken - expectedPlatformFee; + + assertEq(balanceSaleRecipientAfter - balanceSaleRecipientBefore, expectedSaleRecipientProceed); + assertEq(platformFeeRecipientAfter - platformFeeRecipientBefore, expectedPlatformFee); + } +} diff --git a/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.tree b/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.tree new file mode 100644 index 000000000..b962e7d4e --- /dev/null +++ b/src/test/drop/drop-erc721/_collectPriceOnClaim/_collectPriceOnClaim.tree @@ -0,0 +1,20 @@ +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 new file mode 100644 index 000000000..6bf501586 --- /dev/null +++ b/src/test/drop/drop-erc721/_transferTokensOnClaim/_tranferTokensOnClaim.tree @@ -0,0 +1,2 @@ +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 new file mode 100644 index 000000000..87e98d606 --- /dev/null +++ b/src/test/drop/drop-erc721/_transferTokensOnClaim/_transferTokensOnClaim.t.sol @@ -0,0 +1,76 @@ +// 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 new file mode 100644 index 000000000..799435fee --- /dev/null +++ b/src/test/drop/drop-erc721/freezeBatchBaseURI/freezeBatchBaseURI.t.sol @@ -0,0 +1,105 @@ +// 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_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.encodePacked( + "Permissions: account ", + Strings.toHexString(uint160(unauthorized), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ); + 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("Invalid batch"); + 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 new file mode 100644 index 000000000..9c29a9a83 --- /dev/null +++ b/src/test/drop/drop-erc721/freezeBatchBaseURI/freezeBatchBaseURI.tree @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..b43fb881d --- /dev/null +++ b/src/test/drop/drop-erc721/initalizer/initializer.t.sol @@ -0,0 +1,407 @@ +// 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_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("Exceeds max bps"); + 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("Exceeds max bps"); + 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 new file mode 100644 index 000000000..cec2fc97d --- /dev/null +++ b/src/test/drop/drop-erc721/initalizer/initializer.tree @@ -0,0 +1,53 @@ +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 new file mode 100644 index 000000000..baff5a29b --- /dev/null +++ b/src/test/drop/drop-erc721/lazyMint/lazyMint.t.sol @@ -0,0 +1,219 @@ +// 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_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("Not authorized"); + drop.lazyMint(lazyMint_amount, lazyMint_revealedURI, lazymint_data); + } + + function test_revert_AmountEqualZero() public callerWithMinterRole dataLengthZero amountEqualZero { + vm.expectRevert("0 amt"); + 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 new file mode 100644 index 000000000..84738d926 --- /dev/null +++ b/src/test/drop/drop-erc721/lazyMint/lazyMint.tree @@ -0,0 +1,33 @@ +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 new file mode 100644 index 000000000..9ec52d8f8 --- /dev/null +++ b/src/test/drop/drop-erc721/miscellaneous/miscellaneous.t.sol @@ -0,0 +1,195 @@ +// 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 new file mode 100644 index 000000000..0e15c80ad --- /dev/null +++ b/src/test/drop/drop-erc721/miscellaneous/miscellaneous.tree @@ -0,0 +1,23 @@ +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 new file mode 100644 index 000000000..13b53c7ab --- /dev/null +++ b/src/test/drop/drop-erc721/reveal/reveal.t.sol @@ -0,0 +1,133 @@ +// 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"; +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.encodePacked( + "Permissions: account ", + Strings.toHexString(uint160(unauthorized), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ); + 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("Invalid index"); + drop.reveal(reveal_index, reveal_key); + } + + function test_revert_InvalidKey() public validIndex lazyMintEncrypted invalidKey callerWithMetadataRole { + vm.expectRevert("Incorrect key"); + drop.reveal(reveal_index, reveal_key); + } + + function test_revert_NoEncryptedData() public validIndex lazyMintUnEncrypted callerWithMetadataRole { + vm.expectRevert("Nothing to reveal"); + 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 new file mode 100644 index 000000000..5e1e6717d --- /dev/null +++ b/src/test/drop/drop-erc721/reveal/reveal.tree @@ -0,0 +1,16 @@ +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 new file mode 100644 index 000000000..4ec6e1e35 --- /dev/null +++ b/src/test/drop/drop-erc721/setMaxTotalSupply/setMaxTotalSupply.t.sol @@ -0,0 +1,59 @@ +// 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.encodePacked( + "Permissions: account ", + Strings.toHexString(uint160(unauthorized), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ); + 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 new file mode 100644 index 000000000..19631c92f --- /dev/null +++ b/src/test/drop/drop-erc721/setMaxTotalSupply/setMaxTotalSupply.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..b50e1b43c --- /dev/null +++ b/src/test/drop/drop-erc721/updateBatchBaseURI/updateBatchBaseURI.t.sol @@ -0,0 +1,119 @@ +// 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"; +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.encodePacked( + "Permissions: account ", + Strings.toHexString(uint160(unauthorized), 20), + " is missing role ", + Strings.toHexString(uint256(role), 32) + ) + ); + 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("Batch frozen"); + 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 new file mode 100644 index 000000000..b03e1198f --- /dev/null +++ b/src/test/drop/drop-erc721/updateBatchBaseURI/updateBatchBaseURI.tree @@ -0,0 +1,12 @@ +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/EnglishAuctions.t.sol b/src/test/marketplace/EnglishAuctions.t.sol index 8525a275f..ac3a07581 100644 --- a/src/test/marketplace/EnglishAuctions.t.sol +++ b/src/test/marketplace/EnglishAuctions.t.sol @@ -598,7 +598,7 @@ contract MarketplaceEnglishAuctionsTest is BaseTest, IExtension { ); vm.prank(seller); - vm.expectRevert("ERC721: caller is not token owner nor approved"); + vm.expectRevert("ERC721: caller is not token owner or approved"); EnglishAuctionsLogic(marketplace).createAuction(auctionParams); } @@ -2095,12 +2095,7 @@ contract BreitwieserTheCreator is BaseTest, IERC721Receiver, IExtension { address public seller; address public buyer; - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external pure returns (bytes4) { + function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) { return IERC721Receiver.onERC721Received.selector; } diff --git a/src/test/marketplace/direct-listings/_payout/_payout.t.sol b/src/test/marketplace/direct-listings/_payout/_payout.t.sol new file mode 100644 index 000000000..3c9d167d9 --- /dev/null +++ b/src/test/marketplace/direct-listings/_payout/_payout.t.sol @@ -0,0 +1,343 @@ +// 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; + + // 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))); + 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; + + { + // Platform fee recipient receives correct amount + assertBalERC20Eq(address(erc20), platformFeeRecipient, platformFees); + + // Seller gets total price minus royalty amounts + assertBalERC20Eq(address(erc20), seller, totalPrice - platformFees); + } + } + + 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, 9999); // 99.99% fees + + // 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; + + { + // 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); + } + } +} diff --git a/src/test/marketplace/direct-listings/_payout/_payout.tree b/src/test/marketplace/direct-listings/_payout/_payout.tree new file mode 100644 index 000000000..3d09e5d13 --- /dev/null +++ b/src/test/marketplace/direct-listings/_payout/_payout.tree @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000..3673ef854 --- /dev/null +++ b/src/test/marketplace/direct-listings/_transferListingTokens/_transferListingTokens.t.sol @@ -0,0 +1,175 @@ +// 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 new file mode 100644 index 000000000..02204ec44 --- /dev/null +++ b/src/test/marketplace/direct-listings/_transferListingTokens/_transferListingTokens.tree @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..f57643ca0 --- /dev/null +++ b/src/test/marketplace/direct-listings/_validateERC20BalAndAllowance/_validateERC20BalAndAllowance.t.sol @@ -0,0 +1,156 @@ +// 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 new file mode 100644 index 000000000..04b6010d4 --- /dev/null +++ b/src/test/marketplace/direct-listings/_validateERC20BalAndAllowance/_validateERC20BalAndAllowance.tree @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..51b681e34 --- /dev/null +++ b/src/test/marketplace/direct-listings/_validateNewListing/_validateNewListing.t.sol @@ -0,0 +1,292 @@ +// 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 new file mode 100644 index 000000000..a2520cf27 --- /dev/null +++ b/src/test/marketplace/direct-listings/_validateNewListing/_validateNewListing.tree @@ -0,0 +1,23 @@ +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 new file mode 100644 index 000000000..5436d89f7 --- /dev/null +++ b/src/test/marketplace/direct-listings/_validateOwnershipAndApproval/_validateOwnershipAndApproval.t.sol @@ -0,0 +1,254 @@ +// 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 new file mode 100644 index 000000000..2ee82b493 --- /dev/null +++ b/src/test/marketplace/direct-listings/_validateOwnershipAndApproval/_validateOwnershipAndApproval.tree @@ -0,0 +1,21 @@ +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 new file mode 100644 index 000000000..bdd8dbae8 --- /dev/null +++ b/src/test/marketplace/direct-listings/approveBuyerForListing/approveBuyerForListing.t.sol @@ -0,0 +1,222 @@ +// 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 new file mode 100644 index 000000000..5b7aeff2a --- /dev/null +++ b/src/test/marketplace/direct-listings/approveBuyerForListing/approveBuyerForListing.tree @@ -0,0 +1,15 @@ +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 new file mode 100644 index 000000000..e4e70007d --- /dev/null +++ b/src/test/marketplace/direct-listings/approveCurrencyForListing/approveCurrencyForListing.t.sol @@ -0,0 +1,237 @@ +// 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 new file mode 100644 index 000000000..1c7912edc --- /dev/null +++ b/src/test/marketplace/direct-listings/approveCurrencyForListing/approveCurrencyForListing.tree @@ -0,0 +1,19 @@ +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 new file mode 100644 index 000000000..8c11e6540 --- /dev/null +++ b/src/test/marketplace/direct-listings/buyFromListing/buyFromListing.t.sol @@ -0,0 +1,636 @@ +// 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 new file mode 100644 index 000000000..0b7ae81fa --- /dev/null +++ b/src/test/marketplace/direct-listings/buyFromListing/buyFromListing.tree @@ -0,0 +1,172 @@ +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 new file mode 100644 index 000000000..6e77c4fe0 --- /dev/null +++ b/src/test/marketplace/direct-listings/cancelListing/cancelListing.t.sol @@ -0,0 +1,207 @@ +// 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 new file mode 100644 index 000000000..dd34eb23d --- /dev/null +++ b/src/test/marketplace/direct-listings/cancelListing/cancelListing.tree @@ -0,0 +1,9 @@ +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 new file mode 100644 index 000000000..415a1b71d --- /dev/null +++ b/src/test/marketplace/direct-listings/createListing/createListing.t.sol @@ -0,0 +1,363 @@ +// 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 new file mode 100644 index 000000000..8964c7798 --- /dev/null +++ b/src/test/marketplace/direct-listings/createListing/createListing.tree @@ -0,0 +1,27 @@ +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 new file mode 100644 index 000000000..6b35615ea --- /dev/null +++ b/src/test/marketplace/direct-listings/updateListing/updateListing.t.sol @@ -0,0 +1,502 @@ +// 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 new file mode 100644 index 000000000..982ed1076 --- /dev/null +++ b/src/test/marketplace/direct-listings/updateListing/updateListing.tree @@ -0,0 +1,43 @@ +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 new file mode 100644 index 000000000..a0ebbfda9 --- /dev/null +++ b/src/test/marketplace/english-auctions/_payout/_payout.t.sol @@ -0,0 +1,297 @@ +// 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; + + // 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))); + 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; + + { + // Platform fee recipient receives correct amount + assertBalERC20Eq(address(erc20), platformFeeRecipient, platformFees); + + // Seller gets total price minus royalty amounts + assertBalERC20Eq(address(erc20), seller, totalPrice - platformFees); + } + } + + 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, 9999); // 99.99% fees; + + // 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; + + { + // 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); + } + } +} diff --git a/src/test/marketplace/english-auctions/_payout/_payout.tree b/src/test/marketplace/english-auctions/_payout/_payout.tree new file mode 100644 index 000000000..36b930e11 --- /dev/null +++ b/src/test/marketplace/english-auctions/_payout/_payout.tree @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000..5c36a091d --- /dev/null +++ b/src/test/marketplace/english-auctions/_transferAuctionTokens/_transferAuctionTokens.t.sol @@ -0,0 +1,198 @@ +// 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 new file mode 100644 index 000000000..c44dc8c55 --- /dev/null +++ b/src/test/marketplace/english-auctions/_transferAuctionTokens/_transferAuctionTokens.tree @@ -0,0 +1,9 @@ +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 new file mode 100644 index 000000000..5f716875f --- /dev/null +++ b/src/test/marketplace/english-auctions/_validateNewAuction/_validateNewAuction.t.sol @@ -0,0 +1,304 @@ +// 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 new file mode 100644 index 000000000..24429d6c5 --- /dev/null +++ b/src/test/marketplace/english-auctions/_validateNewAuction/_validateNewAuction.tree @@ -0,0 +1,20 @@ +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 new file mode 100644 index 000000000..ad7041106 --- /dev/null +++ b/src/test/marketplace/english-auctions/bidInAuction/bidInAuction.t.sol @@ -0,0 +1,683 @@ +// 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 new file mode 100644 index 000000000..bcb182035 --- /dev/null +++ b/src/test/marketplace/english-auctions/bidInAuction/bidInAuction.tree @@ -0,0 +1,52 @@ +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 new file mode 100644 index 000000000..aa69aacb2 --- /dev/null +++ b/src/test/marketplace/english-auctions/cancelAuction/cancelAuction.t.sol @@ -0,0 +1,261 @@ +// 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 new file mode 100644 index 000000000..397d6bc6d --- /dev/null +++ b/src/test/marketplace/english-auctions/cancelAuction/cancelAuction.tree @@ -0,0 +1,14 @@ +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 new file mode 100644 index 000000000..0ea873384 --- /dev/null +++ b/src/test/marketplace/english-auctions/collectAuctionPayout/collectAuctionPayout.t.sol @@ -0,0 +1,298 @@ +// 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; + + // 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_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) + ); + + assertEq(erc20.balanceOf(address(marketplace)), 0); + assertEq(erc20.balanceOf(seller), marketplaceBal); + } +} diff --git a/src/test/marketplace/english-auctions/collectAuctionPayout/collectAuctionPayout.tree b/src/test/marketplace/english-auctions/collectAuctionPayout/collectAuctionPayout.tree new file mode 100644 index 000000000..571fd4a61 --- /dev/null +++ b/src/test/marketplace/english-auctions/collectAuctionPayout/collectAuctionPayout.tree @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000..11b451d67 --- /dev/null +++ b/src/test/marketplace/english-auctions/collectAuctionTokens/collectAuctionTokens.t.sol @@ -0,0 +1,298 @@ +// 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 new file mode 100644 index 000000000..d54bd1ba3 --- /dev/null +++ b/src/test/marketplace/english-auctions/collectAuctionTokens/collectAuctionTokens.tree @@ -0,0 +1,18 @@ +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 new file mode 100644 index 000000000..8bc759b31 --- /dev/null +++ b/src/test/marketplace/english-auctions/createAuction/createAuction.t.sol @@ -0,0 +1,318 @@ +// 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 new file mode 100644 index 000000000..dcfa71e4c --- /dev/null +++ b/src/test/marketplace/english-auctions/createAuction/createAuction.tree @@ -0,0 +1,13 @@ +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 index 3c0340c28..a819ee55b 100644 --- a/src/test/minimal-factory/MinimalFactory.t.sol +++ b/src/test/minimal-factory/MinimalFactory.t.sol @@ -5,7 +5,6 @@ import "contracts/infra/TWMinimalFactory.sol"; import "contracts/infra/TWProxy.sol"; import "@openzeppelin/contracts/proxy/Clones.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; import "../utils/BaseTest.sol"; @@ -21,11 +20,7 @@ contract DummyUpgradeable { contract TWNotMinimalFactory { /// @dev Deploys a proxy that points to the given implementation. - function deployProxyByImplementation( - address _implementation, - bytes memory _data, - bytes32 _salt - ) public { + function deployProxyByImplementation(address _implementation, bytes memory _data, bytes32 _salt) public { address deployedProxy = Clones.cloneDeterministic(_implementation, _salt); if (_data.length > 0) { diff --git a/src/test/mocks/MockContractPublisher.sol b/src/test/mocks/MockContractPublisher.sol index b7ec5282a..3ce6a09f4 100644 --- a/src/test/mocks/MockContractPublisher.sol +++ b/src/test/mocks/MockContractPublisher.sol @@ -7,12 +7,9 @@ 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) - { + function getAllPublishedContracts( + address + ) external pure override returns (CustomContractInstance[] memory published) { CustomContractInstance[] memory mocks = new CustomContractInstance[](1); mocks[0] = CustomContractInstance( "MockContract", @@ -24,19 +21,17 @@ contract MockContractPublisher is IContractPublisher { return mocks; } - function getPublishedContractVersions(address, string memory) - external - pure - returns (CustomContractInstance[] memory published) - { + 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) - { + function getPublishedContract( + address, + string memory + ) external pure returns (CustomContractInstance memory published) { return CustomContractInstance("", 0, "", "", address(0)); } @@ -57,11 +52,9 @@ contract MockContractPublisher is IContractPublisher { return ""; } - function getPublishedUriFromCompilerUri(string memory) - external - pure - returns (string[] memory publishedMetadataUris) - { + 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 index 2e26802c0..c1af0dc5a 100644 --- a/src/test/mocks/MockERC1155.sol +++ b/src/test/mocks/MockERC1155.sol @@ -6,11 +6,7 @@ import "@openzeppelin/contracts/token/ERC1155/presets/ERC1155PresetMinterPauser. contract MockERC1155 is ERC1155PresetMinterPauser { constructor() ERC1155PresetMinterPauser("ipfs://BaseURI") {} - function mint( - address to, - uint256 id, - uint256 amount - ) public virtual { + function mint(address to, uint256 id, uint256 amount) public virtual { _mint(to, id, amount, ""); } @@ -18,11 +14,7 @@ contract MockERC1155 is ERC1155PresetMinterPauser { return true; } - function mintBatch( - address to, - uint256[] memory ids, - uint256[] memory amounts - ) public virtual { + 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, ""); diff --git a/src/test/mocks/MockERC1155NonBurnable.sol b/src/test/mocks/MockERC1155NonBurnable.sol new file mode 100644 index 000000000..dff355c7f --- /dev/null +++ b/src/test/mocks/MockERC1155NonBurnable.sol @@ -0,0 +1,20 @@ +// 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 index d895d08e0..6b3a5bb8a 100644 --- a/src/test/mocks/MockERC20.sol +++ b/src/test/mocks/MockERC20.sol @@ -18,11 +18,7 @@ contract MockERC20 is ERC20PresetMinterPauser, ERC20Permit { taxActive = !taxActive; } - function _transfer( - address from, - address to, - uint256 amount - ) internal override { + function _transfer(address from, address to, uint256 amount) internal override { if (taxActive) { uint256 tax = (amount * 10) / 100; amount -= tax; diff --git a/src/test/mocks/MockERC20NonCompliant.sol b/src/test/mocks/MockERC20NonCompliant.sol index 8bc8889b5..0e938b1de 100644 --- a/src/test/mocks/MockERC20NonCompliant.sol +++ b/src/test/mocks/MockERC20NonCompliant.sol @@ -37,21 +37,13 @@ contract MockERC20NonCompliant { } // non-compliant ERC20 as transferFrom doesn't return a bool - function transferFrom( - address from, - address to, - uint256 amount - ) public { + 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 { + 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"); @@ -77,22 +69,14 @@ contract MockERC20NonCompliant { _mint(to, amount); } - function _approve( - address owner, - address spender, - uint256 amount - ) internal virtual { + 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 { + 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"); diff --git a/src/test/mocks/MockERC721NonBurnable.sol b/src/test/mocks/MockERC721NonBurnable.sol new file mode 100644 index 000000000..4668d2e75 --- /dev/null +++ b/src/test/mocks/MockERC721NonBurnable.sol @@ -0,0 +1,24 @@ +// 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/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol new file mode 100644 index 000000000..1ed885681 --- /dev/null +++ b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.t.sol @@ -0,0 +1,61 @@ +// 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 new file mode 100644 index 000000000..078bd6a79 --- /dev/null +++ b/src/test/open-edition/_beforeTokenTransfers/_beforeTokenTransfers.tree @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..6903bdae7 --- /dev/null +++ b/src/test/open-edition/_canSetFunctions/_canSetFunctions.t.sol @@ -0,0 +1,128 @@ +// 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 new file mode 100644 index 000000000..1ccb478fd --- /dev/null +++ b/src/test/open-edition/_canSetFunctions/_canSetFunctions.tree @@ -0,0 +1,39 @@ +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 new file mode 100644 index 000000000..5b49bcdd7 --- /dev/null +++ b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.t.sol @@ -0,0 +1,203 @@ +// 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 new file mode 100644 index 000000000..2054cf049 --- /dev/null +++ b/src/test/open-edition/_collectPriceOnClaim/_collectPriceOnClaim.tree @@ -0,0 +1,37 @@ +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 new file mode 100644 index 000000000..d0d2f2173 --- /dev/null +++ b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.t.sol @@ -0,0 +1,96 @@ +// 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 new file mode 100644 index 000000000..bddcf87f6 --- /dev/null +++ b/src/test/open-edition/_transferTokensOnClaim/_transferTokensOnClaim.tree @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..270118999 --- /dev/null +++ b/src/test/open-edition/initialize/initialize.t.sol @@ -0,0 +1,233 @@ +// 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 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("Exceeds max bps"); + 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 new file mode 100644 index 000000000..f56ad144b --- /dev/null +++ b/src/test/open-edition/initialize/initialize.tree @@ -0,0 +1,39 @@ +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 new file mode 100644 index 000000000..44e6b20cb --- /dev/null +++ b/src/test/open-edition/misc/misc.t.sol @@ -0,0 +1,229 @@ +// 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 new file mode 100644 index 000000000..07abb950c --- /dev/null +++ b/src/test/open-edition/misc/misc.tree @@ -0,0 +1,33 @@ +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 index e418e9b7a..94d5a8060 100644 --- a/src/test/pack/Pack.t.sol +++ b/src/test/pack/Pack.t.sol @@ -475,7 +475,7 @@ contract PackTest is BaseTest { address recipient = address(0x123); vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC721: caller is not token owner nor approved"); + vm.expectRevert("ERC721: caller is not token owner or approved"); pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); } @@ -514,7 +514,7 @@ contract PackTest is BaseTest { address recipient = address(0x123); vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC721: caller is not token owner nor approved"); + vm.expectRevert("ERC721: caller is not token owner or approved"); pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); } @@ -527,7 +527,7 @@ contract PackTest is BaseTest { address recipient = address(0x123); vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC1155: caller is not token owner nor approved"); + vm.expectRevert("ERC1155: caller is not token owner or approved"); pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); } @@ -1069,10 +1069,9 @@ contract PackTest is BaseTest { uint256 internal constant MAX_TOKENS = 2000; - function getTokensToPack(uint256 len) - internal - returns (ITokenBundle.Token[] memory tokensToPack, uint256[] memory rewardUnits) - { + 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); @@ -1125,15 +1124,13 @@ contract PackTest is BaseTest { } } - function checkBalances(ITokenBundle.Token[] memory rewardUnits, address) + function checkBalances( + ITokenBundle.Token[] memory rewardUnits, + address + ) internal pure - returns ( - uint256 nativeTokenAmount, - uint256 erc20Amount, - uint256[] memory erc1155Amounts, - uint256 erc721Amount - ) + returns (uint256 nativeTokenAmount, uint256 erc20Amount, uint256[] memory erc1155Amounts, uint256 erc721Amount) { erc1155Amounts = new uint256[](MAX_TOKENS); @@ -1251,11 +1248,7 @@ contract MaliciousERC20 is MockERC20, ITokenBundle { pack = Pack(_pack); } - function transferFrom( - address from, - address to, - uint256 amount - ) public override returns (bool) { + 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); diff --git a/src/test/pack/PackVRFDirect.t.sol b/src/test/pack/PackVRFDirect.t.sol index 178ae2c8c..305378252 100644 --- a/src/test/pack/PackVRFDirect.t.sol +++ b/src/test/pack/PackVRFDirect.t.sol @@ -391,7 +391,7 @@ contract PackVRFDirectTest is BaseTest { address recipient = address(0x123); vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC721: caller is not token owner nor approved"); + vm.expectRevert("ERC721: caller is not token owner or approved"); pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); } @@ -430,7 +430,7 @@ contract PackVRFDirectTest is BaseTest { address recipient = address(0x123); vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC721: caller is not token owner nor approved"); + vm.expectRevert("ERC721: caller is not token owner or approved"); pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); } @@ -443,7 +443,7 @@ contract PackVRFDirectTest is BaseTest { address recipient = address(0x123); vm.startPrank(address(tokenOwner)); - vm.expectRevert("ERC1155: caller is not token owner nor approved"); + vm.expectRevert("ERC1155: caller is not token owner or approved"); pack.createPack(packContents, numOfRewardUnits, packUri, 0, 1, recipient); } @@ -865,10 +865,9 @@ contract PackVRFDirectTest is BaseTest { uint256 internal constant MAX_TOKENS = 2000; - function getTokensToPack(uint256 len) - internal - returns (ITokenBundle.Token[] memory tokensToPack, uint256[] memory rewardUnits) - { + 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); @@ -921,15 +920,13 @@ contract PackVRFDirectTest is BaseTest { } } - function checkBalances(ITokenBundle.Token[] memory rewardUnits, address) + function checkBalances( + ITokenBundle.Token[] memory rewardUnits, + address + ) internal pure - returns ( - uint256 nativeTokenAmount, - uint256 erc20Amount, - uint256[] memory erc1155Amounts, - uint256 erc721Amount - ) + returns (uint256 nativeTokenAmount, uint256 erc20Amount, uint256[] memory erc1155Amounts, uint256 erc721Amount) { erc1155Amounts = new uint256[](MAX_TOKENS); @@ -1047,11 +1044,7 @@ contract MaliciousERC20 is MockERC20, ITokenBundle { pack = Pack(_pack); } - function transferFrom( - address from, - address to, - uint256 amount - ) public override returns (bool) { + 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); diff --git a/src/test/plugin/Map.t.sol b/src/test/plugin/Map.t.sol index 30ada8330..1888b9841 100644 --- a/src/test/plugin/Map.t.sol +++ b/src/test/plugin/Map.t.sol @@ -1,12 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.0; -import "contracts/extension/plugin/PluginMap.sol"; -import { BaseTest } from "../utils/BaseTest.sol"; -import "contracts/lib/TWStrings.sol"; +import { PluginMap, IPluginMap } from "contracts/extension/plugin/PluginMap.sol"; +import "../utils/BaseTest.sol"; contract MapTest is BaseTest { - using TWStrings for uint256; + using Strings for uint256; PluginMap internal map; address[] private pluginAddresses; diff --git a/src/test/scripts/generateRoot.ts b/src/test/scripts/generateRoot.ts index 9fbcbf8d4..128499938 100644 --- a/src/test/scripts/generateRoot.ts +++ b/src/test/scripts/generateRoot.ts @@ -1,4 +1,5 @@ -const { MerkleTree } = require("merkletreejs"); +const { MerkleTree } = require("@thirdweb-dev/merkletree"); + const keccak256 = require("keccak256"); const { ethers } = require("ethers"); @@ -6,8 +7,8 @@ const process = require("process"); const members = [ "0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3", - "0xD0d82c095d184e6E2c8B72689c9171DE59FFd28d", - "0xFD78F7E2dF2B8c3D5bff0413c96f3237500898B3", + "0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd", + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ]; let val = process.argv[2]; @@ -15,7 +16,10 @@ 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]), + ethers.utils.solidityKeccak256( + ["address", "uint256", "uint256", "address"], + [l, val, price, currency], + ), ); const tree = new MerkleTree(hashedLeafs, keccak256, { diff --git a/src/test/scripts/generateRootAirdrop.ts b/src/test/scripts/generateRootAirdrop.ts new file mode 100644 index 000000000..4e2c1fdfb --- /dev/null +++ b/src/test/scripts/generateRootAirdrop.ts @@ -0,0 +1,26 @@ +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/getCloneAddress.ts b/src/test/scripts/getCloneAddress.ts index d2e1255a2..27ade61fd 100644 --- a/src/test/scripts/getCloneAddress.ts +++ b/src/test/scripts/getCloneAddress.ts @@ -1,4 +1,5 @@ -const { MerkleTree } = require("merkletreejs"); +const { MerkleTree } = require("@thirdweb-dev/merkletree"); + const keccak256 = require("keccak256"); const { ethers } = require("ethers"); diff --git a/src/test/scripts/getProof.ts b/src/test/scripts/getProof.ts index b1e41d4fd..2ef4fd4c5 100644 --- a/src/test/scripts/getProof.ts +++ b/src/test/scripts/getProof.ts @@ -1,4 +1,5 @@ -const { MerkleTree } = require("merkletreejs"); +const { MerkleTree } = require("@thirdweb-dev/merkletree"); + const keccak256 = require("keccak256"); const { ethers } = require("ethers"); @@ -6,8 +7,8 @@ const process = require("process"); const members = [ "0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3", - "0xD0d82c095d184e6E2c8B72689c9171DE59FFd28d", - "0xFD78F7E2dF2B8c3D5bff0413c96f3237500898B3", + "0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd", + "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ]; let val = process.argv[2]; @@ -15,7 +16,10 @@ 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]), + ethers.utils.solidityKeccak256( + ["address", "uint256", "uint256", "address"], + [l, val, price, currency], + ), ); const tree = new MerkleTree(hashedLeafs, keccak256, { @@ -25,7 +29,10 @@ const tree = new MerkleTree(hashedLeafs, keccak256, { }); const expectedProof = tree.getHexProof( - ethers.utils.solidityKeccak256(["address", "uint256", "uint256", "address"], [members[0], val, price, currency]), + 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 new file mode 100644 index 000000000..f99582602 --- /dev/null +++ b/src/test/scripts/getProofAirdrop.ts @@ -0,0 +1,30 @@ +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/sdk/base/BaseUtilTest.sol b/src/test/sdk/base/BaseUtilTest.sol index d2f755ec6..7ae0b458d 100644 --- a/src/test/sdk/base/BaseUtilTest.sol +++ b/src/test/sdk/base/BaseUtilTest.sol @@ -9,7 +9,6 @@ import "../../mocks/MockERC20.sol"; import "../../mocks/MockERC721.sol"; import "../../mocks/MockERC1155.sol"; import "contracts/infra/forwarder/Forwarder.sol"; -import "contracts/lib/TWStrings.sol"; abstract contract BaseUtilTest is DSTest, Test { string public constant NAME = "NAME"; @@ -55,22 +54,14 @@ abstract contract BaseUtilTest is DSTest, Test { wallet = new Wallet(); } - function assertIsOwnerERC721( - address _token, - address _owner, - uint256[] memory _tokenIds - ) internal { + 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 { + 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); @@ -103,19 +94,11 @@ abstract contract BaseUtilTest is DSTest, Test { } } - function assertBalERC20Eq( - address _token, - address _owner, - uint256 _amount - ) internal { + function assertBalERC20Eq(address _token, address _owner, uint256 _amount) internal { assertEq(MockERC20(_token).balanceOf(_owner), _amount); } - function assertBalERC20Gte( - address _token, - address _owner, - uint256 _amount - ) internal { + function assertBalERC20Gte(address _token, address _owner, uint256 _amount) internal { assertTrue(MockERC20(_token).balanceOf(_owner) >= _amount); } diff --git a/src/test/sdk/base/ERC1155Base.t.sol b/src/test/sdk/base/ERC1155Base.t.sol index c42425292..f6976a3da 100644 --- a/src/test/sdk/base/ERC1155Base.t.sol +++ b/src/test/sdk/base/ERC1155Base.t.sol @@ -5,11 +5,10 @@ import "@std/Test.sol"; import "@ds-test/test.sol"; import { ERC1155Base } from "contracts/base/ERC1155Base.sol"; - -import "contracts/lib/TWStrings.sol"; +import { Strings } from "contracts/lib/Strings.sol"; contract ERC1155BaseTest is DSTest, Test { - using TWStrings for uint256; + using Strings for uint256; // Target contract ERC1155Base internal base; diff --git a/src/test/sdk/base/ERC1155DelayedReveal.t.sol b/src/test/sdk/base/ERC1155DelayedReveal.t.sol index a05ed8e17..ffc0e1a06 100644 --- a/src/test/sdk/base/ERC1155DelayedReveal.t.sol +++ b/src/test/sdk/base/ERC1155DelayedReveal.t.sol @@ -5,11 +5,10 @@ import "@std/Test.sol"; import "@ds-test/test.sol"; import { ERC1155DelayedReveal } from "contracts/base/ERC1155DelayedReveal.sol"; - -import "contracts/lib/TWStrings.sol"; +import { Strings } from "contracts/lib/Strings.sol"; contract ERC1155DelayedRevealTest is DSTest, Test { - using TWStrings for uint256; + using Strings for uint256; // Target contract ERC1155DelayedReveal internal base; diff --git a/src/test/sdk/base/ERC1155Drop.t.sol b/src/test/sdk/base/ERC1155Drop.t.sol index ee1e04b3d..580fe3b77 100644 --- a/src/test/sdk/base/ERC1155Drop.t.sol +++ b/src/test/sdk/base/ERC1155Drop.t.sol @@ -5,11 +5,10 @@ import "@std/Test.sol"; import "@ds-test/test.sol"; import { ERC1155Drop } from "contracts/base/ERC1155Drop.sol"; - -import "contracts/lib/TWStrings.sol"; +import { Strings } from "contracts/lib/Strings.sol"; contract ERC1155DropTest is DSTest, Test { - using TWStrings for uint256; + using Strings for uint256; // Target contract ERC1155Drop internal base; @@ -230,7 +229,7 @@ contract ERC1155DropTest is DSTest, Test { result = vm.ffi(inputs); bytes32[] memory proofs = abi.decode(result, (bytes32[])); - address claimer = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); + address claimer = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); condition.startTimestamp = block.timestamp; condition.maxClaimableSupply = 100; @@ -288,7 +287,7 @@ contract ERC1155DropTest is DSTest, Test { result = vm.ffi(inputs); bytes32[] memory proofs = abi.decode(result, (bytes32[])); - address claimer = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); + address claimer = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); condition.startTimestamp = block.timestamp; condition.maxClaimableSupply = 100; diff --git a/src/test/sdk/base/ERC1155LazyMint.t.sol b/src/test/sdk/base/ERC1155LazyMint.t.sol index a6089b684..3d83c1b94 100644 --- a/src/test/sdk/base/ERC1155LazyMint.t.sol +++ b/src/test/sdk/base/ERC1155LazyMint.t.sol @@ -5,11 +5,10 @@ import "@std/Test.sol"; import "@ds-test/test.sol"; import { ERC1155LazyMint } from "contracts/base/ERC1155LazyMint.sol"; - -import "contracts/lib/TWStrings.sol"; +import { Strings } from "contracts/lib/Strings.sol"; contract ERC1155LazyMintTest is DSTest, Test { - using TWStrings for uint256; + using Strings for uint256; // Target contract ERC1155LazyMint internal base; diff --git a/src/test/sdk/base/ERC1155SignatureMint.t.sol b/src/test/sdk/base/ERC1155SignatureMint.t.sol index 6ca3e8a34..77433ba98 100644 --- a/src/test/sdk/base/ERC1155SignatureMint.t.sol +++ b/src/test/sdk/base/ERC1155SignatureMint.t.sol @@ -5,11 +5,10 @@ import "@std/Test.sol"; import "@ds-test/test.sol"; import { ERC1155SignatureMint } from "contracts/base/ERC1155SignatureMint.sol"; - -import "contracts/lib/TWStrings.sol"; +import { Strings } from "contracts/lib/Strings.sol"; contract ERC1155SignatureMintTest is DSTest, Test { - using TWStrings for uint256; + using Strings for uint256; // Target contract ERC1155SignatureMint internal base; @@ -30,11 +29,10 @@ contract ERC1155SignatureMintTest is DSTest, Test { ERC1155SignatureMint.MintRequest req; - function signMintRequest(ERC1155SignatureMint.MintRequest memory _request, uint256 privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + ERC1155SignatureMint.MintRequest memory _request, + uint256 privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = bytes.concat( abi.encode( typehashMintRequest, diff --git a/src/test/sdk/base/ERC20Base.t.sol b/src/test/sdk/base/ERC20Base.t.sol index 18eb1d829..ec8131cfd 100644 --- a/src/test/sdk/base/ERC20Base.t.sol +++ b/src/test/sdk/base/ERC20Base.t.sol @@ -9,7 +9,7 @@ import { ERC20Base } from "contracts/base/ERC20Base.sol"; contract BaseERC20BaseTest is BaseUtilTest { ERC20Base internal base; - using TWStrings for uint256; + using Strings for uint256; bytes32 internal permitTypeHash; bytes32 internal permitNameHash; diff --git a/src/test/sdk/base/ERC20Drop.t.sol b/src/test/sdk/base/ERC20Drop.t.sol index 5115d8f50..cb4457298 100644 --- a/src/test/sdk/base/ERC20Drop.t.sol +++ b/src/test/sdk/base/ERC20Drop.t.sol @@ -9,7 +9,7 @@ import { ERC20Drop } from "contracts/base/ERC20Drop.sol"; contract BaseERC20DropTest is BaseUtilTest { ERC20Drop internal base; - using TWStrings for uint256; + using Strings for uint256; bytes32 internal typehashEip712; bytes32 internal domainSeparator; diff --git a/src/test/sdk/base/ERC20DropVote.t.sol b/src/test/sdk/base/ERC20DropVote.t.sol index 7bd1e73ff..fd32af1de 100644 --- a/src/test/sdk/base/ERC20DropVote.t.sol +++ b/src/test/sdk/base/ERC20DropVote.t.sol @@ -9,7 +9,7 @@ import { ERC20DropVote } from "contracts/base/ERC20DropVote.sol"; contract BaseERC20DropVoteTest is BaseUtilTest { ERC20DropVote internal base; - using TWStrings for uint256; + using Strings for uint256; bytes32 internal typehashEip712; bytes32 internal domainSeparator; diff --git a/src/test/sdk/base/ERC20SignatureMint.t.sol b/src/test/sdk/base/ERC20SignatureMint.t.sol index ab5cc9dca..9c4560868 100644 --- a/src/test/sdk/base/ERC20SignatureMint.t.sol +++ b/src/test/sdk/base/ERC20SignatureMint.t.sol @@ -9,7 +9,7 @@ import { ERC20SignatureMint } from "contracts/base/ERC20SignatureMint.sol"; contract BaseERC20SignatureMintTest is BaseUtilTest { ERC20SignatureMint internal base; - using TWStrings for uint256; + using Strings for uint256; bytes32 internal typehashMintRequest; bytes32 internal nameHash; @@ -53,11 +53,10 @@ contract BaseERC20SignatureMintTest is BaseUtilTest { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(ERC20SignatureMint.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + ERC20SignatureMint.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashMintRequest, _request.to, diff --git a/src/test/sdk/base/ERC20SignatureMintVote.t.sol b/src/test/sdk/base/ERC20SignatureMintVote.t.sol index a919974d4..f705aba8e 100644 --- a/src/test/sdk/base/ERC20SignatureMintVote.t.sol +++ b/src/test/sdk/base/ERC20SignatureMintVote.t.sol @@ -9,7 +9,7 @@ import { ERC20SignatureMintVote } from "contracts/base/ERC20SignatureMintVote.so contract BaseERC20SignatureMintVoteTest is BaseUtilTest { ERC20SignatureMintVote internal base; - using TWStrings for uint256; + using Strings for uint256; bytes32 internal typehashMintRequest; bytes32 internal nameHash; @@ -53,11 +53,10 @@ contract BaseERC20SignatureMintVoteTest is BaseUtilTest { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(ERC20SignatureMintVote.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + ERC20SignatureMintVote.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashMintRequest, _request.to, diff --git a/src/test/sdk/base/ERC20Vote.t.sol b/src/test/sdk/base/ERC20Vote.t.sol index d4fa83280..ca57c1c76 100644 --- a/src/test/sdk/base/ERC20Vote.t.sol +++ b/src/test/sdk/base/ERC20Vote.t.sol @@ -9,7 +9,7 @@ import { ERC20Vote } from "contracts/base/ERC20Vote.sol"; contract BaseERC20VoteTest is BaseUtilTest { ERC20Vote internal base; - using TWStrings for uint256; + using Strings for uint256; bytes32 internal permitTypeHash; bytes32 internal delegationTypeHash; diff --git a/src/test/sdk/base/ERC721Base.t.sol b/src/test/sdk/base/ERC721Base.t.sol index 85165bc22..7eaf4b2d2 100644 --- a/src/test/sdk/base/ERC721Base.t.sol +++ b/src/test/sdk/base/ERC721Base.t.sol @@ -9,7 +9,7 @@ import { ERC721Base } from "contracts/base/ERC721Base.sol"; contract BaseERC721BaseTest is BaseUtilTest { ERC721Base internal base; - using TWStrings for uint256; + using Strings for uint256; function setUp() public override { vm.prank(deployer); diff --git a/src/test/sdk/base/ERC721DelayedReveal.t.sol b/src/test/sdk/base/ERC721DelayedReveal.t.sol index f21c8b449..89a901357 100644 --- a/src/test/sdk/base/ERC721DelayedReveal.t.sol +++ b/src/test/sdk/base/ERC721DelayedReveal.t.sol @@ -9,7 +9,7 @@ import { ERC721DelayedReveal } from "contracts/base/ERC721DelayedReveal.sol"; contract BaseERC721DelayedRevealTest is BaseUtilTest { ERC721DelayedReveal internal base; - using TWStrings for uint256; + using Strings for uint256; function setUp() public override { vm.prank(deployer); diff --git a/src/test/sdk/base/ERC721Drop.t.sol b/src/test/sdk/base/ERC721Drop.t.sol index d4010e8bd..663485705 100644 --- a/src/test/sdk/base/ERC721Drop.t.sol +++ b/src/test/sdk/base/ERC721Drop.t.sol @@ -9,7 +9,7 @@ import { ERC721Drop } from "contracts/base/ERC721Drop.sol"; contract BaseERC721DropTest is BaseUtilTest { ERC721Drop internal base; - using TWStrings for uint256; + using Strings for uint256; address recipient; diff --git a/src/test/sdk/base/ERC721LazyMint.t.sol b/src/test/sdk/base/ERC721LazyMint.t.sol index 5a10e5a21..241d82116 100644 --- a/src/test/sdk/base/ERC721LazyMint.t.sol +++ b/src/test/sdk/base/ERC721LazyMint.t.sol @@ -9,7 +9,7 @@ import { ERC721LazyMint } from "contracts/base/ERC721LazyMint.sol"; contract BaseERC721LazyMintTest is BaseUtilTest { ERC721LazyMint internal base; - using TWStrings for uint256; + using Strings for uint256; uint256 _amount; string _baseURIForTokens; diff --git a/src/test/sdk/base/ERC721Multiwrap.t.sol b/src/test/sdk/base/ERC721Multiwrap.t.sol index 1b5e0fc30..30e718d23 100644 --- a/src/test/sdk/base/ERC721Multiwrap.t.sol +++ b/src/test/sdk/base/ERC721Multiwrap.t.sol @@ -11,7 +11,7 @@ import { ITokenBundle } from "contracts/extension/interface/ITokenBundle.sol"; contract BaseERC721MultiwrapTest is BaseUtilTest { ERC721Multiwrap internal base; - using TWStrings for uint256; + using Strings for uint256; Wallet internal tokenOwner; string internal uriForWrappedToken; diff --git a/src/test/sdk/base/ERC721SignatureMint.t.sol b/src/test/sdk/base/ERC721SignatureMint.t.sol index fd09a469d..0dad51e65 100644 --- a/src/test/sdk/base/ERC721SignatureMint.t.sol +++ b/src/test/sdk/base/ERC721SignatureMint.t.sol @@ -9,7 +9,7 @@ import { ERC721SignatureMint } from "contracts/base/ERC721SignatureMint.sol"; contract BaseERC721SignatureMintTest is BaseUtilTest { ERC721SignatureMint internal base; - using TWStrings for uint256; + using Strings for uint256; bytes32 internal typehashMintRequest; bytes32 internal nameHash; @@ -56,11 +56,10 @@ contract BaseERC721SignatureMintTest is BaseUtilTest { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(ERC721SignatureMint.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + ERC721SignatureMint.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashMintRequest, _request.to, diff --git a/src/test/sdk/extension/DropSinglePhase.t.sol b/src/test/sdk/extension/DropSinglePhase.t.sol index f65daebdf..af22ee551 100644 --- a/src/test/sdk/extension/DropSinglePhase.t.sol +++ b/src/test/sdk/extension/DropSinglePhase.t.sol @@ -3,9 +3,9 @@ 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"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract MyDropSinglePhase is DropSinglePhase { bool condition; @@ -25,11 +25,10 @@ contract MyDropSinglePhase is DropSinglePhase { uint256 _pricePerToken ) internal override {} - function _transferTokensOnClaim(address _to, uint256 _quantityBeingClaimed) - internal - override - returns (uint256 startTokenId) - {} + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal override returns (uint256 startTokenId) {} } contract ExtensionDropSinglePhase is DSTest, Test { @@ -143,7 +142,7 @@ contract ExtensionDropSinglePhase is DSTest, Test { vm.warp(1); - address receiver = address(0x92Bb439374a091c7507bE100183d8D1Ed2c9dAD3); + address receiver = address(0xDDdDddDdDdddDDddDDddDDDDdDdDDdDDdDDDDDDd); // bytes32[] memory proofs = new bytes32[](0); diff --git a/src/test/sdk/extension/DropSinglePhase1155.t.sol b/src/test/sdk/extension/DropSinglePhase1155.t.sol index ee851ed28..dc6e02689 100644 --- a/src/test/sdk/extension/DropSinglePhase1155.t.sol +++ b/src/test/sdk/extension/DropSinglePhase1155.t.sol @@ -24,11 +24,7 @@ contract MyDropSinglePhase1155 is DropSinglePhase1155 { uint256 _pricePerToken ) internal override {} - function _transferTokensOnClaim( - address _to, - uint256 _tokenId, - uint256 _quantityBeingClaimed - ) internal override {} + function _transferTokensOnClaim(address _to, uint256 _tokenId, uint256 _quantityBeingClaimed) internal override {} } contract ExtensionDropSinglePhase1155 is DSTest, Test { diff --git a/src/test/sdk/extension/ExtensionUtilTest.sol b/src/test/sdk/extension/ExtensionUtilTest.sol new file mode 100644 index 000000000..694ab6946 --- /dev/null +++ b/src/test/sdk/extension/ExtensionUtilTest.sol @@ -0,0 +1,116 @@ +// 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/Permissions.t.sol b/src/test/sdk/extension/Permissions.t.sol index b77681960..ec4adcab2 100644 --- a/src/test/sdk/extension/Permissions.t.sol +++ b/src/test/sdk/extension/Permissions.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.11; import "@std/Test.sol"; import "@ds-test/test.sol"; -import { Permissions, TWStrings } from "contracts/extension/Permissions.sol"; +import { Permissions, Strings } from "contracts/extension/Permissions.sol"; contract MyPermissions is Permissions { constructor() { @@ -94,9 +94,9 @@ contract ExtensionPermissions is DSTest, Test { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(caller), 20), + Strings.toHexString(uint160(caller), 20), " is missing role ", - TWStrings.toHexString(uint256(ext.DEFAULT_ADMIN_ROLE()), 32) + Strings.toHexString(uint256(ext.DEFAULT_ADMIN_ROLE()), 32) ) ); ext.grantRole(keccak256("role"), address(0x1)); @@ -141,9 +141,9 @@ contract ExtensionPermissions is DSTest, Test { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(address(0x345)), 20), + Strings.toHexString(uint160(address(0x345)), 20), " is missing role ", - TWStrings.toHexString(uint256(ext.DEFAULT_ADMIN_ROLE()), 32) + Strings.toHexString(uint256(ext.DEFAULT_ADMIN_ROLE()), 32) ) ); ext.revokeRole(keccak256("role"), address(0x567)); @@ -153,9 +153,9 @@ contract ExtensionPermissions is DSTest, Test { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(address(0x789)), 20), + Strings.toHexString(uint160(address(0x789)), 20), " is missing role ", - TWStrings.toHexString(uint256(keccak256("role")), 32) + Strings.toHexString(uint256(keccak256("role")), 32) ) ); ext.revokeRole(keccak256("role"), address(0x789)); @@ -192,9 +192,9 @@ contract ExtensionPermissions is DSTest, Test { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(defaultAdmin), 20), + Strings.toHexString(uint160(defaultAdmin), 20), " is missing role ", - TWStrings.toHexString(uint256(keccak256("role")), 32) + Strings.toHexString(uint256(keccak256("role")), 32) ) ); ext.renounceRole(keccak256("role"), defaultAdmin); @@ -220,9 +220,9 @@ contract ExtensionPermissions is DSTest, Test { vm.expectRevert( abi.encodePacked( "Permissions: account ", - TWStrings.toHexString(uint160(address(0x345)), 20), + Strings.toHexString(uint160(address(0x345)), 20), " is missing role ", - TWStrings.toHexString(uint256(ext.DEFAULT_ADMIN_ROLE()), 32) + Strings.toHexString(uint256(ext.DEFAULT_ADMIN_ROLE()), 32) ) ); ext.checkModifier(); diff --git a/src/test/sdk/extension/PermissionsEnumerable.t.sol b/src/test/sdk/extension/PermissionsEnumerable.t.sol index b724c3efc..5a20187d4 100644 --- a/src/test/sdk/extension/PermissionsEnumerable.t.sol +++ b/src/test/sdk/extension/PermissionsEnumerable.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.11; import "@std/Test.sol"; import "@ds-test/test.sol"; -import { PermissionsEnumerable, TWStrings } from "contracts/extension/PermissionsEnumerable.sol"; +import { PermissionsEnumerable, Strings } from "contracts/extension/PermissionsEnumerable.sol"; contract MyPermissionsEnumerable is PermissionsEnumerable { constructor() { diff --git a/src/test/sdk/extension/SignatureMintERC1155.t.sol b/src/test/sdk/extension/SignatureMintERC1155.t.sol index 081c7c3f2..9de73c188 100644 --- a/src/test/sdk/extension/SignatureMintERC1155.t.sol +++ b/src/test/sdk/extension/SignatureMintERC1155.t.sol @@ -17,11 +17,10 @@ contract MySigMint1155 is SignatureMintERC1155 { return condition; } - function mintWithSignature(MintRequest calldata req, bytes calldata signature) - external - payable - returns (address signer) - { + function mintWithSignature( + MintRequest calldata req, + bytes calldata signature + ) external payable returns (address signer) { if (!_canSignMintRequest(msg.sender)) { revert("not authorized"); } @@ -76,11 +75,10 @@ contract ExtensionSignatureMintERC1155 is DSTest, Test { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(MySigMint1155.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + MySigMint1155.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = bytes.concat( abi.encode( typehashMintRequest, diff --git a/src/test/sdk/extension/SignatureMintERC20.t.sol b/src/test/sdk/extension/SignatureMintERC20.t.sol index 058c7325a..452c28a29 100644 --- a/src/test/sdk/extension/SignatureMintERC20.t.sol +++ b/src/test/sdk/extension/SignatureMintERC20.t.sol @@ -17,11 +17,10 @@ contract MySigMint20 is SignatureMintERC20 { return condition; } - function mintWithSignature(MintRequest calldata req, bytes calldata signature) - external - payable - returns (address signer) - { + function mintWithSignature( + MintRequest calldata req, + bytes calldata signature + ) external payable returns (address signer) { if (!_canSignMintRequest(msg.sender)) { revert("not authorized"); } @@ -72,11 +71,10 @@ contract ExtensionSignatureMintERC20 is DSTest, Test { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(MySigMint20.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + MySigMint20.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashMintRequest, _request.to, diff --git a/src/test/sdk/extension/SignatureMintERC721.t.sol b/src/test/sdk/extension/SignatureMintERC721.t.sol index 767322d23..555c54115 100644 --- a/src/test/sdk/extension/SignatureMintERC721.t.sol +++ b/src/test/sdk/extension/SignatureMintERC721.t.sol @@ -17,11 +17,10 @@ contract MySigMint721 is SignatureMintERC721 { return condition; } - function mintWithSignature(MintRequest calldata req, bytes calldata signature) - external - payable - returns (address signer) - { + function mintWithSignature( + MintRequest calldata req, + bytes calldata signature + ) external payable returns (address signer) { if (!_canSignMintRequest(msg.sender)) { revert("not authorized"); } @@ -75,11 +74,10 @@ contract ExtensionSignatureMintERC721 is DSTest, Test { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(MySigMint721.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + MySigMint721.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashMintRequest, _request.to, diff --git a/src/test/sdk/extension/StakingExtension.t.sol b/src/test/sdk/extension/StakingExtension.t.sol index b7177782c..2805ba210 100644 --- a/src/test/sdk/extension/StakingExtension.t.sol +++ b/src/test/sdk/extension/StakingExtension.t.sol @@ -32,12 +32,7 @@ contract MyStakingContract is ERC20, Staking721, IERC721Receiver { ERC 165 / 721 logic //////////////////////////////////////////////////////////////*/ - function onERC721Received( - address, - address, - uint256, - bytes calldata - ) external view override returns (bytes4) { + function onERC721Received(address, address, uint256, bytes calldata) external view override returns (bytes4) { require(isStaking == 2, "Direct transfer"); return this.onERC721Received.selector; } diff --git a/src/test/sdk/extension/TokenBundle.t.sol b/src/test/sdk/extension/TokenBundle.t.sol index 793e1f145..a357f1b35 100644 --- a/src/test/sdk/extension/TokenBundle.t.sol +++ b/src/test/sdk/extension/TokenBundle.t.sol @@ -24,11 +24,7 @@ contract MyTokenBundle is TokenBundle { _addTokenInBundle(_tokenToBind, _bundleId); } - function updateTokenInBundle( - Token memory _tokenToBind, - uint256 _bundleId, - uint256 _index - ) external { + function updateTokenInBundle(Token memory _tokenToBind, uint256 _bundleId, uint256 _index) external { _updateTokenInBundle(_tokenToBind, _bundleId, _index); } 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 new file mode 100644 index 000000000..70a5b4493 --- /dev/null +++ b/src/test/sdk/extension/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.t.sol @@ -0,0 +1,52 @@ +// 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("Invalid index"); + 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 new file mode 100644 index 000000000..572dd5203 --- /dev/null +++ b/src/test/sdk/extension/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.tree @@ -0,0 +1,7 @@ +_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 new file mode 100644 index 000000000..ac839fbcd --- /dev/null +++ b/src/test/sdk/extension/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.t.sol @@ -0,0 +1,71 @@ +// 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("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/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.tree b/src/test/sdk/extension/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.tree new file mode 100644 index 000000000..4dd87edef --- /dev/null +++ b/src/test/sdk/extension/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.tree @@ -0,0 +1,6 @@ +_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 new file mode 100644 index 000000000..088adba26 --- /dev/null +++ b/src/test/sdk/extension/batch-mint-metadata/get-base-uri/_getBaseURI.t.sol @@ -0,0 +1,67 @@ +// 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("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/batch-mint-metadata/get-base-uri/_getBaseURI.tree b/src/test/sdk/extension/batch-mint-metadata/get-base-uri/_getBaseURI.tree new file mode 100644 index 000000000..c4ee674bf --- /dev/null +++ b/src/test/sdk/extension/batch-mint-metadata/get-base-uri/_getBaseURI.tree @@ -0,0 +1,6 @@ +_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 new file mode 100644 index 000000000..ffea5de91 --- /dev/null +++ b/src/test/sdk/extension/batch-mint-metadata/get-batch-id/_getBatchId.t.sol @@ -0,0 +1,65 @@ +// 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("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/batch-mint-metadata/get-batch-id/_getBatchId.tree b/src/test/sdk/extension/batch-mint-metadata/get-batch-id/_getBatchId.tree new file mode 100644 index 000000000..2e6dd366e --- /dev/null +++ b/src/test/sdk/extension/batch-mint-metadata/get-batch-id/_getBatchId.tree @@ -0,0 +1,6 @@ +_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 new file mode 100644 index 000000000..b3cfba004 --- /dev/null +++ b/src/test/sdk/extension/batch-mint-metadata/get-batch-start-id/_getBatchStartId.t.sol @@ -0,0 +1,62 @@ +// 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("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/batch-mint-metadata/get-batch-start-id/_getBatchStartId.tree b/src/test/sdk/extension/batch-mint-metadata/get-batch-start-id/_getBatchStartId.tree new file mode 100644 index 000000000..7e303ab46 --- /dev/null +++ b/src/test/sdk/extension/batch-mint-metadata/get-batch-start-id/_getBatchStartId.tree @@ -0,0 +1,6 @@ +_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 new file mode 100644 index 000000000..ee4baa4d6 --- /dev/null +++ b/src/test/sdk/extension/batch-mint-metadata/set-base-uri/_setBaseURI.t.sol @@ -0,0 +1,84 @@ +// 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("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] - 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 new file mode 100644 index 000000000..3df76f653 --- /dev/null +++ b/src/test/sdk/extension/batch-mint-metadata/set-base-uri/_setBaseURI.tree @@ -0,0 +1,6 @@ +_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 new file mode 100644 index 000000000..e01939b3b --- /dev/null +++ b/src/test/sdk/extension/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.t.sol @@ -0,0 +1,126 @@ +// 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 new file mode 100644 index 000000000..a2a3911ac --- /dev/null +++ b/src/test/sdk/extension/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.tree @@ -0,0 +1,15 @@ +_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 new file mode 100644 index 000000000..b4e721145 --- /dev/null +++ b/src/test/sdk/extension/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.t.sol @@ -0,0 +1,87 @@ +// 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 new file mode 100644 index 000000000..d6e347f5e --- /dev/null +++ b/src/test/sdk/extension/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.tree @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..b985d473d --- /dev/null +++ b/src/test/sdk/extension/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.t.sol @@ -0,0 +1,146 @@ +// 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 new file mode 100644 index 000000000..ffc4dba9d --- /dev/null +++ b/src/test/sdk/extension/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.tree @@ -0,0 +1,23 @@ +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 new file mode 100644 index 000000000..30b463c4d --- /dev/null +++ b/src/test/sdk/extension/contract-metadata/set-contract-uri/setContractURI.t.sol @@ -0,0 +1,65 @@ +// 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("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/contract-metadata/set-contract-uri/setContractURI.tree b/src/test/sdk/extension/contract-metadata/set-contract-uri/setContractURI.tree new file mode 100644 index 000000000..e626d76e4 --- /dev/null +++ b/src/test/sdk/extension/contract-metadata/set-contract-uri/setContractURI.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..ad9e08828 --- /dev/null +++ b/src/test/sdk/extension/delayed-reveal/get-reveal-uri/getRevealURI.t.sol @@ -0,0 +1,66 @@ +// 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("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/delayed-reveal/get-reveal-uri/getRevealURI.tree b/src/test/sdk/extension/delayed-reveal/get-reveal-uri/getRevealURI.tree new file mode 100644 index 000000000..acb580468 --- /dev/null +++ b/src/test/sdk/extension/delayed-reveal/get-reveal-uri/getRevealURI.tree @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..096e33568 --- /dev/null +++ b/src/test/sdk/extension/delayed-reveal/set-encrypted-data/_setEncryptedData.t.sol @@ -0,0 +1,37 @@ +// 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 new file mode 100644 index 000000000..68f99a2c8 --- /dev/null +++ b/src/test/sdk/extension/delayed-reveal/set-encrypted-data/_setEncryptedData.tree @@ -0,0 +1,3 @@ +_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 new file mode 100644 index 000000000..c1228add8 --- /dev/null +++ b/src/test/sdk/extension/drop/claim/claim.t.sol @@ -0,0 +1,106 @@ +// 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("!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/drop/claim/claim.tree b/src/test/sdk/extension/drop/claim/claim.tree new file mode 100644 index 000000000..4ca1d3187 --- /dev/null +++ b/src/test/sdk/extension/drop/claim/claim.tree @@ -0,0 +1,15 @@ +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 new file mode 100644 index 000000000..833e4f4fd --- /dev/null +++ b/src/test/sdk/extension/drop/get-active-claim-condition-id/getActiveClaimConditionId.t.sol @@ -0,0 +1,117 @@ +// 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 new file mode 100644 index 000000000..8b8a94d99 --- /dev/null +++ b/src/test/sdk/extension/drop/get-active-claim-condition-id/getActiveClaimConditionId.tree @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..9cd4ca78b --- /dev/null +++ b/src/test/sdk/extension/drop/set-claim-conditions/setClaimConditions.t.sol @@ -0,0 +1,379 @@ +// 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("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/drop/set-claim-conditions/setClaimConditions.tree b/src/test/sdk/extension/drop/set-claim-conditions/setClaimConditions.tree new file mode 100644 index 000000000..dbf6297d3 --- /dev/null +++ b/src/test/sdk/extension/drop/set-claim-conditions/setClaimConditions.tree @@ -0,0 +1,24 @@ +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 new file mode 100644 index 000000000..60d1d03be --- /dev/null +++ b/src/test/sdk/extension/drop/verify-claim/verifyClaim.t.sol @@ -0,0 +1,379 @@ +// 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("!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/drop/verify-claim/verifyClaim.tree b/src/test/sdk/extension/drop/verify-claim/verifyClaim.tree new file mode 100644 index 000000000..64553ab90 --- /dev/null +++ b/src/test/sdk/extension/drop/verify-claim/verifyClaim.tree @@ -0,0 +1,67 @@ +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 new file mode 100644 index 000000000..135980bad --- /dev/null +++ b/src/test/sdk/extension/lazy-mint/lazy-mint/lazyMint.t.sol @@ -0,0 +1,119 @@ +// 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("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/lazy-mint/lazy-mint/lazyMint.tree b/src/test/sdk/extension/lazy-mint/lazy-mint/lazyMint.tree new file mode 100644 index 000000000..72ac4ddb3 --- /dev/null +++ b/src/test/sdk/extension/lazy-mint/lazy-mint/lazyMint.tree @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000..328c53d2a --- /dev/null +++ b/src/test/sdk/extension/ownable/set-owner/setOwner.t.sol @@ -0,0 +1,72 @@ +// 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("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/ownable/set-owner/setOwner.tree b/src/test/sdk/extension/ownable/set-owner/setOwner.tree new file mode 100644 index 000000000..9db2c0a70 --- /dev/null +++ b/src/test/sdk/extension/ownable/set-owner/setOwner.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..a218d1125 --- /dev/null +++ b/src/test/sdk/extension/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol @@ -0,0 +1,94 @@ +// 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("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/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.tree b/src/test/sdk/extension/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.tree new file mode 100644 index 000000000..78a4312de --- /dev/null +++ b/src/test/sdk/extension/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.tree @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..e167be3db --- /dev/null +++ b/src/test/sdk/extension/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol @@ -0,0 +1,108 @@ +// 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("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/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.tree b/src/test/sdk/extension/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.tree new file mode 100644 index 000000000..e28295634 --- /dev/null +++ b/src/test/sdk/extension/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.tree @@ -0,0 +1,15 @@ +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 new file mode 100644 index 000000000..6ca105a6f --- /dev/null +++ b/src/test/sdk/extension/upgradeable/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.t.sol @@ -0,0 +1,52 @@ +// 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 new file mode 100644 index 000000000..572dd5203 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/batch-mint-metadata/batch-mint-metadata/_batchMintMetadata.tree @@ -0,0 +1,7 @@ +_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 new file mode 100644 index 000000000..9bc777bf4 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.t.sol @@ -0,0 +1,75 @@ +// 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 new file mode 100644 index 000000000..4dd87edef --- /dev/null +++ b/src/test/sdk/extension/upgradeable/batch-mint-metadata/freeze-base-uri/_freezeBaseURI.tree @@ -0,0 +1,6 @@ +_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 new file mode 100644 index 000000000..153915408 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-base-uri/_getBaseURI.t.sol @@ -0,0 +1,67 @@ +// 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 new file mode 100644 index 000000000..c4ee674bf --- /dev/null +++ b/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-base-uri/_getBaseURI.tree @@ -0,0 +1,6 @@ +_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 new file mode 100644 index 000000000..9c8ec1136 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-id/_getBatchId.t.sol @@ -0,0 +1,65 @@ +// 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 new file mode 100644 index 000000000..2e6dd366e --- /dev/null +++ b/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-id/_getBatchId.tree @@ -0,0 +1,6 @@ +_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 new file mode 100644 index 000000000..6a6182a3b --- /dev/null +++ b/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-start-id/_getBatchStartId.t.sol @@ -0,0 +1,62 @@ +// 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 new file mode 100644 index 000000000..7e303ab46 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/batch-mint-metadata/get-batch-start-id/_getBatchStartId.tree @@ -0,0 +1,6 @@ +_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 new file mode 100644 index 000000000..28a351d76 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/batch-mint-metadata/set-base-uri/_setBaseURI.t.sol @@ -0,0 +1,84 @@ +// 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 new file mode 100644 index 000000000..3df76f653 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/batch-mint-metadata/set-base-uri/_setBaseURI.tree @@ -0,0 +1,6 @@ +_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 new file mode 100644 index 000000000..bc530cd78 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.t.sol @@ -0,0 +1,126 @@ +// 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 new file mode 100644 index 000000000..a2a3911ac --- /dev/null +++ b/src/test/sdk/extension/upgradeable/burn-to-claim/burn-tokens-on-origin/_burnTokensOnOrigin.tree @@ -0,0 +1,15 @@ +_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 new file mode 100644 index 000000000..cb3cf0133 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.t.sol @@ -0,0 +1,87 @@ +// 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 new file mode 100644 index 000000000..d6e347f5e --- /dev/null +++ b/src/test/sdk/extension/upgradeable/burn-to-claim/set-burn-to-claim-info/setBurnToClaimInfo.tree @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..030ea0bb2 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.t.sol @@ -0,0 +1,146 @@ +// 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 new file mode 100644 index 000000000..ffc4dba9d --- /dev/null +++ b/src/test/sdk/extension/upgradeable/burn-to-claim/verify-burn-to-claim/verifyBurnToClaim.tree @@ -0,0 +1,23 @@ +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 new file mode 100644 index 000000000..0be6c9030 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/contract-metadata/set-contract-uri/setContractURI.t.sol @@ -0,0 +1,65 @@ +// 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 new file mode 100644 index 000000000..e626d76e4 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/contract-metadata/set-contract-uri/setContractURI.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..62a10b844 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/delayed-reveal/get-reveal-uri/getRevealURI.t.sol @@ -0,0 +1,66 @@ +// 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 new file mode 100644 index 000000000..acb580468 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/delayed-reveal/get-reveal-uri/getRevealURI.tree @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..a499bad5e --- /dev/null +++ b/src/test/sdk/extension/upgradeable/delayed-reveal/set-encrypted-data/_setEncryptedData.t.sol @@ -0,0 +1,37 @@ +// 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 new file mode 100644 index 000000000..68f99a2c8 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/delayed-reveal/set-encrypted-data/_setEncryptedData.tree @@ -0,0 +1,3 @@ +_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 new file mode 100644 index 000000000..ca65c8830 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/drop/claim/claim.t.sol @@ -0,0 +1,106 @@ +// 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 new file mode 100644 index 000000000..4ca1d3187 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/drop/claim/claim.tree @@ -0,0 +1,15 @@ +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 new file mode 100644 index 000000000..3fdd2dce2 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/drop/get-active-claim-condition-id/getActiveClaimConditionId.t.sol @@ -0,0 +1,117 @@ +// 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 new file mode 100644 index 000000000..8b8a94d99 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/drop/get-active-claim-condition-id/getActiveClaimConditionId.tree @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..cd96e3970 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/drop/set-claim-conditions/setClaimConditions.t.sol @@ -0,0 +1,379 @@ +// 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 new file mode 100644 index 000000000..aecd6b06f --- /dev/null +++ b/src/test/sdk/extension/upgradeable/drop/set-claim-conditions/setClaimConditions.tree @@ -0,0 +1,24 @@ +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 new file mode 100644 index 000000000..3cd3372a1 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.t.sol @@ -0,0 +1,379 @@ +// 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 new file mode 100644 index 000000000..ef84f4ef2 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/drop/verify-claim/verifyClaim.tree @@ -0,0 +1,67 @@ +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 new file mode 100644 index 000000000..97fec025d --- /dev/null +++ b/src/test/sdk/extension/upgradeable/lazy-mint/lazy-mint/lazyMint.t.sol @@ -0,0 +1,119 @@ +// 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 new file mode 100644 index 000000000..daf177146 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/lazy-mint/lazy-mint/lazyMint.tree @@ -0,0 +1,17 @@ +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 new file mode 100644 index 000000000..6c938158f --- /dev/null +++ b/src/test/sdk/extension/upgradeable/ownable/set-owner/setOwner.t.sol @@ -0,0 +1,72 @@ +// 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 new file mode 100644 index 000000000..9db2c0a70 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/ownable/set-owner/setOwner.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..e541be5ad --- /dev/null +++ b/src/test/sdk/extension/upgradeable/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol @@ -0,0 +1,94 @@ +// 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 new file mode 100644 index 000000000..78a4312de --- /dev/null +++ b/src/test/sdk/extension/upgradeable/royalty/set-default-royalty-info/setDefaultRoyaltyInfo.tree @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..d28a142ee --- /dev/null +++ b/src/test/sdk/extension/upgradeable/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol @@ -0,0 +1,108 @@ +// 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 new file mode 100644 index 000000000..e28295634 --- /dev/null +++ b/src/test/sdk/extension/upgradeable/royalty/set-royalty-info-for-token/setRoyaltyInfoForToken.tree @@ -0,0 +1,15 @@ +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 index fdd04e811..799ade4da 100644 --- a/src/test/smart-wallet/Account.t.sol +++ b/src/test/smart-wallet/Account.t.sol @@ -12,6 +12,7 @@ import { UserOperation } from "contracts/prebuilts/account/utils/UserOperation.s 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 { @@ -49,18 +50,16 @@ contract SimpleAccountTest is BaseTest { address private nonSigner; // UserOp terminology: `sender` is the smart wallet. - address private sender = 0xBB956D56140CA3f3060986586A2631922a4B347E; + 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) - { + 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)" ); @@ -91,11 +90,9 @@ contract SimpleAccountTest is BaseTest { typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } - function _signSignerPermissionRequest(IAccountPermissions.SignerPermissionRequest memory _req) - internal - view - returns (bytes memory signature) - { + 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); @@ -141,6 +138,63 @@ contract SimpleAccountTest is BaseTest { ops[0] = op; } + function _setupUserOpWithSender( + bytes memory _initCode, + bytes memory _callDataForEntrypoint, + address _sender + ) internal returns (UserOperation[] memory ops) { + uint256 nonce = entrypoint.getNonce(_sender, 0); + + // Get user op fields + UserOperation memory op = UserOperation({ + sender: _sender, + nonce: nonce, + initCode: _initCode, + callData: _callDataForEntrypoint, + callGasLimit: 500_000, + verificationGasLimit: 500_000, + preVerificationGas: 500_000, + maxFeePerGas: 0, + maxPriorityFeePerGas: 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 UserOperation[](1); + ops[0] = op; + } + + function _setupUserOpExecuteWithSender( + bytes memory _initCode, + address _target, + uint256 _value, + bytes memory _callData, + address _sender + ) internal returns (UserOperation[] 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, @@ -175,6 +229,11 @@ contract SimpleAccountTest is BaseTest { 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(); @@ -188,7 +247,7 @@ contract SimpleAccountTest is BaseTest { // Setup contracts entrypoint = new EntryPoint(); // deploy account factory - accountFactory = new AccountFactory(IEntryPoint(payable(address(entrypoint)))); + accountFactory = new AccountFactory(deployer, IEntryPoint(payable(address(entrypoint)))); // deploy dummy contract numberContract = new Number(); } @@ -234,7 +293,106 @@ contract SimpleAccountTest is BaseTest { function test_revert_onRegister_nonFactoryChildContract() public { vm.prank(address(0x12345)); vm.expectRevert("AccountFactory: not an account."); - accountFactory.onRegister(accountAdmin, ""); + 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) + ); + + UserOperation[] 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); } /*/////////////////////////////////////////////////////////////// diff --git a/src/test/smart-wallet/AccountVulnPOC.t.sol b/src/test/smart-wallet/AccountVulnPOC.t.sol index 4de05daf2..6ffd8b6ad 100644 --- a/src/test/smart-wallet/AccountVulnPOC.t.sol +++ b/src/test/smart-wallet/AccountVulnPOC.t.sol @@ -6,10 +6,12 @@ import "../utils/BaseTest.sol"; // Account Abstraction setup for smart wallets. import { EntryPoint, IEntryPoint } from "contracts/prebuilts/account/utils/Entrypoint.sol"; import { UserOperation } from "contracts/prebuilts/account/utils/UserOperation.sol"; +import { TWProxy } from "contracts/infra/TWProxy.sol"; // Target import { IAccountPermissions } from "contracts/extension/interface/IAccountPermissions.sol"; -import { AccountFactory, Account } from "contracts/prebuilts/account/non-upgradeable/AccountFactory.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; @@ -35,15 +37,11 @@ contract Number { num += 1; } - function setNumBySignature( - address owner, - uint256 newNum, - bytes calldata signature - ) public { + function setNumBySignature(address owner, uint256 newNum, bytes calldata signature) public { if (owner.code.length == 0) { // Signature verification by ECDSA } else { - // Signature verfication by EIP1271 + // Signature verification by EIP1271 bytes32 digest = keccak256(abi.encode(newNum)); require( EIP1271Verifier(owner).isValidSignature(digest, signature) == GPv2EIP1271.MAGICVALUE, @@ -73,18 +71,16 @@ contract SimpleAccountVulnPOCTest is BaseTest { address private nonSigner; // UserOp terminology: `sender` is the smart wallet. - address private sender = 0xBB956D56140CA3f3060986586A2631922a4B347E; + 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) - { + 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)" ); @@ -115,11 +111,9 @@ contract SimpleAccountVulnPOCTest is BaseTest { typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } - function _signSignerPermissionRequest(IAccountPermissions.SignerPermissionRequest memory _req) - internal - view - returns (bytes memory signature) - { + 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); @@ -212,7 +206,7 @@ contract SimpleAccountVulnPOCTest is BaseTest { // Setup contracts entrypoint = new EntryPoint(); // deploy account factory - accountFactory = new AccountFactory(IEntryPoint(payable(address(entrypoint)))); + accountFactory = new AccountFactory(deployer, IEntryPoint(payable(address(entrypoint)))); // deploy dummy contract numberContract = new Number(); } @@ -242,6 +236,8 @@ contract SimpleAccountVulnPOCTest is BaseTest { /*////////////////////////////////////////////////////////// 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 @@ -259,7 +255,6 @@ contract SimpleAccountVulnPOCTest is BaseTest { vm.prank(accountAdmin); bytes memory sig = _signSignerPermissionRequest(permissionsReq); - address account = accountFactory.getAddress(accountAdmin, bytes("")); 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 @@ -281,14 +276,40 @@ contract SimpleAccountVulnPOCTest is BaseTest { Attack //////////////////////////////////////////////////////////////*/ - //However they can bypass this by using signature verification on number contract instead + // However they can bypass this by using signature verification on number contract instead vm.prank(accountSigner); bytes32 digest = keccak256(abi.encode(42)); - (uint8 v, bytes32 r, bytes32 s) = vm.sign(accountSignerPKey, digest); + bytes32 toSign = SimpleAccount(payable(account)).getMessageHash(abi.encode(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 index 33f2a8585..5892ac43a 100644 --- a/src/test/smart-wallet/DynamicAccount.t.sol +++ b/src/test/smart-wallet/DynamicAccount.t.sol @@ -15,6 +15,7 @@ import { UserOperation } from "contracts/prebuilts/account/utils/UserOperation.s // 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 { @@ -34,19 +35,14 @@ contract Number { } contract NFTRejector { - function onERC721Received( - address, - address, - uint256, - bytes memory - ) public virtual returns (bytes4) { + function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) { revert("NFTs not accepted"); } } contract DynamicAccountTest is BaseTest { // Target contracts - EntryPoint private entrypoint; + EntryPoint private constant entrypoint = EntryPoint(payable(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789)); DynamicAccountFactory private accountFactory; // Mocks @@ -65,18 +61,16 @@ contract DynamicAccountTest is BaseTest { bytes internal data = bytes(""); // UserOp terminology: `sender` is the smart wallet. - address private sender = 0xbC12AEae5E1b1a80401dd20A6728f7a01a3A6166; + 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) - { + 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)" ); @@ -107,11 +101,9 @@ contract DynamicAccountTest is BaseTest { typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } - function _signSignerPermissionRequest(IAccountPermissions.SignerPermissionRequest memory _req) - internal - view - returns (bytes memory signature) - { + 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); @@ -157,6 +149,63 @@ contract DynamicAccountTest is BaseTest { ops[0] = op; } + function _setupUserOpWithSender( + bytes memory _initCode, + bytes memory _callDataForEntrypoint, + address _sender + ) internal returns (UserOperation[] memory ops) { + uint256 nonce = entrypoint.getNonce(_sender, 0); + + // Get user op fields + UserOperation memory op = UserOperation({ + sender: _sender, + nonce: nonce, + initCode: _initCode, + callData: _callDataForEntrypoint, + callGasLimit: 5_000_000, + verificationGasLimit: 5_000_000, + preVerificationGas: 5_000_000, + maxFeePerGas: 0, + maxPriorityFeePerGas: 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 UserOperation[](1); + ops[0] = op; + } + + function _setupUserOpExecuteWithSender( + bytes memory _initCode, + address _target, + uint256 _value, + bytes memory _callData, + address _sender + ) internal returns (UserOperation[] 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, @@ -191,6 +240,11 @@ contract DynamicAccountTest is BaseTest { 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(); @@ -202,7 +256,8 @@ contract DynamicAccountTest is BaseTest { nonSigner = vm.addr(nonSignerPKey); // Setup contracts - entrypoint = new EntryPoint(); + address _deployedEntrypoint = address(new EntryPoint()); + vm.etch(address(entrypoint), bytes(_deployedEntrypoint.code)); // Setting up default extension. IExtension.Extension memory defaultExtension; @@ -213,7 +268,7 @@ contract DynamicAccountTest is BaseTest { implementation: address(new AccountExtension()) }); - defaultExtension.functions = new IExtension.ExtensionFunction[](7); + defaultExtension.functions = new IExtension.ExtensionFunction[](9); defaultExtension.functions[0] = IExtension.ExtensionFunction( AccountExtension.supportsInterface.selector, @@ -243,12 +298,20 @@ contract DynamicAccountTest is BaseTest { 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(IEntryPoint(payable(address(entrypoint))), extensions); + accountFactory = new DynamicAccountFactory(deployer, extensions); // deploy dummy contract numberContract = new Number(); } @@ -303,10 +366,7 @@ contract DynamicAccountTest is BaseTest { extensions[0] = defaultExtension; // deploy account factory - DynamicAccountFactory factory = new DynamicAccountFactory( - IEntryPoint(payable(address(entrypoint))), - extensions - ); + DynamicAccountFactory factory = new DynamicAccountFactory(deployer, extensions); } /// @dev Create an account by directly calling the factory. @@ -346,7 +406,53 @@ contract DynamicAccountTest is BaseTest { function test_revert_onRegister_nonFactoryChildContract() public { vm.prank(address(0x12345)); vm.expectRevert("AccountFactory: not an account."); - accountFactory.onRegister(accountAdmin, ""); + 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) + ); + + UserOperation[] 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) + ) + ); + } } /*/////////////////////////////////////////////////////////////// diff --git a/src/test/smart-wallet/ManagedAccount.t.sol b/src/test/smart-wallet/ManagedAccount.t.sol index 8900950c5..643f8e1b8 100644 --- a/src/test/smart-wallet/ManagedAccount.t.sol +++ b/src/test/smart-wallet/ManagedAccount.t.sol @@ -15,6 +15,7 @@ import { UserOperation } from "contracts/prebuilts/account/utils/UserOperation.s // 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 { @@ -34,12 +35,7 @@ contract Number { } contract NFTRejector { - function onERC721Received( - address, - address, - uint256, - bytes memory - ) public virtual returns (bytes4) { + function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) { revert("NFTs not accepted"); } } @@ -66,18 +62,16 @@ contract ManagedAccountTest is BaseTest { address private nonSigner; // UserOp terminology: `sender` is the smart wallet. - address private sender = 0x56C860085A6A0AEd5137b17a185160865bf6a75A; + 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) - { + 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)" ); @@ -108,11 +102,9 @@ contract ManagedAccountTest is BaseTest { typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } - function _signSignerPermissionRequest(IAccountPermissions.SignerPermissionRequest memory _req) - internal - view - returns (bytes memory signature) - { + 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); @@ -158,6 +150,63 @@ contract ManagedAccountTest is BaseTest { ops[0] = op; } + function _setupUserOpWithSender( + bytes memory _initCode, + bytes memory _callDataForEntrypoint, + address _sender + ) internal returns (UserOperation[] memory ops) { + uint256 nonce = entrypoint.getNonce(_sender, 0); + + // Get user op fields + UserOperation memory op = UserOperation({ + sender: _sender, + nonce: nonce, + initCode: _initCode, + callData: _callDataForEntrypoint, + callGasLimit: 5_000_000, + verificationGasLimit: 5_000_000, + preVerificationGas: 5_000_000, + maxFeePerGas: 0, + maxPriorityFeePerGas: 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 UserOperation[](1); + ops[0] = op; + } + + function _setupUserOpExecuteWithSender( + bytes memory _initCode, + address _target, + uint256 _value, + bytes memory _callData, + address _sender + ) internal returns (UserOperation[] 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, @@ -192,6 +241,11 @@ contract ManagedAccountTest is BaseTest { 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(); @@ -214,7 +268,7 @@ contract ManagedAccountTest is BaseTest { implementation: address(new AccountExtension()) }); - defaultExtension.functions = new IExtension.ExtensionFunction[](7); + defaultExtension.functions = new IExtension.ExtensionFunction[](9); defaultExtension.functions[0] = IExtension.ExtensionFunction( AccountExtension.supportsInterface.selector, @@ -244,13 +298,25 @@ contract ManagedAccountTest is BaseTest { 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(IEntryPoint(payable(address(entrypoint))), extensions); + accountFactory = new ManagedAccountFactory( + factoryDeployer, + IEntryPoint(payable(address(entrypoint))), + extensions + ); // deploy dummy contract numberContract = new Number(); } @@ -303,6 +369,7 @@ contract ManagedAccountTest is BaseTest { // deploy account factory vm.prank(factoryDeployer); ManagedAccountFactory factory = new ManagedAccountFactory( + factoryDeployer, IEntryPoint(payable(address(entrypoint))), extensions ); @@ -350,7 +417,53 @@ contract ManagedAccountTest is BaseTest { function test_revert_onRegister_nonFactoryChildContract() public { vm.prank(address(0x12345)); vm.expectRevert("AccountFactory: not an account."); - accountFactory.onRegister(accountAdmin, ""); + 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) + ); + + UserOperation[] 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) + ) + ); + } } /*/////////////////////////////////////////////////////////////// diff --git a/src/test/smart-wallet/account-core/isValidSigner.t.sol b/src/test/smart-wallet/account-core/isValidSigner.t.sol index 453a8a630..49ddd39c3 100644 --- a/src/test/smart-wallet/account-core/isValidSigner.t.sol +++ b/src/test/smart-wallet/account-core/isValidSigner.t.sol @@ -35,9 +35,10 @@ contract Number { contract MyDynamicAccount is DynamicAccount { using EnumerableSet for EnumerableSet.AddressSet; - constructor(IEntryPoint _entrypoint, Extension[] memory _defaultExtensions) - DynamicAccount(_entrypoint, _defaultExtensions) - {} + constructor( + IEntryPoint _entrypoint, + Extension[] memory _defaultExtensions + ) DynamicAccount(_entrypoint, _defaultExtensions) {} function setPermissionsForSigner( address _signer, @@ -165,10 +166,10 @@ contract AccountCoreTest_isValidSigner is BaseTest { return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); } - function _setupUserOpInvalidFunction(uint256 _signerPKey, bytes memory _initCode) - internal - returns (UserOperation memory) - { + function _setupUserOpInvalidFunction( + uint256 _signerPKey, + bytes memory _initCode + ) internal returns (UserOperation memory) { bytes memory callDataForEntrypoint = abi.encodeWithSignature("invalidFunction()"); return _setupUserOp(_signerPKey, _initCode, callDataForEntrypoint); @@ -190,7 +191,7 @@ contract AccountCoreTest_isValidSigner is BaseTest { IExtension.Extension[] memory extensions; // deploy account factory - accountFactory = new DynamicAccountFactory(IEntryPoint(payable(address(entrypoint))), extensions); + accountFactory = new DynamicAccountFactory(deployer, extensions); // deploy dummy contract numberContract = new Number(); diff --git a/src/test/smart-wallet/account-permissions/setPermissionsForSigner.t.sol b/src/test/smart-wallet/account-permissions/setPermissionsForSigner.t.sol index 170db6fcd..f2eb8bb28 100644 --- a/src/test/smart-wallet/account-permissions/setPermissionsForSigner.t.sol +++ b/src/test/smart-wallet/account-permissions/setPermissionsForSigner.t.sol @@ -15,6 +15,7 @@ import { UserOperation } from "contracts/prebuilts/account/utils/UserOperation.s // 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 { @@ -34,12 +35,7 @@ contract Number { } contract NFTRejector { - function onERC721Received( - address, - address, - uint256, - bytes memory - ) public virtual returns (bytes4) { + function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) { revert("NFTs not accepted"); } } @@ -54,7 +50,7 @@ contract AccountPermissionsTest_setPermissionsForSigner is BaseTest { ); // Target contracts - EntryPoint private entrypoint; + EntryPoint private constant entrypoint = EntryPoint(payable(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789)); DynamicAccountFactory private accountFactory; // Mocks @@ -73,18 +69,16 @@ contract AccountPermissionsTest_setPermissionsForSigner is BaseTest { bytes internal data = bytes(""); // UserOp terminology: `sender` is the smart wallet. - address private sender = 0xbC12AEae5E1b1a80401dd20A6728f7a01a3A6166; + 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) - { + 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)" ); @@ -115,21 +109,17 @@ contract AccountPermissionsTest_setPermissionsForSigner is BaseTest { typedDataHash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } - function _signSignerPermissionRequest(IAccountPermissions.SignerPermissionRequest memory _req) - internal - view - returns (bytes memory signature) - { + 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) - { + 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); @@ -220,7 +210,8 @@ contract AccountPermissionsTest_setPermissionsForSigner is BaseTest { nonSigner = vm.addr(nonSignerPKey); // Setup contracts - entrypoint = new EntryPoint(); + address _deployedEntrypoint = address(new EntryPoint()); + vm.etch(address(entrypoint), bytes(_deployedEntrypoint.code)); // Setting up default extension. IExtension.Extension memory defaultExtension; @@ -266,7 +257,7 @@ contract AccountPermissionsTest_setPermissionsForSigner is BaseTest { extensions[0] = defaultExtension; // deploy account factory - accountFactory = new DynamicAccountFactory(IEntryPoint(payable(address(entrypoint))), extensions); + accountFactory = new DynamicAccountFactory(deployer, extensions); // deploy dummy contract numberContract = new Number(); } diff --git a/src/test/smart-wallet/utils/AABenchmarkArtifacts.sol b/src/test/smart-wallet/utils/AABenchmarkArtifacts.sol new file mode 100644 index 000000000..6502cae8f --- /dev/null +++ b/src/test/smart-wallet/utils/AABenchmarkArtifacts.sol @@ -0,0 +1,14 @@ + +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"608060405234801561001057600080fd5b50600436106101285760003560e01c806308e93d0a1461012d5780630b61e12b1461014b5780630e6254fd1461016057806311464fbe14610173578063248a9ca3146101b25780632f2ff15d146101d357806336568abe146101e657806358451f97146101f957806383a03f8c146102015780638878ed33146102145780639010d07c1461022757806391d148541461023a5780639387a3801461025d578063938e3d7b14610270578063a217fddf14610283578063a32fa5b31461028b578063a65d69d41461029e578063ac9650d8146102c5578063c3c5a547146102e5578063ca15c873146102f8578063d547741f1461030b578063d8fd8f441461031e578063e68a7c3b14610331578063e8a3d48514610344575b600080fd5b610135610359565b60405161014291906118d5565b60405180910390f35b61015e61015936600461193e565b61036a565b005b61013561016e366004611968565b61040b565b61019a7f000000000000000000000000ffd4505b3452dc22f8473616d50503ba9e1710ac81565b6040516001600160a01b039091168152602001610142565b6101c56101c0366004611983565b610435565b604051908152602001610142565b61015e6101e136600461199c565b610453565b61015e6101f436600461199c565b6104fd565b6101c561055c565b61015e61020f366004611983565b610568565b61019a6102223660046119c8565b6105b6565b61019a610235366004611a4a565b610630565b61024d61024836600461199c565b61073e565b6040519015158152602001610142565b61015e61026b36600461193e565b610772565b61015e61027e366004611a82565b610809565b6101c5600081565b61024d61029936600461199c565b61085a565b61019a7f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d278981565b6102d86102d3366004611b32565b6108bd565b6040516101429190611bf6565b61024d6102f3366004611968565b6109a6565b6101c5610306366004611983565b6109b2565b61015e61031936600461199c565b610a4f565b61019a61032c3660046119c8565b610a5a565b61013561033f366004611a4a565b610ba5565b61034c610cd6565b6040516101429190611c5a565b60606103656000610d6e565b905090565b336103758183610d7b565b61039a5760405162461bcd60e51b815260040161039190611c6d565b60405180910390fd5b6001600160a01b03831660009081526002602052604081206103bc9083610dbf565b9050801561040557836001600160a01b0316826001600160a01b03167f12146497b3b826918ec47f0cac7272a09ed06b30c16c030e99ec48ff5dd60b4760405160405180910390a35b50505050565b6001600160a01b038116600090815260026020526040902060609061042f90610d6e565b92915050565b600061043f610dd4565b600092835260010160205250604090205490565b61047761045e610dd4565b6000848152600191909101602052604090205433610df8565b61047f610dd4565b6000838152602091825260408082206001600160a01b0385168352909252205460ff16156104ef5760405162461bcd60e51b815260206004820152601d60248201527f43616e206f6e6c79206772616e7420746f206e6f6e20686f6c646572730000006044820152606401610391565b6104f98282610e7d565b5050565b336001600160a01b038216146105525760405162461bcd60e51b815260206004820152601a60248201527921b0b71037b7363c903932b737bab731b2903337b91039b2b63360311b6044820152606401610391565b6104f98282610e91565b60006103656000610ea5565b336105738183610d7b565b61058f5760405162461bcd60e51b815260040161039190611c6d565b61059a600082610dbf565b6104f95760405162461bcd60e51b815260040161039190611ca4565b6000806105f98585858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610eaf92505050565b90506106257f000000000000000000000000ffd4505b3452dc22f8473616d50503ba9e1710ac82610ee2565b9150505b9392505050565b60008061063b610f42565b600085815260209190915260408120549150805b82811015610735576000610661610f42565b60008881526020918252604080822085835260010190925220546001600160a01b0316146106d9578482036106c757610698610f42565b600087815260209182526040808220938252600190930190915220546001600160a01b0316925061042f915050565b6106d2600183611d04565b9150610723565b6106e486600061073e565b801561071057506106f3610f42565b600087815260209182526040808220828052600201909252205481145b1561072357610720600183611d04565b91505b61072e600182611d04565b905061064f565b50505092915050565b6000610748610dd4565b6000938452602090815260408085206001600160a01b039490941685529290525090205460ff1690565b3361077d8183610d7b565b6107995760405162461bcd60e51b815260040161039190611c6d565b6001600160a01b03831660009081526002602052604081206107bb9083610f4c565b9050801561040557836001600160a01b0316826001600160a01b03167f98d1ebbe00ae92a5de96a0f49742a8afa89f42363592bc2e7cfaaed68b45e7a660405160405180910390a350505050565b610811610f61565b61084e5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b6044820152606401610391565b61085781610f6d565b50565b6000610864610dd4565b600084815260209182526040808220828052909252205460ff166108b45761088a610dd4565b6000848152602091825260408082206001600160a01b0386168352909252205460ff16905061042f565b50600192915050565b6060816001600160401b038111156108d7576108d7611a6c565b60405190808252806020026020018201604052801561090a57816020015b60608152602001906001900390816108f55790505b50905060005b8281101561099f5761097a3085858481811061092e5761092e611d17565b90506020028101906109409190611d2d565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061105492505050565b82828151811061098c5761098c611d17565b6020908102919091010152600101610910565b5092915050565b600061042f8183611079565b6000806109bd610f42565b6000848152602091909152604081205491505b81811015610a2a5760006109e2610f42565b60008681526020918252604080822085835260010190925220546001600160a01b031614610a1857610a15600184611d04565b92505b610a23600182611d04565b90506109d0565b50610a3683600061073e565b15610a4957610a46600183611d04565b91505b50919050565b61055261045e610dd4565b6000807f000000000000000000000000ffd4505b3452dc22f8473616d50503ba9e1710ac90506000610ac28686868080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610eaf92505050565b90506000610ad08383610ee2565b90506001600160a01b0381163b15610aec579250610629915050565b610af6838361109b565b9050336001600160a01b037f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27891614610b4f57610b33600082610dbf565b610b4f5760405162461bcd60e51b815260040161039190611ca4565b610b5b81888888611132565b866001600160a01b0316816001600160a01b03167fac631f3001b55ea1509cf3d7e74898f85392a61a76e8149181ae1259622dabc860405160405180910390a39695505050505050565b60608183108015610bbf5750610bbb6000610ea5565b8211155b610c175760405162461bcd60e51b815260206004820152602360248201527f426173654163636f756e74466163746f72793a20696e76616c696420696e646960448201526263657360e81b6064820152608401610391565b6000610c238484611d7a565b9050610c2f8484611d7a565b6001600160401b03811115610c4657610c46611a6c565b604051908082528060200260200182016040528015610c6f578160200160208202803683370190505b50915060005b81811015610cce57610c92610c8a8683611d04565b60009061119a565b838281518110610ca457610ca4611d17565b6001600160a01b0390921660209283029190910190910152610cc7600182611d04565b9050610c75565b505092915050565b6060610ce06111a6565b8054610ceb90611d8d565b80601f0160208091040260200160405190810160405280929190818152602001828054610d1790611d8d565b8015610d645780601f10610d3957610100808354040283529160200191610d64565b820191906000526020600020905b815481529060010190602001808311610d4757829003601f168201915b5050505050905090565b60606000610629836111ca565b600080610da87f000000000000000000000000ffd4505b3452dc22f8473616d50503ba9e1710ac84610ee2565b6001600160a01b0385811691161491505092915050565b6000610629836001600160a01b038416611226565b7f0a7b0f5c59907924802379ebe98cdc23e2ee7820f63d30126e10b3752010e50090565b610e00610dd4565b6000838152602091825260408082206001600160a01b0385168352909252205460ff166104f957610e3b816001600160a01b03166014611275565b610e46836020611275565b604051602001610e57929190611dc1565b60408051601f198184030181529082905262461bcd60e51b825261039191600401611c5a565b610e878282611410565b6104f98282611479565b610e9b8282611538565b6104f982826115a1565b600061042f825490565b60008282604051602001610ec4929190611e2e565b60405160208183030381529060405280519060200120905092915050565b6040513060388201526f5af43d82803e903d91602b57fd5bf3ff602482015260148101839052733d602d80600a3d3981f3363d3d373d3d3d363d738152605881018290526037600c82012060788201526055604390910120600090610629565b6000610365611630565b6000610629836001600160a01b038416611692565b6000610365813361073e565b6000610f776111a6565b8054610f8290611d8d565b80601f0160208091040260200160405190810160405280929190818152602001828054610fae90611d8d565b8015610ffb5780601f10610fd057610100808354040283529160200191610ffb565b820191906000526020600020905b815481529060010190602001808311610fde57829003601f168201915b505050505090508161100b6111a6565b906110169082611eab565b507fc9c7c3fe08b88b4df9d4d47ef47d2c43d55c025a0ba88ca442580ed9e7348a168183604051611048929190611f6a565b60405180910390a15050565b6060610629838360405180606001604052806027815260200161203060279139611785565b6001600160a01b03811660009081526001830160205260408120541515610629565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b03811661042f5760405162461bcd60e51b8152602060048201526017602482015276115490cc4c4d8dce8818dc99585d194c8819985a5b1959604a1b6044820152606401610391565b60405163347d5e2560e21b81526001600160a01b0385169063d1f578949061116290869086908690600401611f8f565b600060405180830381600087803b15801561117c57600080fd5b505af1158015611190573d6000803e3d6000fd5b5050505050505050565b60006106298383611863565b7f4bc804ba64359c0e35e5ed5d90ee596ecaa49a3a930ddcb1470ea0dd625da90090565b60608160000180548060200260200160405190810160405280929190818152602001828054801561121a57602002820191906000526020600020905b815481526020019060010190808311611206575b50505050509050919050565b600081815260018301602052604081205461126d5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561042f565b50600061042f565b60606000611284836002611fcf565b61128f906002611d04565b6001600160401b038111156112a6576112a6611a6c565b6040519080825280601f01601f1916602001820160405280156112d0576020820181803683370190505b509050600360fc1b816000815181106112eb576112eb611d17565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061131a5761131a611d17565b60200101906001600160f81b031916908160001a905350600061133e846002611fcf565b611349906001611d04565b90505b60018111156113c1576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061137d5761137d611d17565b1a60f81b82828151811061139357611393611d17565b60200101906001600160f81b031916908160001a90535060049490941c936113ba81611fe6565b905061134c565b5083156106295760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610391565b600161141a610dd4565b6000848152602091825260408082206001600160a01b0386168084529352808220805460ff1916941515949094179093559151339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6000611483610f42565b600084815260209190915260409020549050600161149f610f42565b60008581526020919091526040812080549091906114be908490611d04565b909155508290506114cd610f42565b6000858152602091825260408082208583526001019092522080546001600160a01b0319166001600160a01b03929092169190911790558061150d610f42565b6000948552602090815260408086206001600160a01b03909516865260029094019052919092205550565b6115428282610df8565b61154a610dd4565b6000838152602091825260408082206001600160a01b0385168084529352808220805460ff191690555133929185917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006115ab610f42565b6000848152602091825260408082206001600160a01b0386168352600201909252205490506115d8610f42565b6000848152602091825260408082208483526001019092522080546001600160a01b0319169055611607610f42565b6000938452602090815260408085206001600160a01b0390941685526002909301905250812055565b60008060ff1961166160017f0c4ba382c0009cf238e4c1ca1a52f51c61e6248a70bdfb34e5ed49d5578a5c0c611d7a565b60405160200161167391815260200190565b60408051601f1981840301815291905280516020909101201692915050565b6000818152600183016020526040812054801561177b5760006116b6600183611d7a565b85549091506000906116ca90600190611d7a565b905081811461172f5760008660000182815481106116ea576116ea611d17565b906000526020600020015490508087600001848154811061170d5761170d611d17565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061174057611740611ffd565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061042f565b600091505061042f565b60606117908461188d565b80156117a257506117a03361188d565b155b6117ee5760405162461bcd60e51b815260206004820152601e60248201527f416464726573733a20696e76616c69642064656c65676174652063616c6c00006044820152606401610391565b600080856001600160a01b0316856040516118099190612013565b600060405180830381855af49150503d8060008114611844576040519150601f19603f3d011682016040523d82523d6000602084013e611849565b606091505b509150915061185982828661189c565b9695505050505050565b600082600001828154811061187a5761187a611d17565b9060005260206000200154905092915050565b6001600160a01b03163b151590565b606083156118ab575081610629565b8251156118bb5782518084602001fd5b8160405162461bcd60e51b81526004016103919190611c5a565b6020808252825182820181905260009190848201906040850190845b818110156119165783516001600160a01b0316835292840192918401916001016118f1565b50909695505050505050565b80356001600160a01b038116811461193957600080fd5b919050565b6000806040838503121561195157600080fd5b61195a83611922565b946020939093013593505050565b60006020828403121561197a57600080fd5b61062982611922565b60006020828403121561199557600080fd5b5035919050565b600080604083850312156119af57600080fd5b823591506119bf60208401611922565b90509250929050565b6000806000604084860312156119dd57600080fd5b6119e684611922565b925060208401356001600160401b0380821115611a0257600080fd5b818601915086601f830112611a1657600080fd5b813581811115611a2557600080fd5b876020828501011115611a3757600080fd5b6020830194508093505050509250925092565b60008060408385031215611a5d57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b600060208284031215611a9457600080fd5b81356001600160401b0380821115611aab57600080fd5b818401915084601f830112611abf57600080fd5b813581811115611ad157611ad1611a6c565b604051601f8201601f19908116603f01168101908382118183101715611af957611af9611a6c565b81604052828152876020848701011115611b1257600080fd5b826020860160208301376000928101602001929092525095945050505050565b60008060208385031215611b4557600080fd5b82356001600160401b0380821115611b5c57600080fd5b818501915085601f830112611b7057600080fd5b813581811115611b7f57600080fd5b8660208260051b8501011115611b9457600080fd5b60209290920196919550909350505050565b60005b83811015611bc1578181015183820152602001611ba9565b50506000910152565b60008151808452611be2816020860160208601611ba6565b601f01601f19169290920160200192915050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b82811015611c4d57603f19888603018452611c3b858351611bca565b94509285019290850190600101611c1f565b5092979650505050505050565b6020815260006106296020830184611bca565b6020808252601f908201527f4163636f756e74466163746f72793a206e6f7420616e206163636f756e742e00604082015260600190565b6020808252602a908201527f4163636f756e74466163746f72793a206163636f756e7420616c7265616479206040820152691c9959da5cdd195c995960b21b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8082018082111561042f5761042f611cee565b634e487b7160e01b600052603260045260246000fd5b6000808335601e19843603018112611d4457600080fd5b8301803591506001600160401b03821115611d5e57600080fd5b602001915036819003821315611d7357600080fd5b9250929050565b8181038181111561042f5761042f611cee565b600181811c90821680611da157607f821691505b602082108103610a4957634e487b7160e01b600052602260045260246000fd5b7402832b936b4b9b9b4b7b7399d1030b1b1b7bab73a1605d1b815260008351611df1816015850160208801611ba6565b7001034b99036b4b9b9b4b733903937b6329607d1b6015918401918201528351611e22816026840160208801611ba6565b01602601949350505050565b6001600160a01b0383168152604060208201819052600090611e5290830184611bca565b949350505050565b601f821115611ea6576000816000526020600020601f850160051c81016020861015611e835750805b601f850160051c820191505b81811015611ea257828155600101611e8f565b5050505b505050565b81516001600160401b03811115611ec457611ec4611a6c565b611ed881611ed28454611d8d565b84611e5a565b602080601f831160018114611f0d5760008415611ef55750858301515b600019600386901b1c1916600185901b178555611ea2565b600085815260208120601f198616915b82811015611f3c57888601518255948401946001909101908401611f1d565b5085821015611f5a5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b604081526000611f7d6040830185611bca565b82810360208401526106258185611bca565b6001600160a01b03841681526040602082018190528101829052818360608301376000818301606090810191909152601f909201601f1916010192915050565b808202811582820484141761042f5761042f611cee565b600081611ff557611ff5611cee565b506000190190565b634e487b7160e01b600052603160045260246000fd5b60008251612025818460208701611ba6565b919091019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220f3f85d264596b5ea921ce5fd2e297c9ed38322d32f7f884eb4ef1c787629afd064736f6c63430008170033"; +bytes constant THIRDWEB_ACCOUNT_IMPL_BYTECODE = hex"60806040526004361061014b5760003560e01c806301ffc9a7146101575780630a1028c41461018c578063150b7a02146101ba5780631626ba7e146101fe5780631dd756c51461021e57806324d7806c1461023e5780633a871cdd1461025e57806347e1da2a1461027e5780634a58db19146102a05780634d44560d146102a85780635892e236146102c85780637dff5a79146102e85780638b52d72314610308578063938e3d7b1461032a578063a9082d841461034a578063ac9650d814610389578063b0d691fe146103b6578063b61d27f6146103d8578063b76464d5146103f8578063bc197c8114610418578063c45a015514610444578063d087d28814610478578063d1f578941461048d578063d42f2f35146104ad578063e8a3d485146104c2578063e9523c97146104e4578063f15d424e14610506578063f23a6e611461053357600080fd5b3661015257005b600080fd5b34801561016357600080fd5b50610177610172366004612d1a565b61055f565b60405190151581526020015b60405180910390f35b34801561019857600080fd5b506101ac6101a7366004612e01565b6105a5565b604051908152602001610183565b3480156101c657600080fd5b506101e56101d5366004612e5a565b630a85bd0160e11b949350505050565b6040516001600160e01b03199091168152602001610183565b34801561020a57600080fd5b506101e5610219366004612ec5565b610649565b34801561022a57600080fd5b50610177610239366004612f24565b610789565b34801561024a57600080fd5b50610177610259366004612f69565b610a4d565b34801561026a57600080fd5b506101ac610279366004612f86565b610a7c565b34801561028a57600080fd5b5061029e610299366004613017565b610aa2565b005b61029e610c09565b3480156102b457600080fd5b5061029e6102c33660046130b0565b610c71565b3480156102d457600080fd5b5061029e6102e336600461311d565b610ce4565b3480156102f457600080fd5b50610177610303366004612f69565b6110a1565b34801561031457600080fd5b5061031d61115a565b6040516101839190613230565b34801561033657600080fd5b5061029e610345366004613294565b6113a1565b34801561035657600080fd5b5061036a61036536600461311d565b6113f2565b6040805192151583526001600160a01b03909116602083015201610183565b34801561039557600080fd5b506103a96103a43660046132dc565b611449565b604051610183919061336d565b3480156103c257600080fd5b506103cb611532565b60405161018391906133c4565b3480156103e457600080fd5b5061029e6103f33660046133d8565b61157b565b34801561040457600080fd5b5061029e610413366004612f69565b61160b565b34801561042457600080fd5b506101e56104333660046134c5565b63bc197c8160e01b95945050505050565b34801561045057600080fd5b506103cb7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b81565b34801561048457600080fd5b506101ac61163d565b34801561049957600080fd5b5061029e6104a8366004613572565b6116bd565b3480156104b957600080fd5b5061031d611875565b3480156104ce57600080fd5b506104d76119e6565b60405161018391906135b9565b3480156104f057600080fd5b506104f9611a7e565b60405161018391906135cc565b34801561051257600080fd5b50610526610521366004612f69565b611a90565b6040516101839190613619565b34801561053f57600080fd5b506101e561054e36600461362c565b63f23a6e6160e01b95945050505050565b60006001600160e01b03198216630271189760e51b148061059057506001600160e01b03198216630a85bd0160e11b145b8061059f575061059f82611b68565b92915050565b6000807f82cac545155fcbf147f2a9013809613677ac7d65498556e6d19ce43bcbf6c28483805190602001206040516020016105eb929190918252602082015260400190565b60405160208183030381529060405280519060200120905061060b611b9d565b60405161190160f01b602082015260228101919091526042810182905260620160405160208183030381529060405280519060200120915050919050565b6000806106768460405160200161066291815260200190565b6040516020818303038152906040526105a5565b905060006106848285611cc4565b905061068f81610a4d565b156106a65750630b135d3f60e11b915061059f9050565b3360006106b1611ce8565b6001600160a01b03841660009081526006919091016020526040902090506106d98183611d0c565b8061070957506106e881611d2e565b6001148015610709575060006106fe8282611d38565b6001600160a01b0316145b6107665760405162461bcd60e51b8152602060048201526024808201527f4163636f756e743a2063616c6c6572206e6f7420617070726f7665642074617260448201526333b2ba1760e11b60648201526084015b60405180910390fd5b61076f836110a1565b1561077f57630b135d3f60e11b94505b5050505092915050565b6000610793611ce8565b6001600160a01b0384166000908152600491909101602052604090205460ff16156107c05750600161059f565b60006107ca611ce8565b6001600160a01b0385166000908152600591909101602090815260408083208151606081018352815481526001909101546001600160801b0380821694830194909452600160801b9004909216908201529150610825611ce8565b6006016000866001600160a01b03166001600160a01b0316815260200190815260200160002090504282602001516001600160801b03161180610875575081604001516001600160801b03164210155b80610886575061088481611d2e565b155b156108965760009250505061059f565b60006108ad6108a86060870187613694565b611d44565b905060006108ba83611d2e565b60011480156108db575060006108d08482611d38565b6001600160a01b0316145b90506324f16c0560e11b6001600160e01b03198316016109525760008061090d61090860608a018a613694565b611d7e565b9150915082610933576109208583611d0c565b610933576000965050505050505061059f565b855181111561094b576000965050505050505061059f565b5050610a40565b635c0f12eb60e11b6001600160e01b0319831601610a335760008061098261097d60608a018a613694565b611de3565b5091509150826109e25760005b82518110156109e0576109c48382815181106109ad576109ad6136da565b602002602001015187611d0c90919063ffffffff16565b6109d857600097505050505050505061059f565b60010161098f565b505b60005b8251811015610a2b57818181518110610a0057610a006136da565b602002602001015187600001511015610a2357600097505050505050505061059f565b6001016109e5565b505050610a40565b600094505050505061059f565b5060019695505050505050565b6000610a57611ce8565b6001600160a01b03909216600090815260049290920160205250604090205460ff1690565b6000610a86611e30565b610a908484611e99565b9050610a9b82611fde565b9392505050565b610aaa611532565b6001600160a01b0316336001600160a01b03161480610acd5750610acd33610a4d565b610ae95760405162461bcd60e51b815260040161075d906136f0565b610af161202b565b8481148015610aff57508483145b610b4b5760405162461bcd60e51b815260206004820152601d60248201527f4163636f756e743a2077726f6e67206172726179206c656e677468732e000000604482015260640161075d565b60005b85811015610c0057610bf7878783818110610b6b57610b6b6136da565b9050602002016020810190610b809190612f69565b868684818110610b9257610b926136da565b90506020020135858585818110610bab57610bab6136da565b9050602002810190610bbd9190613694565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061211192505050565b50600101610b4e565b50505050505050565b610c11611532565b6001600160a01b031663b760faf934306040518363ffffffff1660e01b8152600401610c3d91906133c4565b6000604051808303818588803b158015610c5657600080fd5b505af1158015610c6a573d6000803e3d6000fd5b5050505050565b610c79612182565b610c81611532565b6001600160a01b031663205c287883836040518363ffffffff1660e01b8152600401610cae929190613731565b600060405180830381600087803b158015610cc857600080fd5b505af1158015610cdc573d6000803e3d6000fd5b505050505050565b6000610cf36020850185612f69565b905042610d0660e0860160c08701613761565b6001600160801b031611158015610d355750610d29610100850160e08601613761565b6001600160801b031642105b610d6b5760405162461bcd60e51b8152602060048201526007602482015266085c195c9a5bd960ca1b604482015260640161075d565b600080610d798686866113f2565b9150915081610db35760405162461bcd60e51b815260040161075d906020808252600490820152632173696760e01b604082015260600190565b6001610dbd611ce8565b610100880135600090815260079190910160209081526040808320805460ff1916941515949094179093559091610df99190890190890161378d565b60ff161115610e26576000610e14604088016020890161378d565b60ff166001149050610c0084826121c0565b610e2f83610a4d565b15610e645760405162461bcd60e51b815260206004820152600560248201526430b236b4b760d91b604482015260640161075d565b610e7983610e70611ce8565b60020190612295565b50604051806060016040528087606001358152602001876080016020810190610ea29190613761565b6001600160801b03168152602001610ec060c0890160a08a01613761565b6001600160801b03169052610ed3611ce8565b6001600160a01b03851660009081526005919091016020908152604080832084518155918401519301516001600160801b03908116600160801b02931692909217600190920191909155610f49610f28611ce8565b6001600160a01b0386166000908152600691909101602052604090206122aa565b805190915060005b81811015610fb357610fa0838281518110610f6e57610f6e6136da565b6020026020010151610f7e611ce8565b6001600160a01b038916600090815260069190910160205260409020906122b7565b50610fac6001826137be565b9050610f51565b50610fc160408901896137d1565b9050905060005b818110156110425761102f610fe060408b018b6137d1565b83818110610ff057610ff06136da565b90506020020160208101906110059190612f69565b61100d611ce8565b6001600160a01b03891660009081526006919091016020526040902090612295565b5061103b6001826137be565b9050610fc8565b5061104c886122cc565b846001600160a01b0316836001600160a01b03167ff21d10c26e35863a8df291aca54181ee8c4a3bc8e00246c3f7a5a14b69d826a78a60405161108f91906138ab565b60405180910390a35050505050505050565b6000806110ac611ce8565b6001600160a01b038416600090815260059190910160209081526040918290208251606081018452815481526001909101546001600160801b03808216938301849052600160801b9091041692810192909252909150421080159061111d575080604001516001600160801b031642105b8015610a9b57506000611152611131611ce8565b6001600160a01b038616600090815260069190910160205260409020611d2e565b119392505050565b60606000611171611169611ce8565b6002016122aa565b80519091506000805b82811015611202576111a4848281518110611197576111976136da565b60200260200101516110a1565b156111bb57816111b381613996565b9250506111f0565b60008482815181106111cf576111cf6136da565b60200260200101906001600160a01b031690816001600160a01b0316815250505b6111fb6001826137be565b905061117a565b50806001600160401b0381111561121b5761121b612d44565b60405190808252806020026020018201604052801561125457816020015b611241612cd0565b8152602001906001900390816112395790505b5093506000805b838110156113995760006001600160a01b0316858281518110611280576112806136da565b60200260200101516001600160a01b0316146113875760008582815181106112aa576112aa6136da565b6020026020010151905060006112be611ce8565b6001600160a01b038316600081815260059290920160209081526040928390208351606081018552815481526001909101546001600160801b0380821683850152600160801b9091041681850152835160a081019094529183529092508101611328610f28611ce8565b81526020018260000151815260200182602001516001600160801b0316815260200182604001516001600160801b031681525088858061136790613996565b965081518110611379576113796136da565b602002602001018190525050505b6113926001826137be565b905061125b565b505050505090565b6113a9612361565b6113e65760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b604482015260640161075d565b6113ef81612379565b50565b60008061140861140186612460565b85856125a4565b9050611412611ce8565b6101008601356000908152600791909101602052604090205460ff1615801561143f575061143f81610a4d565b9150935093915050565b6060816001600160401b0381111561146357611463612d44565b60405190808252806020026020018201604052801561149657816020015b60608152602001906001900390816114815790505b50905060005b8281101561152b57611506308585848181106114ba576114ba6136da565b90506020028101906114cc9190613694565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506125fe92505050565b828281518110611518576115186136da565b602090810291909101015260010161149c565b5092915050565b60008061153d612623565b546001600160a01b03169050801561155457919050565b7f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d278991505090565b611583611532565b6001600160a01b0316336001600160a01b031614806115a657506115a633610a4d565b6115c25760405162461bcd60e51b815260040161075d906136f0565b6115ca61202b565b610c6a848484848080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061211192505050565b611613612182565b8061161c612623565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6000611647611532565b604051631aab3f0d60e11b8152306004820152600060248201526001600160a01b0391909116906335567e1a90604401602060405180830381865afa158015611694573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116b891906139af565b905090565b60006116c7612647565b5460ff16905060006116d7612647565b54610100900460ff16905080158080156116f4575060018360ff16105b8061171357506117033061266b565b15801561171357508260ff166001145b6117765760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161075d565b6001611780612647565b805460ff191660ff9290921691909117905580156117b95760016117a2612647565b80549115156101000261ff00199092169190911790555b6117f98686868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061267a92505050565b611801612623565b600101819055506118138660016121c0565b8015610cdc576000611823612647565b80549115156101000261ff0019909216919091179055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050505050565b60606000611884611169611ce8565b8051909150806001600160401b038111156118a1576118a1612d44565b6040519080825280602002602001820160405280156118da57816020015b6118c7612cd0565b8152602001906001900390816118bf5790505b50925060005b818110156119e05760008382815181106118fc576118fc6136da565b602002602001015190506000611910611ce8565b6001600160a01b038316600081815260059290920160209081526040928390208351606081018552815481526001909101546001600160801b0380821683850152600160801b9091041681850152835160a08101909452918352909250810161197a610f28611ce8565b81526020018260000151815260200182602001516001600160801b0316815260200182604001516001600160801b03168152508684815181106119bf576119bf6136da565b602002602001018190525050506001816119d991906137be565b90506118e0565b50505090565b60606119f06126ad565b80546119fb906139c8565b80601f0160208091040260200160405190810160405280929190818152602001828054611a27906139c8565b8015611a745780601f10611a4957610100808354040283529160200191611a74565b820191906000526020600020905b815481529060010190602001808311611a5757829003601f168201915b5050505050905090565b60606116b8611a8b611ce8565b6122aa565b611a98612cd0565b6000611aa2611ce8565b6001600160a01b038416600081815260059290920160209081526040928390208351606081018552815481526001909101546001600160801b0380821683850152600160801b9091041681850152835160a081019094529183529092508101611b2d611b0c611ce8565b6001600160a01b0387166000908152600691909101602052604090206122aa565b81526020018260000151815260200182602001516001600160801b0316815260200182604001516001600160801b0316815250915050919050565b60006001600160e01b03198216630271189760e51b148061059f57506301ffc9a760e01b6001600160e01b031983161461059f565b6000306001600160a01b037f000000000000000000000000ffd4505b3452dc22f8473616d50503ba9e1710ac16148015611bf657507f0000000000000000000000000000000000000000000000000000000000007a6946145b15611c2057507fbcdadf6444930a967ffda04923d78c49b3dd65df3ed39abb04a1e3eb1190553790565b50604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020808301919091527ff0729608244859f656d32ae4cbc6b0367695d68d8e941a28f5e2d33c6d5182dd828401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608301524660808301523060a0808401919091528351808403909101815260c0909201909252805191012090565b6000806000611cd385856126d1565b91509150611ce081612716565b509392505050565b7f3181e78fc1b109bc611fd2406150bf06e33faa75f71cba12c3e1fd670f2def0090565b6001600160a01b03811660009081526001830160205260408120541515610a9b565b600061059f825490565b6000610a9b838361285b565b60006004821015611d675760405162461bcd60e51b815260040161075d906139fc565b611d75600460008486613a1b565b610a9b91613a45565b6000806044831015611da25760405162461bcd60e51b815260040161075d906139fc565b611db0602460048587613a1b565b810190611dbd9190612f69565b9150611dcd604460248587613a1b565b810190611dda9190613a75565b90509250929050565b606080806064841015611e085760405162461bcd60e51b815260040161075d906139fc565b611e158460048188613a1b565b810190611e229190613b0d565b919790965090945092505050565b611e38611532565b6001600160a01b0316336001600160a01b031614611e975760405162461bcd60e51b815260206004820152601c60248201527b1858d8dbdd5b9d0e881b9bdd08199c9bdb48115b9d1c9e541bda5b9d60221b604482015260640161075d565b565b7b0ca2ba3432b932bab69029b4b3b732b21026b2b9b9b0b3b29d05199960211b6000908152601c829052603c81206000611f17611eda610140870187613694565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152508693925050611cc49050565b9050611f238186610789565b611f325760019250505061059f565b6000611f3c611ce8565b6001600160a01b03929092166000908152600590920160209081526040808420815160608082018452825482526001909201546001600160801b0380821683870152600160801b8204908116928501929092528351928301845295825265ffffffffffff8087169483019490945292831691015260d09290921b6001600160d01b03191660a09290921b65ffffffffffff60a01b169190911795945050505050565b80156113ef57604051600090339060001990849084818181858888f193505050503d8060008114610c6a576040519150601f19603f3d011682016040523d82523d6000602084013e610c6a565b60405163c3c5a54760e01b81527f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b906001600160a01b0382169063c3c5a547906120799030906004016133c4565b602060405180830381865afa158015612096573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ba9190613bf2565b6113ef57806001600160a01b03166383a03f8c6120d5612623565b600101546040518263ffffffff1660e01b81526004016120f791815260200190565b600060405180830381600087803b158015610c5657600080fd5b60606000846001600160a01b0316848460405161212e9190613c14565b60006040518083038185875af1925050503d806000811461216b576040519150601f19603f3d011682016040523d82523d6000602084013e612170565b606091505b509250905080611ce057815160208301fd5b61218b33610a4d565b611e975760405162461bcd60e51b815260206004820152600660248201526510b0b236b4b760d11b604482015260640161075d565b6121ca8282612885565b6001600160a01b037f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b163b15612291578015612259577f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b6001600160a01b0316630b61e12b83612238612623565b600101546040518363ffffffff1660e01b8152600401610cae929190613731565b7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b6001600160a01b0316639387a38083612238612623565b5050565b6000610a9b836001600160a01b038416612934565b60606000610a9b83612983565b6000610a9b836001600160a01b0384166129df565b6001600160a01b037f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b163b156113ef576001600160a01b037f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16630b61e12b6123386020840184612f69565b612340612623565b600101546040518363ffffffff1660e01b81526004016120f7929190613731565b600061236c33610a4d565b806116b857505030331490565b60006123836126ad565b805461238e906139c8565b80601f01602080910402602001604051908101604052809291908181526020018280546123ba906139c8565b80156124075780601f106123dc57610100808354040283529160200191612407565b820191906000526020600020905b8154815290600101906020018083116123ea57829003601f168201915b50505050509050816124176126ad565b906124229082613c7d565b507fc9c7c3fe08b88b4df9d4d47ef47d2c43d55c025a0ba88ca442580ed9e7348a168183604051612454929190613d3c565b60405180910390a15050565b60607f3fd4a1a1a267c84185e3b7eecd57c68783c0581d538b9d6e5f23e4670497c1e96124906020840184612f69565b6124a0604085016020860161378d565b6124ad60408601866137d1565b6040516020016124be929190613d6a565b60408051601f19818403018152919052805160209091012060608601356124eb60a0880160808901613761565b6124fb60c0890160a08a01613761565b61250b60e08a0160c08b01613761565b61251c6101008b0160e08c01613761565b60408051602081019a909a526001600160a01b039098169789019790975260ff9095166060880152608087019390935260a08601919091526001600160801b0390811660c086015290811660e0850152908116610100848101919091529116610120830152830135610140820152610160016040516020818303038152906040529050919050565b60006125f683838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050875160208901206125f092509050612ad2565b90611cc4565b949350505050565b6060610a9b8383604051806060016040528060278152602001613e1060279139612aff565b7f036f52c1827dab135f7fd44ca0bddde297e2f659c710e0ec53e975f22b54830090565b7f322cf19c484104d3b1a9c2982ebae869ede3fa5f6c4703ca41b9a48c76ee030090565b6001600160a01b03163b151590565b6000828260405160200161268f929190613dac565b60405160208183030381529060405280519060200120905092915050565b7f4bc804ba64359c0e35e5ed5d90ee596ecaa49a3a930ddcb1470ea0dd625da90090565b60008082516041036127075760208301516040840151606085015160001a6126fb87828585612bdd565b9450945050505061270f565b506000905060025b9250929050565b600081600481111561272a5761272a613dd0565b036127325750565b600181600481111561274657612746613dd0565b0361278e5760405162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b604482015260640161075d565b60028160048111156127a2576127a2613dd0565b036127ef5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161075d565b600381600481111561280357612803613dd0565b036113ef5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161075d565b6000826000018281548110612872576128726136da565b9060005260206000200154905092915050565b8061288e611ce8565b6001600160a01b038416600090815260049190910160205260409020805460ff191691151591909117905580156128d7576128d1826128cb611ce8565b90612295565b506128eb565b6128e9826128e3611ce8565b906122b7565b505b816001600160a01b03167f235bc17e7930760029e9f4d860a2a8089976de5b381cf8380fc11c1d88a1113382604051612928911515815260200190565b60405180910390a25050565b600081815260018301602052604081205461297b5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561059f565b50600061059f565b6060816000018054806020026020016040519081016040528092919081815260200182805480156129d357602002820191906000526020600020905b8154815260200190600101908083116129bf575b50505050509050919050565b60008181526001830160205260408120548015612ac8576000612a03600183613de6565b8554909150600090612a1790600190613de6565b9050818114612a7c576000866000018281548110612a3757612a376136da565b9060005260206000200154905080876000018481548110612a5a57612a5a6136da565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080612a8d57612a8d613df9565b60019003818190600052602060002001600090559055856001016000868152602001908152602001600020600090556001935050505061059f565b600091505061059f565b600061059f612adf611b9d565b8360405161190160f01b8152600281019290925260228201526042902090565b6060612b0a8461266b565b8015612b1c5750612b1a3361266b565b155b612b685760405162461bcd60e51b815260206004820152601e60248201527f416464726573733a20696e76616c69642064656c65676174652063616c6c0000604482015260640161075d565b600080856001600160a01b031685604051612b839190613c14565b600060405180830381855af49150503d8060008114612bbe576040519150601f19603f3d011682016040523d82523d6000602084013e612bc3565b606091505b5091509150612bd3828286612c97565b9695505050505050565b6000806fa2a8918ca85bafe22016d0b997e4df60600160ff1b03831115612c0a5750600090506003612c8e565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015612c5e573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116612c8757600060019250925050612c8e565b9150600090505b94509492505050565b60608315612ca6575081610a9b565b825115612cb65782518084602001fd5b8160405162461bcd60e51b815260040161075d91906135b9565b6040518060a0016040528060006001600160a01b03168152602001606081526020016000815260200160006001600160801b0316815260200160006001600160801b031681525090565b600060208284031215612d2c57600080fd5b81356001600160e01b031981168114610a9b57600080fd5b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715612d8257612d82612d44565b604052919050565b60006001600160401b03831115612da357612da3612d44565b612db6601f8401601f1916602001612d5a565b9050828152838383011115612dca57600080fd5b828260208301376000602084830101529392505050565b600082601f830112612df257600080fd5b610a9b83833560208501612d8a565b600060208284031215612e1357600080fd5b81356001600160401b03811115612e2957600080fd5b6125f684828501612de1565b6001600160a01b03811681146113ef57600080fd5b8035612e5581612e35565b919050565b60008060008060808587031215612e7057600080fd5b8435612e7b81612e35565b93506020850135612e8b81612e35565b92506040850135915060608501356001600160401b03811115612ead57600080fd5b612eb987828801612de1565b91505092959194509250565b60008060408385031215612ed857600080fd5b8235915060208301356001600160401b03811115612ef557600080fd5b612f0185828601612de1565b9150509250929050565b60006101608284031215612f1e57600080fd5b50919050565b60008060408385031215612f3757600080fd5b8235612f4281612e35565b915060208301356001600160401b03811115612f5d57600080fd5b612f0185828601612f0b565b600060208284031215612f7b57600080fd5b8135610a9b81612e35565b600080600060608486031215612f9b57600080fd5b83356001600160401b03811115612fb157600080fd5b612fbd86828701612f0b565b9660208601359650604090950135949350505050565b60008083601f840112612fe557600080fd5b5081356001600160401b03811115612ffc57600080fd5b6020830191508360208260051b850101111561270f57600080fd5b6000806000806000806060878903121561303057600080fd5b86356001600160401b038082111561304757600080fd5b6130538a838b01612fd3565b9098509650602089013591508082111561306c57600080fd5b6130788a838b01612fd3565b9096509450604089013591508082111561309157600080fd5b5061309e89828a01612fd3565b979a9699509497509295939492505050565b600080604083850312156130c357600080fd5b82356130ce81612e35565b946020939093013593505050565b60008083601f8401126130ee57600080fd5b5081356001600160401b0381111561310557600080fd5b60208301915083602082850101111561270f57600080fd5b60008060006040848603121561313257600080fd5b83356001600160401b038082111561314957600080fd5b90850190610120828803121561315e57600080fd5b9093506020850135908082111561317457600080fd5b50613181868287016130dc565b9497909650939450505050565b6001600160801b03169052565b80516001600160a01b03908116835260208083015160a082860181905281519086018190526000939183019290849060c08801905b808310156131f2578551851682529483019460019290920191908301906131d0565b506040870151604089015260608701519450613211606089018661318e565b60808701519450613225608089018661318e565b979650505050505050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b8281101561328757603f1988860301845261327585835161319b565b94509285019290850190600101613259565b5092979650505050505050565b6000602082840312156132a657600080fd5b81356001600160401b038111156132bc57600080fd5b8201601f810184136132cd57600080fd5b6125f684823560208401612d8a565b600080602083850312156132ef57600080fd5b82356001600160401b0381111561330557600080fd5b61331185828601612fd3565b90969095509350505050565b60005b83811015613338578181015183820152602001613320565b50506000910152565b6000815180845261335981602086016020860161331d565b601f01601f19169290920160200192915050565b600060208083016020845280855180835260408601915060408160051b87010192506020870160005b8281101561328757603f198886030184526133b2858351613341565b94509285019290850190600101613396565b6001600160a01b0391909116815260200190565b600080600080606085870312156133ee57600080fd5b84356133f981612e35565b93506020850135925060408501356001600160401b0381111561341b57600080fd5b613427878288016130dc565b95989497509550505050565b60006001600160401b0382111561344c5761344c612d44565b5060051b60200190565b600082601f83011261346757600080fd5b8135602061347c61347783613433565b612d5a565b8083825260208201915060208460051b87010193508684111561349e57600080fd5b602086015b848110156134ba57803583529183019183016134a3565b509695505050505050565b600080600080600060a086880312156134dd57600080fd5b85356134e881612e35565b945060208601356134f881612e35565b935060408601356001600160401b038082111561351457600080fd5b61352089838a01613456565b9450606088013591508082111561353657600080fd5b61354289838a01613456565b9350608088013591508082111561355857600080fd5b5061356588828901612de1565b9150509295509295909350565b60008060006040848603121561358757600080fd5b833561359281612e35565b925060208401356001600160401b038111156135ad57600080fd5b613181868287016130dc565b602081526000610a9b6020830184613341565b6020808252825182820181905260009190848201906040850190845b8181101561360d5783516001600160a01b0316835292840192918401916001016135e8565b50909695505050505050565b602081526000610a9b602083018461319b565b600080600080600060a0868803121561364457600080fd5b853561364f81612e35565b9450602086013561365f81612e35565b9350604086013592506060860135915060808601356001600160401b0381111561368857600080fd5b61356588828901612de1565b6000808335601e198436030181126136ab57600080fd5b8301803591506001600160401b038211156136c557600080fd5b60200191503681900382131561270f57600080fd5b634e487b7160e01b600052603260045260246000fd5b60208082526021908201527f4163636f756e743a206e6f742061646d696e206f7220456e747279506f696e746040820152601760f91b606082015260800190565b6001600160a01b03929092168252602082015260400190565b80356001600160801b0381168114612e5557600080fd5b60006020828403121561377357600080fd5b610a9b8261374a565b803560ff81168114612e5557600080fd5b60006020828403121561379f57600080fd5b610a9b8261377c565b634e487b7160e01b600052601160045260246000fd5b8082018082111561059f5761059f6137a8565b6000808335601e198436030181126137e857600080fd5b8301803591506001600160401b0382111561380257600080fd5b6020019150600581901b360382131561270f57600080fd5b6000808335601e1984360301811261383157600080fd5b83016020810192503590506001600160401b0381111561385057600080fd5b8060051b360382131561270f57600080fd5b8183526000602080850194508260005b858110156138a057813561388581612e35565b6001600160a01b031687529582019590820190600101613872565b509495945050505050565b602081526138cc602082016138bf84612e4a565b6001600160a01b03169052565b60006138da6020840161377c565b60ff81166040840152506138f1604084018461381a565b61012080606086015261390961014086018385613862565b9250606086013560808601526139216080870161374a565b915061393060a086018361318e565b61393c60a0870161374a565b915061394b60c086018361318e565b61395760c0870161374a565b915061396660e086018361318e565b61397260e0870161374a565b91506101006139838187018461318e565b9590950135939094019290925250919050565b6000600182016139a8576139a86137a8565b5060010190565b6000602082840312156139c157600080fd5b5051919050565b600181811c908216806139dc57607f821691505b602082108103612f1e57634e487b7160e01b600052602260045260246000fd5b602080825260059082015264214461746160d81b604082015260600190565b60008085851115613a2b57600080fd5b83861115613a3857600080fd5b5050820193919092039150565b6001600160e01b03198135818116916004851015613a6d5780818660040360031b1b83161692505b505092915050565b600060208284031215613a8757600080fd5b5035919050565b600082601f830112613a9f57600080fd5b81356020613aaf61347783613433565b82815260059290921b84018101918181019086841115613ace57600080fd5b8286015b848110156134ba5780356001600160401b03811115613af15760008081fd5b613aff8986838b0101612de1565b845250918301918301613ad2565b600080600060608486031215613b2257600080fd5b83356001600160401b0380821115613b3957600080fd5b818601915086601f830112613b4d57600080fd5b81356020613b5d61347783613433565b82815260059290921b8401810191818101908a841115613b7c57600080fd5b948201945b83861015613ba3578535613b9481612e35565b82529482019490820190613b81565b97505087013592505080821115613bb957600080fd5b613bc587838801613456565b93506040860135915080821115613bdb57600080fd5b50613be886828701613a8e565b9150509250925092565b600060208284031215613c0457600080fd5b81518015158114610a9b57600080fd5b60008251613c2681846020870161331d565b9190910192915050565b601f821115613c78576000816000526020600020601f850160051c81016020861015613c595750805b601f850160051c820191505b81811015610cdc57828155600101613c65565b505050565b81516001600160401b03811115613c9657613c96612d44565b613caa81613ca484546139c8565b84613c30565b602080601f831160018114613cdf5760008415613cc75750858301515b600019600386901b1c1916600185901b178555610cdc565b600085815260208120601f198616915b82811015613d0e57888601518255948401946001909101908401613cef565b5085821015613d2c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b604081526000613d4f6040830185613341565b8281036020840152613d618185613341565b95945050505050565b60008184825b85811015613da1578135613d8381612e35565b6001600160a01b031683526020928301929190910190600101613d70565b509095945050505050565b6001600160a01b03831681526040602082018190526000906125f690830184613341565b634e487b7160e01b600052602160045260246000fd5b8181038181111561059f5761059f6137a8565b634e487b7160e01b600052603160045260246000fdfe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220d2173bb723866f0f6899074ddc9d72c37aa0ec712faafb4bb7aed6492cb13c8764736f6c63430008170033"; + diff --git a/src/test/smart-wallet/utils/AABenchmarkPrepare.sol b/src/test/smart-wallet/utils/AABenchmarkPrepare.sol new file mode 100644 index 000000000..146be270c --- /dev/null +++ b/src/test/smart-wallet/utils/AABenchmarkPrepare.sol @@ -0,0 +1,82 @@ +// 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(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789))) + ); + } + + 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 new file mode 100644 index 000000000..0ca4622b1 --- /dev/null +++ b/src/test/smart-wallet/utils/AABenchmarkTest.t.sol @@ -0,0 +1,44 @@ +// 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(UserOperation 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(UserOperation 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 new file mode 100644 index 000000000..66ecea693 --- /dev/null +++ b/src/test/smart-wallet/utils/AATestArtifacts.sol @@ -0,0 +1,9 @@ +pragma solidity ^0.8.0; + +bytes constant ENTRYPOINT_0_6_BYTECODE = hex"60806040526004361015610023575b361561001957600080fd5b610021615531565b005b60003560e01c80630396cb60146101b35780630bd28e3b146101aa5780631b2e01b8146101a15780631d732756146101985780631fad948c1461018f578063205c28781461018657806335567e1a1461017d5780634b1d7cf5146101745780635287ce121461016b57806370a08231146101625780638f41ec5a14610159578063957122ab146101505780639b249f6914610147578063a61935311461013e578063b760faf914610135578063bb9fe6bf1461012c578063c23a5cea14610123578063d6383f941461011a578063ee219423146101115763fc7e286d0361000e5761010c611bcd565b61000e565b5061010c6119b5565b5061010c61184d565b5061010c6116b4565b5061010c611536565b5061010c6114f7565b5061010c6114d6565b5061010c611337565b5061010c611164565b5061010c611129565b5061010c6110a4565b5061010c610f54565b5061010c610bf8565b5061010c610b33565b5061010c610994565b5061010c6108ba565b5061010c6106e7565b5061010c610467565b5061010c610385565b5060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595760043563ffffffff8116808203610359576103547fa5ae833d0bb1dcd632d98a8b70973e8516812898e19bf27b70071ebc8dc52c01916102716102413373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b9161024d811515615697565b61026a610261600185015463ffffffff1690565b63ffffffff1690565b11156156fc565b54926103366dffffffffffffffffffffffffffff946102f461029834888460781c166121d5565b966102a4881515615761565b6102b0818911156157c6565b6102d4816102bc6105ec565b941684906dffffffffffffffffffffffffffff169052565b6001602084015287166dffffffffffffffffffffffffffff166040830152565b63ffffffff83166060820152600060808201526103313373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b61582b565b6040805194855263ffffffff90911660208501523393918291820190565b0390a2005b600080fd5b6024359077ffffffffffffffffffffffffffffffffffffffffffffffff8216820361035957565b50346103595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595760043577ffffffffffffffffffffffffffffffffffffffffffffffff81168103610359576104149033600052600160205260406000209077ffffffffffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b61041e8154612491565b9055005b73ffffffffffffffffffffffffffffffffffffffff81160361035957565b6024359061044d82610422565b565b60c4359061044d82610422565b359061044d82610422565b50346103595760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595760206104fc6004356104a881610422565b73ffffffffffffffffffffffffffffffffffffffff6104c561035e565b91166000526001835260406000209077ffffffffffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b54604051908152f35b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60a0810190811067ffffffffffffffff82111761055157604052565b610559610505565b604052565b610100810190811067ffffffffffffffff82111761055157604052565b67ffffffffffffffff811161055157604052565b6060810190811067ffffffffffffffff82111761055157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761055157604052565b6040519061044d82610535565b6040519060c0820182811067ffffffffffffffff82111761055157604052565b604051906040820182811067ffffffffffffffff82111761055157604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209267ffffffffffffffff8111610675575b01160190565b61067d610505565b61066f565b92919261068e82610639565b9161069c60405193846105ab565b829481845281830111610359578281602093846000960137010152565b9181601f840112156103595782359167ffffffffffffffff8311610359576020838186019501011161035957565b5034610359576101c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595767ffffffffffffffff60043581811161035957366023820112156103595761074a903690602481600401359101610682565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc36016101808112610359576101006040519161078783610535565b12610359576040516107988161055e565b6107a0610440565b815260443560208201526064356040820152608435606082015260a43560808201526107ca61044f565b60a082015260e43560c08201526101043560e082015281526101243560208201526101443560408201526101643560608201526101843560808201526101a4359182116103595761083e9261082661082e9336906004016106b9565b9290916128b1565b6040519081529081906020820190565b0390f35b9060407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126103595760043567ffffffffffffffff9283821161035957806023830112156103595781600401359384116103595760248460051b830101116103595760240191906024356108b781610422565b90565b5034610359576108c936610842565b6108d4929192611e3a565b6108dd83611d2d565b60005b84811061095d57506000927fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f9728480a183915b85831061092d576109238585611ed7565b6100216001600255565b909193600190610953610941878987611dec565b61094b8886611dca565b51908861233f565b0194019190610912565b8061098b610984610972600194869896611dca565b5161097e848a88611dec565b84613448565b9083612f30565b019290926108e0565b50346103595760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610359576004356109d081610422565b6024359060009133835282602052604083206dffffffffffffffffffffffffffff81541692838311610ad557848373ffffffffffffffffffffffffffffffffffffffff829593610a788496610a3f610a2c8798610ad29c6121c0565b6dffffffffffffffffffffffffffff1690565b6dffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000825416179055565b6040805173ffffffffffffffffffffffffffffffffffffffff831681526020810185905233917fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb91a2165af1610acc611ea7565b50615ba2565b80f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f576974686472617720616d6f756e7420746f6f206c61726765000000000000006044820152fd5b50346103595760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610359576020600435610b7181610422565b73ffffffffffffffffffffffffffffffffffffffff610b8e61035e565b911660005260018252610bc98160406000209077ffffffffffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006040519260401b16178152f35b503461035957610c0736610842565b610c0f611e3a565b6000805b838210610df657610c249150611d2d565b7fbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972600080a16000805b848110610d5c57505060008093815b818110610c9357610923868660007f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d8180a2611ed7565b610cf7610ca182848a6124cb565b610ccc610cb3610cb36020840161256d565b73ffffffffffffffffffffffffffffffffffffffff1690565b7f575ff3acadd5ab348fe1855e217e0f3678f8d767d7494c9f9fefbee2e17cca4d600080a280612519565b906000915b808310610d1457505050610d0f90612491565b610c5c565b90919497610d4f610d49610d5592610d438c8b610d3c82610d368e8b8d611dec565b92611dca565b519161233f565b906121d5565b99612491565b95612491565b9190610cfc565b610d678186886124cb565b6020610d7f610d768380612519565b9290930161256d565b9173ffffffffffffffffffffffffffffffffffffffff60009316905b828410610db45750505050610daf90612491565b610c4d565b90919294610d4f81610de985610de2610dd0610dee968d611dca565b51610ddc8c8b8a611dec565b85613448565b908b613148565b612491565b929190610d9b565b610e018285876124cb565b90610e0c8280612519565b92610e1c610cb36020830161256d565b9173ffffffffffffffffffffffffffffffffffffffff8316610e416001821415612577565b610e62575b505050610e5c91610e56916121d5565b91612491565b90610c13565b909592610e7b6040999693999895989788810190611fc8565b92908a3b156103595789938b918a5193849283927fe3563a4f00000000000000000000000000000000000000000000000000000000845260049e8f850193610ec294612711565b03815a93600094fa9081610f3b575b50610f255786517f86a9f75000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8a16818a0190815281906020010390fd5b0390fd5b9497509295509093509181610e56610e5c610e46565b80610f48610f4e9261057b565b8061111e565b38610ed1565b50346103595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595761083e73ffffffffffffffffffffffffffffffffffffffff600435610fa881610422565b608060409283928351610fba81610535565b60009381858093528260208201528287820152826060820152015216815280602052209061104965ffffffffffff6001835194610ff686610535565b80546dffffffffffffffffffffffffffff8082168852607082901c60ff161515602089015260789190911c1685870152015463ffffffff8116606086015260201c16608084019065ffffffffffff169052565b5191829182919091608065ffffffffffff8160a08401956dffffffffffffffffffffffffffff808251168652602082015115156020870152604082015116604086015263ffffffff6060820151166060860152015116910152565b50346103595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595773ffffffffffffffffffffffffffffffffffffffff6004356110f581610422565b16600052600060205260206dffffffffffffffffffffffffffff60406000205416604051908152f35b600091031261035957565b50346103595760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035957602060405160018152f35b50346103595760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035957600467ffffffffffffffff8135818111610359576111b590369084016106b9565b9050602435916111c483610422565b604435908111610359576111db90369085016106b9565b92909115908161132d575b506112c6576014821015611236575b610f21836040519182917f08c379a0000000000000000000000000000000000000000000000000000000008352820160409060208152600060208201520190565b6112466112529261124c92612b88565b90612b96565b60601c90565b3b1561125f5738806111f5565b610f21906040519182917f08c379a0000000000000000000000000000000000000000000000000000000008352820160609060208152601b60208201527f41413330207061796d6173746572206e6f74206465706c6f796564000000000060408201520190565b610f21836040519182917f08c379a0000000000000000000000000000000000000000000000000000000008352820160609060208152601960208201527f41413230206163636f756e74206e6f74206465706c6f7965640000000000000060408201520190565b90503b15386111e6565b50346103595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595760043567ffffffffffffffff81116103595761138960249136906004016106b9565b906113bf6040519283927f570e1a3600000000000000000000000000000000000000000000000000000000845260048401612d2c565b0360208273ffffffffffffffffffffffffffffffffffffffff92816000857f0000000000000000000000007fc98430eaedbb6070b35b39d798725049088348165af1918215611471575b600092611441575b50604051917f6ca7b806000000000000000000000000000000000000000000000000000000008352166004820152fd5b61146391925060203d811161146a575b61145b81836105ab565b810190612d17565b9038611411565b503d611451565b611479612183565b611409565b90816101609103126103595790565b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc820112610359576004359067ffffffffffffffff8211610359576108b79160040161147e565b50346103595760206114ef6114ea3661148d565b612a0c565b604051908152f35b5060207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595761002160043561153181610422565b61562b565b5034610359576000807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126116b1573381528060205260408120600181019063ffffffff825416908115611653576115f06115b5611618936115a76115a2855460ff9060701c1690565b61598f565b65ffffffffffff42166159f4565b84547fffffffffffffffffffffffffffffffffffffffffffff000000000000ffffffff16602082901b69ffffffffffff000000001617909455565b7fffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffff8154169055565b60405165ffffffffffff91909116815233907ffa9b3c14cc825c412c9ed81b3ba365a5b459439403f18829e572ed53a4180f0a90602090a280f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f6e6f74207374616b6564000000000000000000000000000000000000000000006044820152fd5b80fd5b50346103595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610359576004356116f081610422565b610ad273ffffffffffffffffffffffffffffffffffffffff6117323373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b926117ea611755610a2c86546dffffffffffffffffffffffffffff9060781c1690565b94611761861515615a0e565b6117c26001820161179a65ffffffffffff611786835465ffffffffffff9060201c1690565b16611792811515615a73565b421015615ad8565b80547fffffffffffffffffffffffffffffffffffffffffffff00000000000000000000169055565b7fffffff0000000000000000000000000000ffffffffffffffffffffffffffffff8154169055565b6040805173ffffffffffffffffffffffffffffffffffffffff831681526020810186905233917fb7c918e0e249f999e965cafeb6c664271b3f4317d296461500e71da39f0cbda391a2600080809581948294165af1611847611ea7565b50615b3d565b50346103595760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595767ffffffffffffffff6004358181116103595761189e90369060040161147e565b602435916118ab83610422565b604435908111610359576118c6610f219136906004016106b9565b6118ce611caa565b6118d785612e2b565b6118ea6118e48287613240565b906153ba565b946118fa826000924384526121e2565b96438252819360609573ffffffffffffffffffffffffffffffffffffffff8316611981575b50505050608001519361194e6040611940602084015165ffffffffffff1690565b92015165ffffffffffff1690565b906040519687967f8b7ac980000000000000000000000000000000000000000000000000000000008852600488016127e1565b8395508394965061199b60409492939451809481936127d3565b03925af19060806119aa611ea7565b92919038808061191f565b5034610359576119c43661148d565b6119cc611caa565b6119d582612e2b565b6119df8183613240565b825160a00151919391611a0c9073ffffffffffffffffffffffffffffffffffffffff166154dc565b6154dc565b90611a30611a07855173ffffffffffffffffffffffffffffffffffffffff90511690565b94611a39612b50565b50611a68611a4c60409586810190611fc8565b90600060148310611bc55750611246611a079261124c92612b88565b91611a72916153ba565b805173ffffffffffffffffffffffffffffffffffffffff169073ffffffffffffffffffffffffffffffffffffffff821660018114916080880151978781015191886020820151611ac79065ffffffffffff1690565b91015165ffffffffffff16916060015192611ae06105f9565b9a8b5260208b0152841515898b015265ffffffffffff1660608a015265ffffffffffff16608089015260a088015215159081611bbc575b50611b515750610f2192519485947fe0cff05f00000000000000000000000000000000000000000000000000000000865260048601612cbd565b9190610f2193611b60846154dc565b611b87611b6b610619565b73ffffffffffffffffffffffffffffffffffffffff9096168652565b6020850152519586957ffaecb4e400000000000000000000000000000000000000000000000000000000875260048701612c2b565b90501538611b17565b9150506154dc565b50346103595760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103595773ffffffffffffffffffffffffffffffffffffffff600435611c1e81610422565b16600052600060205260a0604060002065ffffffffffff60018254920154604051926dffffffffffffffffffffffffffff90818116855260ff8160701c161515602086015260781c16604084015263ffffffff8116606084015260201c166080820152f35b60209067ffffffffffffffff8111611c9d575b60051b0190565b611ca5610505565b611c96565b60405190611cb782610535565b604051608083610100830167ffffffffffffffff811184821017611d20575b60405260009283815283602082015283604082015283606082015283838201528360a08201528360c08201528360e082015281528260208201528260408201528260608201520152565b611d28610505565b611cd6565b90611d3782611c83565b611d4460405191826105ab565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0611d728294611c83565b019060005b828110611d8357505050565b602090611d8e611caa565b82828501015201611d77565b507f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6020918151811015611ddf575b60051b010190565b611de7611d9a565b611dd7565b9190811015611e2d575b60051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea181360301821215610359570190565b611e35611d9a565b611df6565b6002805414611e495760028055565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152fd5b3d15611ed2573d90611eb882610639565b91611ec660405193846105ab565b82523d6000602084013e565b606090565b73ffffffffffffffffffffffffffffffffffffffff168015611f6a57600080809381935af1611f04611ea7565b5015611f0c57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f41413931206661696c65642073656e6420746f2062656e6566696369617279006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f4141393020696e76616c69642062656e656669636961727900000000000000006044820152fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610359570180359067ffffffffffffffff82116103595760200191813603831361035957565b90816020910312610359575190565b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938186528686013760008582860101520116010190565b60005b83811061207a5750506000910152565b818101518382015260200161206a565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f6020936120c681518092818752878088019101612067565b0116010190565b906120e76080916108b796946101c0808652850191612028565b9360e0815173ffffffffffffffffffffffffffffffffffffffff80825116602087015260208201516040870152604082015160608701526060820151858701528482015160a087015260a08201511660c086015260c081015182860152015161010084015260208101516101208401526040810151610140840152606081015161016084015201516101808201526101a081840391015261208a565b506040513d6000823e3d90fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b919082039182116121cd57565b61044d612190565b919082018092116121cd57565b905a918160206121fb6060830151936060810190611fc8565b906122348560405195869485947f1d732756000000000000000000000000000000000000000000000000000000008652600486016120cd565b03816000305af16000918161230f575b50612308575060206000803e7fdeaddead000000000000000000000000000000000000000000000000000000006000511461229b5761229561228a6108b7945a906121c0565b6080840151906121d5565b91614afc565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152600f60408201527f41413935206f7574206f6620676173000000000000000000000000000000000060608201520190565b9250505090565b61233191925060203d8111612338575b61232981836105ab565b810190612019565b9038612244565b503d61231f565b909291925a9380602061235b6060830151946060810190611fc8565b906123948660405195869485947f1d732756000000000000000000000000000000000000000000000000000000008652600486016120cd565b03816000305af160009181612471575b5061246a575060206000803e7fdeaddead00000000000000000000000000000000000000000000000000000000600051146123fc576123f66123eb6108b795965a906121c0565b6080830151906121d5565b92614ddf565b610f21836040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152600f60408201527f41413935206f7574206f6620676173000000000000000000000000000000000060608201520190565b9450505050565b61248a91925060203d81116123385761232981836105ab565b90386123a4565b6001907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146124bf570190565b6124c7612190565b0190565b919081101561250c575b60051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa181360301821215610359570190565b612514611d9a565b6124d5565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610359570180359067ffffffffffffffff821161035957602001918160051b3603831361035957565b356108b781610422565b1561257e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4141393620696e76616c69642061676772656761746f720000000000000000006044820152fd5b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561035957016020813591019167ffffffffffffffff821161035957813603831361035957565b6108b7916126578161263d8461045c565b73ffffffffffffffffffffffffffffffffffffffff169052565b602082013560208201526126f26126a361268861267760408601866125dc565b610160806040880152860191612028565b61269560608601866125dc565b908583036060870152612028565b6080840135608084015260a084013560a084015260c084013560c084015260e084013560e084015261010080850135908401526101206126e5818601866125dc565b9185840390860152612028565b9161270361014091828101906125dc565b929091818503910152612028565b949391929083604087016040885252606086019360608160051b8801019482600090815b848310612754575050505050508460206108b795968503910152612028565b9091929394977fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08b820301855288357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea1843603018112156127cf57600191846127bd920161262c565b98602090810196950193019190612735565b8280fd5b908092918237016000815290565b9290936108b796959260c0958552602085015265ffffffffffff8092166040850152166060830152151560808201528160a0820152019061208a565b1561282457565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4141393220696e7465726e616c2063616c6c206f6e6c790000000000000000006044820152fd5b9060406108b79260008152816020820152019061208a565b6040906108b793928152816020820152019061208a565b909291925a936128c230331461281d565b8151946040860151955a6113886060830151890101116129e2576108b7966000958051612909575b50505090612903915a9003608084015101943691610682565b91615047565b612938916129349161292f855173ffffffffffffffffffffffffffffffffffffffff1690565b615c12565b1590565b612944575b80806128ea565b61290392919450612953615c24565b908151612967575b5050600193909161293d565b7f1c4fada7374c0a9ee8841fc38afe82932dc0f8e69012e927f061a8bae611a20173ffffffffffffffffffffffffffffffffffffffff6020870151926129d860206129c6835173ffffffffffffffffffffffffffffffffffffffff1690565b9201519560405193849316968361289a565b0390a3388061295b565b7fdeaddead0000000000000000000000000000000000000000000000000000000060005260206000fd5b612a22612a1c6040830183611fc8565b90615c07565b90612a33612a1c6060830183611fc8565b90612ae9612a48612a1c610120840184611fc8565b60405194859360208501956101008201359260e08301359260c08101359260a08201359260808301359273ffffffffffffffffffffffffffffffffffffffff60208201359135168c9693909a9998959261012098959273ffffffffffffffffffffffffffffffffffffffff6101408a019d168952602089015260408801526060870152608086015260a085015260c084015260e08301526101008201520152565b0391612b1b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0938481018352826105ab565b51902060408051602081019283523091810191909152466060820152608092830181529091612b4a90826105ab565b51902090565b604051906040820182811067ffffffffffffffff821117612b7b575b60405260006020838281520152565b612b83610505565b612b6c565b906014116103595790601490565b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009035818116939260148110612bcb57505050565b60140360031b82901b16169150565b9060c060a06108b793805184526020810151602085015260408101511515604085015265ffffffffffff80606083015116606086015260808201511660808501520151918160a0820152019061208a565b9294612c8c61044d95612c7a610100959998612c68612c54602097610140808c528b0190612bda565b9b878a019060208091805184520151910152565b80516060890152602001516080880152565b805160a08701526020015160c0860152565b73ffffffffffffffffffffffffffffffffffffffff81511660e0850152015191019060208091805184520151910152565b612d0661044d94612cf4612cdf60a0959998969960e0865260e0860190612bda565b98602085019060208091805184520151910152565b80516060840152602001516080830152565b019060208091805184520151910152565b9081602091031261035957516108b781610422565b9160206108b7938181520191612028565b90612d6c73ffffffffffffffffffffffffffffffffffffffff916108b797959694606085526060850191612028565b941660208201526040818503910152612028565b60009060033d11612d8d57565b905060046000803e60005160e01c90565b600060443d106108b7576040517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc91823d016004833e815167ffffffffffffffff918282113d602484011117612e1a57818401948551938411612e22573d85010160208487010111612e1a57506108b7929101602001906105ab565b949350505050565b50949350505050565b612e386040820182611fc8565b612e50612e448461256d565b93610120810190611fc8565b9290303b1561035957600093612e949160405196879586957f957122ab00000000000000000000000000000000000000000000000000000000875260048701612d3d565b0381305afa9081612f1d575b5061044d576001612eaf612d80565b6308c379a014612ec8575b612ec057565b61044d612183565b612ed0612d9e565b80612edc575b50612eba565b80516000925015612ed657610f21906040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301612882565b80610f48612f2a9261057b565b38612ea0565b9190612f3b9061317f565b73ffffffffffffffffffffffffffffffffffffffff929183166130da5761306c57612f659061317f565b9116612ffe57612f725750565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152602160448201527f41413332207061796d61737465722065787069726564206f72206e6f7420647560648201527f6500000000000000000000000000000000000000000000000000000000000000608482015260a490fd5b610f21826040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601460408201527f41413334207369676e6174757265206572726f7200000000000000000000000060608201520190565b610f21836040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601760408201527f414132322065787069726564206f72206e6f742064756500000000000000000060608201520190565b610f21846040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601460408201527f41413234207369676e6174757265206572726f7200000000000000000000000060608201520190565b9291906131549061317f565b909273ffffffffffffffffffffffffffffffffffffffff808095169116036130da5761306c57612f65905b80156131d25761318e9061535f565b73ffffffffffffffffffffffffffffffffffffffff65ffffffffffff8060408401511642119081156131c2575b5091511691565b90506020830151164210386131bb565b50600090600090565b156131e257565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f41413934206761732076616c756573206f766572666c6f7700000000000000006044820152fd5b916000915a9381519061325382826136b3565b61325c81612a0c565b602084015261329a6effffffffffffffffffffffffffffff60808401516060850151176040850151176101008401359060e0850135171711156131db565b6132a382613775565b6132ae818584613836565b97906132df6129346132d4875173ffffffffffffffffffffffffffffffffffffffff1690565b60208801519061546c565b6133db576132ec43600052565b73ffffffffffffffffffffffffffffffffffffffff61332460a0606097015173ffffffffffffffffffffffffffffffffffffffff1690565b166133c1575b505a810360a0840135106133545760809360c092604087015260608601525a900391013501910152565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601e60408201527f41413430206f76657220766572696669636174696f6e4761734c696d6974000060608201520190565b909350816133d2929750858461455c565b9590923861332a565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601a60408201527f4141323520696e76616c6964206163636f756e74206e6f6e636500000000000060608201520190565b9290916000925a825161345b81846136b3565b61346483612a0c565b60208501526134a26effffffffffffffffffffffffffffff60808301516060840151176040840151176101008601359060e0870135171711156131db565b6134ab81613775565b6134b78186868b613ba2565b98906134e86129346134dd865173ffffffffffffffffffffffffffffffffffffffff1690565b60208701519061546c565b6135e0576134f543600052565b73ffffffffffffffffffffffffffffffffffffffff61352d60a0606096015173ffffffffffffffffffffffffffffffffffffffff1690565b166135c5575b505a840360a08601351061355f5750604085015260608401526080919060c0905a900391013501910152565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152601e60448201527f41413430206f76657220766572696669636174696f6e4761734c696d697400006064820152608490fd5b909250816135d79298508686856147ef565b96909138613533565b610f21826040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601a60408201527f4141323520696e76616c6964206163636f756e74206e6f6e636500000000000060608201520190565b1561365557565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4141393320696e76616c6964207061796d6173746572416e64446174610000006044820152fd5b613725906136dd6136c38261256d565b73ffffffffffffffffffffffffffffffffffffffff168452565b602081013560208401526080810135604084015260a0810135606084015260c0810135608084015260e081013560c084015261010081013560e0840152610120810190611fc8565b90811561376a5761374f61124c6112468460a09461374a601461044d9998101561364e565b612b88565b73ffffffffffffffffffffffffffffffffffffffff16910152565b505060a06000910152565b60a081015173ffffffffffffffffffffffffffffffffffffffff16156137b75760c060035b60ff60408401519116606084015102016080830151019101510290565b60c0600161379a565b6137d86040929594939560608352606083019061262c565b9460208201520152565b9061044d602f60405180947f414132332072657665727465643a20000000000000000000000000000000000060208301526138268151809260208686019101612067565b810103600f8101855201836105ab565b916000926000925a936139046020835193613865855173ffffffffffffffffffffffffffffffffffffffff1690565b9561387d6138766040830183611fc8565b9084613e0d565b60a086015173ffffffffffffffffffffffffffffffffffffffff16906138a243600052565b85809373ffffffffffffffffffffffffffffffffffffffff809416159889613b3a575b60600151908601516040517f3a871cdd0000000000000000000000000000000000000000000000000000000081529788968795869390600485016137c0565b03938a1690f1829181613b1a575b50613b115750600190613923612d80565b6308c379a014613abd575b50613a50575b613941575b50505a900391565b61396b9073ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b613986610a2c82546dffffffffffffffffffffffffffff1690565b8083116139e3576139dc926dffffffffffffffffffffffffffff9103166dffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000825416179055565b3880613939565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601760408201527f41413231206469646e2774207061792070726566756e6400000000000000000060608201520190565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601660408201527f4141323320726576657274656420286f72204f4f47290000000000000000000060608201520190565b613ac5612d9e565b9081613ad1575061392e565b610f2191613adf91506137e2565b6040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301612882565b95506139349050565b613b3391925060203d81116123385761232981836105ab565b9038613912565b9450613b80610a2c613b6c8c73ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b546dffffffffffffffffffffffffffff1690565b8b811115613b975750856060835b969150506138c5565b606087918d03613b8e565b90926000936000935a94613beb6020835193613bd2855173ffffffffffffffffffffffffffffffffffffffff1690565b9561387d613be36040830183611fc8565b90848c61412b565b03938a1690f1829181613ded575b50613de45750600190613c0a612d80565b6308c379a014613d8e575b50613d20575b613c29575b5050505a900391565b613c539073ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b91613c6f610a2c84546dffffffffffffffffffffffffffff1690565b90818311613cba575082547fffffffffffffffffffffffffffffffffffff0000000000000000000000000000169190036dffffffffffffffffffffffffffff16179055388080613c20565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152601760448201527f41413231206469646e2774207061792070726566756e640000000000000000006064820152608490fd5b610f21846040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601660408201527f4141323320726576657274656420286f72204f4f47290000000000000000000060608201520190565b613d96612d9e565b9081613da25750613c15565b8691613dae91506137e2565b90610f216040519283927f220266b60000000000000000000000000000000000000000000000000000000084526004840161289a565b9650613c1b9050565b613e0691925060203d81116123385761232981836105ab565b9038613bf9565b909180613e1957505050565b81515173ffffffffffffffffffffffffffffffffffffffff1692833b6140be57606083510151604051907f570e1a3600000000000000000000000000000000000000000000000000000000825260208280613e78878760048401612d2c565b0381600073ffffffffffffffffffffffffffffffffffffffff95867f0000000000000000000000007fc98430eaedbb6070b35b39d7987250490883481690f19182156140b1575b600092614091575b508082169586156140245716809503613fb7573b15613f4a5761124c6112467fd51a9c61267aa6196961883ecf5ff2da6619c37dac0fa92122513fb32c032d2d93613f1193612b88565b602083810151935160a001516040805173ffffffffffffffffffffffffffffffffffffffff9485168152939091169183019190915290a3565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152602060408201527f4141313520696e6974436f6465206d757374206372656174652073656e64657260608201520190565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152602060408201527f4141313420696e6974436f6465206d7573742072657475726e2073656e64657260608201520190565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601b60408201527f4141313320696e6974436f6465206661696c6564206f72204f4f47000000000060608201520190565b6140aa91925060203d811161146a5761145b81836105ab565b9038613ec7565b6140b9612183565b613ebf565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601f60408201527f414131302073656e64657220616c726561647920636f6e73747275637465640060608201520190565b9290918161413a575b50505050565b82515173ffffffffffffffffffffffffffffffffffffffff1693843b6143e257606084510151604051907f570e1a3600000000000000000000000000000000000000000000000000000000825260208280614199888860048401612d2c565b0381600073ffffffffffffffffffffffffffffffffffffffff95867f0000000000000000000000007fc98430eaedbb6070b35b39d7987250490883481690f19182156143d5575b6000926143b5575b5080821696871561434757168096036142d9573b15614273575061124c6112467fd51a9c61267aa6196961883ecf5ff2da6619c37dac0fa92122513fb32c032d2d9361423393612b88565b602083810151935160a001516040805173ffffffffffffffffffffffffffffffffffffffff9485168152939091169183019190915290a338808080614134565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152602060448201527f4141313520696e6974436f6465206d757374206372656174652073656e6465726064820152608490fd5b610f21826040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152602060408201527f4141313420696e6974436f6465206d7573742072657475726e2073656e64657260608201520190565b610f21846040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601b60408201527f4141313320696e6974436f6465206661696c6564206f72204f4f47000000000060608201520190565b6143ce91925060203d811161146a5761145b81836105ab565b90386141e8565b6143dd612183565b6141e0565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152601f60448201527f414131302073656e64657220616c726561647920636f6e7374727563746564006064820152608490fd5b1561444f57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f4141343120746f6f206c6974746c6520766572696669636174696f6e476173006044820152fd5b919060408382031261035957825167ffffffffffffffff81116103595783019080601f83011215610359578151916144e483610639565b916144f260405193846105ab565b838352602084830101116103595760209261451291848085019101612067565b92015190565b9061044d602f60405180947f414133332072657665727465643a20000000000000000000000000000000000060208301526138268151809260208686019101612067565b93919260609460009460009380519261459b60a08a86015195614580888811614448565b015173ffffffffffffffffffffffffffffffffffffffff1690565b916145c68373ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b946145e2610a2c87546dffffffffffffffffffffffffffff1690565b968588106147825773ffffffffffffffffffffffffffffffffffffffff60208a98946146588a966dffffffffffffffffffffffffffff8b6146919e03166dffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000825416179055565b015194604051998a98899788937ff465c77e000000000000000000000000000000000000000000000000000000008552600485016137c0565b0395169103f190818391849361475c575b506147555750506001906146b4612d80565b6308c379a014614733575b506146c657565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601660408201527f4141333320726576657274656420286f72204f4f47290000000000000000000060608201520190565b61473b612d9e565b908161474757506146bf565b610f2191613adf9150614518565b9450925050565b90925061477b91503d8085833e61477381836105ab565b8101906144ad565b91386146a2565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601e60408201527f41413331207061796d6173746572206465706f73697420746f6f206c6f77000060608201520190565b91949293909360609560009560009382519061481660a08b84015193614580848611614448565b936148418573ffffffffffffffffffffffffffffffffffffffff166000526000602052604060002090565b61485c610a2c82546dffffffffffffffffffffffffffff1690565b8781106149b7579273ffffffffffffffffffffffffffffffffffffffff60208a989693946146588a966dffffffffffffffffffffffffffff8d6148d69e9c9a03166dffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000825416179055565b0395169103f1908183918493614999575b506149915750506001906148f9612d80565b6308c379a014614972575b5061490c5750565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152601660448201527f4141333320726576657274656420286f72204f4f4729000000000000000000006064820152608490fd5b61497a612d9e565b90816149865750614904565b613dae925050614518565b955093505050565b9092506149b091503d8085833e61477381836105ab565b91386148e7565b610f218a6040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601e60408201527f41413331207061796d6173746572206465706f73697420746f6f206c6f77000060608201520190565b60031115614a2f57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b929190614a7c6040916002865260606020870152606086019061208a565b930152565b939291906003811015614a2f57604091614a7c91865260606020870152606086019061208a565b9061044d603660405180947f4141353020706f73744f702072657665727465643a20000000000000000000006020830152614aec8151809260208686019101612067565b81010360168101855201836105ab565b929190925a93600091805191614b1183615318565b9260a0810195614b35875173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff93908481169081614ca457505050614b76825173ffffffffffffffffffffffffffffffffffffffff1690565b985b5a90030193840297604084019089825110614c37577f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f94614bc26020928c614c329551039061553a565b015194896020614c04614be9865173ffffffffffffffffffffffffffffffffffffffff1690565b9a5173ffffffffffffffffffffffffffffffffffffffff1690565b9401519785604051968796169a16988590949392606092608083019683521515602083015260408201520152565b0390a4565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152602060408201527f414135312070726566756e642062656c6f772061637475616c476173436f737460608201520190565b9a918051614cb4575b5050614b78565b6060850151600099509091803b15614ddb579189918983614d07956040518097819682957fa9a234090000000000000000000000000000000000000000000000000000000084528c029060048401614a5e565b0393f19081614dc8575b50614dc3576001614d20612d80565b6308c379a014614da4575b614d37575b3880614cad565b6040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601260408201527f4141353020706f73744f7020726576657274000000000000000000000000000060608201520190565b614dac612d9e565b80614db75750614d2b565b613adf610f2191614aa8565b614d30565b80610f48614dd59261057b565b38614d11565b8980fd5b9392915a90600092805190614df382615318565b9360a0830196614e17885173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff95908681169081614f0d57505050614e58845173ffffffffffffffffffffffffffffffffffffffff1690565b915b5a9003019485029860408301908a825110614ea757507f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f949392614bc2614c32938c60209451039061553a565b604080517f220266b600000000000000000000000000000000000000000000000000000000815260048101929092526024820152602060448201527f414135312070726566756e642062656c6f772061637475616c476173436f73746064820152608490fd5b93918051614f1d575b5050614e5a565b606087015160009a509091803b1561504357918a918a83614f70956040518097819682957fa9a234090000000000000000000000000000000000000000000000000000000084528c029060048401614a5e565b0393f19081615030575b5061502b576001614f89612d80565b6308c379a01461500e575b614fa0575b3880614f16565b610f218b6040519182917f220266b600000000000000000000000000000000000000000000000000000000835260048301608091815260406020820152601260408201527f4141353020706f73744f7020726576657274000000000000000000000000000060608201520190565b615016612d9e565b806150215750614f94565b613dae8d91614aa8565b614f99565b80610f4861503d9261057b565b38614f7a565b8a80fd5b909392915a9480519161505983615318565b9260a081019561507d875173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff938185169182615165575050506150bd825173ffffffffffffffffffffffffffffffffffffffff1690565b985b5a90030193840297604084019089825110614c37577f49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f946151096020928c614c329551039061553a565b61511288614a25565b015194896020615139614be9865173ffffffffffffffffffffffffffffffffffffffff1690565b940151604080519182529815602082015297880152606087015290821695909116939081906080820190565b9a918151615175575b50506150bf565b8784026151818a614a25565b60028a1461520c576060860151823b15610359576151d493600080948d604051978896879586937fa9a2340900000000000000000000000000000000000000000000000000000000855260048501614a81565b0393f180156151ff575b6151ec575b505b388061516e565b80610f486151f99261057b565b386151e3565b615207612183565b6151de565b6060860151823b156103595761525793600080948d604051978896879586937fa9a2340900000000000000000000000000000000000000000000000000000000855260048501614a81565b0393f19081615305575b50615300576001615270612d80565b6308c379a0146152ed575b156151e5576040517f220266b600000000000000000000000000000000000000000000000000000000815280610f21600482016080906000815260406020820152601260408201527f4141353020706f73744f7020726576657274000000000000000000000000000060608201520190565b6152f5612d9e565b80614db7575061527b565b6151e5565b80610f486153129261057b565b38615261565b60e060c082015191015180821461533c57480180821015615337575090565b905090565b5090565b6040519061534d8261058f565b60006040838281528260208201520152565b615367615340565b5065ffffffffffff808260a01c1680156153b3575b604051926153898461058f565b73ffffffffffffffffffffffffffffffffffffffff8116845260d01c602084015216604082015290565b508061537c565b6153cf6153d5916153c9615340565b5061535f565b9161535f565b9073ffffffffffffffffffffffffffffffffffffffff9182825116928315615461575b65ffffffffffff928391826040816020850151169301511693836040816020840151169201511690808410615459575b50808511615451575b506040519561543f8761058f565b16855216602084015216604082015290565b935038615431565b925038615428565b8151811693506153f8565b73ffffffffffffffffffffffffffffffffffffffff16600052600160205267ffffffffffffffff6154c88260401c60406000209077ffffffffffffffffffffffffffffffffffffffffffffffff16600052602052604060002090565b918254926154d584612491565b9055161490565b9073ffffffffffffffffffffffffffffffffffffffff6154fa612b50565b9216600052600060205263ffffffff600160406000206dffffffffffffffffffffffffffff815460781c1685520154166020830152565b61044d3361562b565b73ffffffffffffffffffffffffffffffffffffffff16600052600060205260406000206dffffffffffffffffffffffffffff8082541692830180931161561e575b8083116155c05761044d92166dffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000825416179055565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f6465706f736974206f766572666c6f77000000000000000000000000000000006044820152fd5b615626612190565b61557b565b73ffffffffffffffffffffffffffffffffffffffff9061564b348261553a565b168060005260006020527f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c460206dffffffffffffffffffffffffffff60406000205416604051908152a2565b1561569e57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f6d757374207370656369667920756e7374616b652064656c61790000000000006044820152fd5b1561570357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f63616e6e6f7420646563726561736520756e7374616b652074696d65000000006044820152fd5b1561576857565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6e6f207374616b652073706563696669656400000000000000000000000000006044820152fd5b156157cd57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f7374616b65206f766572666c6f770000000000000000000000000000000000006044820152fd5b9065ffffffffffff6080600161044d9461588b6dffffffffffffffffffffffffffff86511682906dffffffffffffffffffffffffffff167fffffffffffffffffffffffffffffffffffff0000000000000000000000000000825416179055565b602085015115156eff000000000000000000000000000082549160701b16807fffffffffffffffffffffffffffffffffff00ffffffffffffffffffffffffffff83161783557fffffff000000000000000000000000000000ffffffffffffffffffffffffffff7cffffffffffffffffffffffffffff000000000000000000000000000000604089015160781b16921617178155019263ffffffff6060820151167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000008554161784550151167fffffffffffffffffffffffffffffffffffffffffffff000000000000ffffffff69ffffffffffff0000000083549260201b169116179055565b1561599657565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f616c726561647920756e7374616b696e670000000000000000000000000000006044820152fd5b91909165ffffffffffff808094169116019182116121cd57565b15615a1557565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f4e6f207374616b6520746f2077697468647261770000000000000000000000006044820152fd5b15615a7a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f6d7573742063616c6c20756e6c6f636b5374616b6528292066697273740000006044820152fd5b15615adf57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f5374616b65207769746864726177616c206973206e6f742064756500000000006044820152fd5b15615b4457565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f6661696c656420746f207769746864726177207374616b6500000000000000006044820152fd5b15615ba957565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6661696c656420746f20776974686472617700000000000000000000000000006044820152fd5b816040519182372090565b9060009283809360208451940192f190565b3d610800808211615c4b575b50604051906020818301016040528082526000602083013e90565b905038615c3056fea2646970667358221220a706d8b02d7086d80e9330811f5af84b2614abdc5e9a1f2260126070a31d7cee64736f6c63430008110033"; + +bytes constant CREATOR_0_6_BYTECODE = hex"6080604052600436101561001257600080fd5b6000803560e01c63570e1a361461002857600080fd5b346100c95760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100c95760043567ffffffffffffffff918282116100c957366023830112156100c95781600401359283116100c95736602484840101116100c9576100c561009e84602485016100fc565b60405173ffffffffffffffffffffffffffffffffffffffff90911681529081906020820190565b0390f35b80fd5b507f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90806014116101bb5767ffffffffffffffff917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec82018381116101cd575b604051937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0603f81600b8701160116850190858210908211176101c0575b604052808452602084019036848401116101bb576020946000600c819682946014880187378301015251923560601c5af19060005191156101b557565b60009150565b600080fd5b6101c86100cc565b610178565b6101d56100cc565b61013a56fea26469706673582212201927e80b76ab9b71c952137dd676621a9fdf520c25928815636594036eb1c40364736f6c63430008110033"; + +bytes constant VERIFYINGPAYMASTER_BYTECODE = hex"6080604052600436106100e85760003560e01c8063a9a234091161008a578063c399ec8811610059578063c399ec881461028d578063d0e30db0146102a2578063f2fde38b146102aa578063f465c77e146102ca57600080fd5b8063a9a2340914610204578063b0d691fe14610224578063bb9fe6bf14610258578063c23a5cea1461026d57600080fd5b8063715018a6116100c6578063715018a6146101735780638da5cb5b1461018857806394d4ad60146101a657806394e1fc19146101d657600080fd5b80630396cb60146100ed578063205c28781461010257806323d9ac9b14610122575b600080fd5b6101006100fb366004610dc7565b6102f8565b005b34801561010e57600080fd5b5061010061011d366004610e09565b610383565b34801561012e57600080fd5b506101567f000000000000000000000000074bd8c57fe6f33ec5b580c92401f4feafc4443381565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561017f57600080fd5b506101006103f5565b34801561019457600080fd5b506000546001600160a01b0316610156565b3480156101b257600080fd5b506101c66101c1366004610e77565b610409565b60405161016a9493929190610eb9565b3480156101e257600080fd5b506101f66101f1366004610f39565b610446565b60405190815260200161016a565b34801561021057600080fd5b5061010061021f366004610f97565b610542565b34801561023057600080fd5b506101567f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d278981565b34801561026457600080fd5b5061010061055c565b34801561027957600080fd5b50610100610288366004610ff7565b6105d3565b34801561029957600080fd5b506101f6610659565b6101006106e9565b3480156102b657600080fd5b506101006102c5366004610ff7565b61074b565b3480156102d657600080fd5b506102ea6102e5366004611014565b6107c9565b60405161016a929190611062565b6103006107ed565b604051621cb65b60e51b815263ffffffff821660048201527f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27896001600160a01b031690630396cb609034906024016000604051808303818588803b15801561036757600080fd5b505af115801561037b573d6000803e3d6000fd5b505050505050565b61038b6107ed565b60405163040b850f60e31b81526001600160a01b038381166004830152602482018390527f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789169063205c287890604401600060405180830381600087803b15801561036757600080fd5b6103fd6107ed565b6104076000610847565b565b600080368161041c6054601487896110b7565b81019061042991906110e1565b909450925061043b85605481896110b7565b949793965094505050565b6000833580602086013561045d6040880188611114565b60405161046b92919061115b565b6040519081900390206104816060890189611114565b60405161048f92919061115b565b604080519182900382206001600160a01b03909516602083015281019290925260608201526080808201929092529086013560a08083019190915286013560c08083019190915286013560e08083019190915286013561010080830191909152860135610120820152466101408201523061016082015265ffffffffffff80861661018083015284166101a08201526101c001604051602081830303815290604052805190602001209150509392505050565b61054a610897565b61055684848484610907565b50505050565b6105646107ed565b7f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27896001600160a01b031663bb9fe6bf6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156105bf57600080fd5b505af1158015610556573d6000803e3d6000fd5b6105db6107ed565b60405163611d2e7560e11b81526001600160a01b0382811660048301527f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d2789169063c23a5cea90602401600060405180830381600087803b15801561063e57600080fd5b505af1158015610652573d6000803e3d6000fd5b5050505050565b6040516370a0823160e01b81523060048201526000907f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27896001600160a01b0316906370a0823190602401602060405180830381865afa1580156106c0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106e4919061116b565b905090565b60405163b760faf960e01b81523060048201527f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d27896001600160a01b03169063b760faf99034906024016000604051808303818588803b15801561063e57600080fd5b6107536107ed565b6001600160a01b0381166107bd5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b6107c681610847565b50565b606060006107d5610897565b6107e085858561093f565b915091505b935093915050565b6000546001600160a01b031633146104075760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016107b4565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b336001600160a01b037f0000000000000000000000005ff137d4b0fdcd49dca30c7cf57e578a026d278916146104075760405162461bcd60e51b815260206004820152601560248201527414d95b99195c881b9bdd08115b9d1c9e541bda5b9d605a1b60448201526064016107b4565b60405162461bcd60e51b815260206004820152600d60248201526c6d757374206f7665727269646560981b60448201526064016107b4565b60606000808036816109586101c16101208b018b611114565b9296509094509250905060408114806109715750604181145b6109e5576040805162461bcd60e51b81526020600482015260248101919091527f566572696679696e675061796d61737465723a20696e76616c6964207369676e60448201527f6174757265206c656e67746820696e207061796d6173746572416e644461746160648201526084016107b4565b6000610a486109f58b8787610446565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c8101829052600090605c01604051602081830303815290604052805190602001209050919050565b9050610a8a8184848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610b1892505050565b6001600160a01b03167f000000000000000000000000074bd8c57fe6f33ec5b580c92401f4feafc444336001600160a01b031614610aed57610ace60018686610b3c565b60405180602001604052806000815250909650965050505050506107e5565b610af960008686610b3c565b6040805160208101909152600081529b909a5098505050505050505050565b6000806000610b278585610b74565b91509150610b3481610bb9565b509392505050565b600060d08265ffffffffffff16901b60a08465ffffffffffff16901b85610b64576000610b67565b60015b60ff161717949350505050565b6000808251604103610baa5760208301516040840151606085015160001a610b9e87828585610d03565b94509450505050610bb2565b506000905060025b9250929050565b6000816004811115610bcd57610bcd611184565b03610bd55750565b6001816004811115610be957610be9611184565b03610c365760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016107b4565b6002816004811115610c4a57610c4a611184565b03610c975760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016107b4565b6003816004811115610cab57610cab611184565b036107c65760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016107b4565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115610d3a5750600090506003610dbe565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015610d8e573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116610db757600060019250925050610dbe565b9150600090505b94509492505050565b600060208284031215610dd957600080fd5b813563ffffffff81168114610ded57600080fd5b9392505050565b6001600160a01b03811681146107c657600080fd5b60008060408385031215610e1c57600080fd5b8235610e2781610df4565b946020939093013593505050565b60008083601f840112610e4757600080fd5b50813567ffffffffffffffff811115610e5f57600080fd5b602083019150836020828501011115610bb257600080fd5b60008060208385031215610e8a57600080fd5b823567ffffffffffffffff811115610ea157600080fd5b610ead85828601610e35565b90969095509350505050565b600065ffffffffffff808716835280861660208401525060606040830152826060830152828460808401376000608084840101526080601f19601f850116830101905095945050505050565b60006101608284031215610f1857600080fd5b50919050565b803565ffffffffffff81168114610f3457600080fd5b919050565b600080600060608486031215610f4e57600080fd5b833567ffffffffffffffff811115610f6557600080fd5b610f7186828701610f05565b935050610f8060208501610f1e565b9150610f8e60408501610f1e565b90509250925092565b60008060008060608587031215610fad57600080fd5b843560038110610fbc57600080fd5b9350602085013567ffffffffffffffff811115610fd857600080fd5b610fe487828801610e35565b9598909750949560400135949350505050565b60006020828403121561100957600080fd5b8135610ded81610df4565b60008060006060848603121561102957600080fd5b833567ffffffffffffffff81111561104057600080fd5b61104c86828701610f05565b9660208601359650604090950135949350505050565b604081526000835180604084015260005b818110156110905760208187018101516060868401015201611073565b506000606082850101526060601f19601f8301168401019150508260208301529392505050565b600080858511156110c757600080fd5b838611156110d457600080fd5b5050820193919092039150565b600080604083850312156110f457600080fd5b6110fd83610f1e565b915061110b60208401610f1e565b90509250929050565b6000808335601e1984360301811261112b57600080fd5b83018035915067ffffffffffffffff82111561114657600080fd5b602001915036819003821315610bb257600080fd5b8183823760009101908152919050565b60006020828403121561117d57600080fd5b5051919050565b634e487b7160e01b600052602160045260246000fd"; + +address constant VERIFYINGPAYMASTER_ADDRESS = 0xe1Fb85Ec54767ED89252751F6667CF566b16f1F0; diff --git a/src/test/smart-wallet/utils/AATestBase.sol b/src/test/smart-wallet/utils/AATestBase.sol new file mode 100644 index 000000000..74947587f --- /dev/null +++ b/src/test/smart-wallet/utils/AATestBase.sol @@ -0,0 +1,277 @@ +pragma solidity ^0.8.0; + +import { IEntryPoint } from "contracts/prebuilts/account/interface/IEntrypoint.sol"; +import { UserOperation } from "contracts/prebuilts/account/utils/UserOperation.sol"; +import { IAccount } from "contracts/prebuilts/account/interface/IAccount.sol"; +import { VERIFYINGPAYMASTER_BYTECODE, VERIFYINGPAYMASTER_ADDRESS, ENTRYPOINT_0_6_BYTECODE, CREATOR_0_6_BYTECODE } from "./AATestArtifacts.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( + UserOperation 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; + IVerifyingPaymaster public paymaster; + address public verifier; + uint256 public verifierKey; + bool public writeGasProfile = false; + + function(UserOperation memory) internal view returns (bytes memory) paymasterData; + function(UserOperation memory) internal view returns (bytes memory) dummyPaymasterData; + + function initializeTest(string memory _name) internal { + writeGasProfile = vm.envOr("WRITE_GAS_PROFILE", false); + name = _name; + entryPoint = IEntryPoint(payable(address(0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789))); + vm.etch(address(entryPoint), ENTRYPOINT_0_6_BYTECODE); + vm.etch(0x7fc98430eAEdbb6070B35B39D798725049088348, CREATOR_0_6_BYTECODE); + beneficiary = payable(makeAddr("beneficiary")); + vm.deal(beneficiary, 1e18); + paymasterData = emptyPaymasterAndData; + dummyPaymasterData = emptyPaymasterAndData; + (verifier, verifierKey) = makeAddrAndKey("VERIFIER"); + paymaster = IVerifyingPaymaster(VERIFYINGPAYMASTER_ADDRESS); + vm.etch(address(paymaster), VERIFYINGPAYMASTER_BYTECODE); + vm.store(address(paymaster), bytes32(0), bytes32(uint256(uint160(verifier)))); + } + + function setAccount() internal { + (owner, key) = makeAddrAndKey("Owner"); + account = getAccountAddr(owner); + vm.deal(address(account), 1e18); + } + + function fillUserOp(bytes memory _data) internal view returns (UserOperation memory op) { + op.sender = address(account); + op.nonce = entryPoint.getNonce(address(account), 0); + if (address(account).code.length == 0) { + op.initCode = getInitCode(owner); + } + op.callData = _data; + op.callGasLimit = 1000000; + op.verificationGasLimit = 1000000; + op.preVerificationGas = 21000; + op.maxFeePerGas = 1; + op.maxPriorityFeePerGas = 1; + op.signature = getDummySig(op); + op.paymasterAndData = dummyPaymasterData(op); + op.preVerificationGas = calculatePreVerificationGas(op); + op.paymasterAndData = paymasterData(op); + op.signature = getSignature(op); + } + + function signUserOpHash(uint256 _key, UserOperation 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(UserOperation memory _op, string memory _test, uint256 _value) internal { + UserOperation[] memory ops = new UserOperation[](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 { + UserOperation 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); + UserOperation memory op = fillUserOp(fillData(_recipient, _amount, "")); + executeUserOp(op, "native", _amount); + } + + function testTransferNative() internal { + createAccount(owner); + uint256 amount = 5e17; + address recipient = makeAddr("recipient"); + UserOperation 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); + UserOperation 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)); + 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: 100e18 }(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: 100e18 }(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(UserOperation memory _op) internal pure returns (bytes memory ret) {} + + function validatePaymasterAndData(UserOperation 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(UserOperation memory _op) internal view returns (bytes memory ret) { + ret = abi.encodePacked( + address(paymaster), + uint256(0), + uint256(0), + hex"fffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c" + ); + } + + function pack(UserOperation memory _op) internal pure returns (bytes memory) { + bytes memory packed = abi.encode( + _op.sender, + _op.nonce, + _op.initCode, + _op.callData, + _op.callGasLimit, + _op.verificationGasLimit, + _op.preVerificationGas, + _op.maxFeePerGas, + _op.maxPriorityFeePerGas, + _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(UserOperation 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(UserOperation memory _op) internal view virtual returns (bytes memory); + + function getDummySig(UserOperation 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/split-BTT/distribute-erc20/distribute.t.sol b/src/test/split-BTT/distribute-erc20/distribute.t.sol new file mode 100644 index 000000000..4c2d9c139 --- /dev/null +++ b/src/test/split-BTT/distribute-erc20/distribute.t.sol @@ -0,0 +1,78 @@ +// 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 new file mode 100644 index 000000000..320921cca --- /dev/null +++ b/src/test/split-BTT/distribute-erc20/distribute.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..fd524c40d --- /dev/null +++ b/src/test/split-BTT/distribute-native-token/distribute.t.sol @@ -0,0 +1,77 @@ +// 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 new file mode 100644 index 000000000..d75f4cc53 --- /dev/null +++ b/src/test/split-BTT/distribute-native-token/distribute.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..bc75ae266 --- /dev/null +++ b/src/test/split-BTT/initialize/initialize.t.sol @@ -0,0 +1,131 @@ +// 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 new file mode 100644 index 000000000..4647d7f70 --- /dev/null +++ b/src/test/split-BTT/initialize/initialize.tree @@ -0,0 +1,25 @@ +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 new file mode 100644 index 000000000..2bf87b871 --- /dev/null +++ b/src/test/split-BTT/other-functions/other.t.sol @@ -0,0 +1,58 @@ +// 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 new file mode 100644 index 000000000..dd1798caa --- /dev/null +++ b/src/test/split-BTT/other-functions/other.tree @@ -0,0 +1,5 @@ +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 new file mode 100644 index 000000000..c5499f664 --- /dev/null +++ b/src/test/split-BTT/release-erc20/release.t.sol @@ -0,0 +1,108 @@ +// 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 new file mode 100644 index 000000000..217ea3b6c --- /dev/null +++ b/src/test/split-BTT/release-erc20/release.tree @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..18210e0da --- /dev/null +++ b/src/test/split-BTT/release-native-token/release.t.sol @@ -0,0 +1,104 @@ +// 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 new file mode 100644 index 000000000..afa44e86d --- /dev/null +++ b/src/test/split-BTT/release-native-token/release.tree @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..af3cc85d1 --- /dev/null +++ b/src/test/split-BTT/set-contract-uri/setContractURI.t.sol @@ -0,0 +1,85 @@ +// 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 new file mode 100644 index 000000000..8fc480b19 --- /dev/null +++ b/src/test/split-BTT/set-contract-uri/setContractURI.tree @@ -0,0 +1,8 @@ +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 index 31e25d42d..0df3b9e91 100644 --- a/src/test/staking/EditionStake.t.sol +++ b/src/test/staking/EditionStake.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import { EditionStake } from "contracts/prebuilts/staking/EditionStake.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; contract EditionStakeTest is BaseTest { diff --git a/src/test/staking/EditionStake_EthReward.t.sol b/src/test/staking/EditionStake_EthReward.t.sol index 784cb82a2..2f829cdb9 100644 --- a/src/test/staking/EditionStake_EthReward.t.sol +++ b/src/test/staking/EditionStake_EthReward.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import { EditionStake } from "contracts/prebuilts/staking/EditionStake.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; contract EditionStakeEthRewardTest is BaseTest { diff --git a/src/test/staking/NFTStake.t.sol b/src/test/staking/NFTStake.t.sol index 9518a5429..8acd02214 100644 --- a/src/test/staking/NFTStake.t.sol +++ b/src/test/staking/NFTStake.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import { NFTStake } from "contracts/prebuilts/staking/NFTStake.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; contract NFTStakeTest is BaseTest { diff --git a/src/test/staking/NFTStake_EthReward.t.sol b/src/test/staking/NFTStake_EthReward.t.sol index de5d5e778..459f111e5 100644 --- a/src/test/staking/NFTStake_EthReward.t.sol +++ b/src/test/staking/NFTStake_EthReward.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import { NFTStake } from "contracts/prebuilts/staking/NFTStake.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; contract NFTStakeEthRewardTest is BaseTest { diff --git a/src/test/staking/TokenStake.t.sol b/src/test/staking/TokenStake.t.sol index a78e5e7d6..f2a7d63ae 100644 --- a/src/test/staking/TokenStake.t.sol +++ b/src/test/staking/TokenStake.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import { TokenStake } from "contracts/prebuilts/staking/TokenStake.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; contract TokenStakeTest is BaseTest { @@ -860,7 +860,7 @@ contract Macro_TokenStake_Tax is BaseTest { // vm.stopPrank(); // } - // // Staked amount still reamins unchanged for stakerOne + // // Staked amount still remains unchanged for stakerOne // (stakingTokenAmount, ) = stakeContract.getStakeInfo(stakerOne); // assertEq(stakingTokenAmount, 100 ether); diff --git a/src/test/staking/TokenStake_EthReward.t.sol b/src/test/staking/TokenStake_EthReward.t.sol index 14c01fa59..9cef37deb 100644 --- a/src/test/staking/TokenStake_EthReward.t.sol +++ b/src/test/staking/TokenStake_EthReward.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import { TokenStake } from "contracts/prebuilts/staking/TokenStake.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; contract TokenStakeEthRewardTest is BaseTest { diff --git a/src/test/staking/TokenStake_EthStake.t.sol b/src/test/staking/TokenStake_EthStake.t.sol index 58ff1df06..8630d08a9 100644 --- a/src/test/staking/TokenStake_EthStake.t.sol +++ b/src/test/staking/TokenStake_EthStake.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import { TokenStake } from "contracts/prebuilts/staking/TokenStake.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; contract TokenStakeEthStakeTest is BaseTest { diff --git a/src/test/token/TokenERC1155.t.sol b/src/test/token/TokenERC1155.t.sol index 0cc4cc248..ced59d548 100644 --- a/src/test/token/TokenERC1155.t.sol +++ b/src/test/token/TokenERC1155.t.sol @@ -4,13 +4,12 @@ pragma solidity ^0.8.0; import { TokenERC1155, IPlatformFee } from "contracts/prebuilts/token/TokenERC1155.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract TokenERC1155Test is BaseTest { - using StringsUpgradeable for uint256; + using Strings for uint256; event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri, uint256 quantityMinted); event TokensMintedWithSignature( @@ -83,11 +82,10 @@ contract TokenERC1155Test is BaseTest { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(TokenERC1155.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + TokenERC1155.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = bytes.concat( abi.encode( typehashMintRequest, @@ -410,9 +408,9 @@ contract TokenERC1155Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -554,9 +552,9 @@ contract TokenERC1155Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -594,9 +592,9 @@ contract TokenERC1155Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -657,9 +655,9 @@ contract TokenERC1155Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -843,9 +841,9 @@ contract TokenERC1155Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -854,9 +852,9 @@ contract TokenERC1155Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -895,9 +893,9 @@ contract TokenERC1155Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); diff --git a/src/test/token/TokenERC20.t.sol b/src/test/token/TokenERC20.t.sol index cf368ff67..03e44d81c 100644 --- a/src/test/token/TokenERC20.t.sol +++ b/src/test/token/TokenERC20.t.sol @@ -4,13 +4,12 @@ pragma solidity ^0.8.0; import { TokenERC20 } from "contracts/prebuilts/token/TokenERC20.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract TokenERC20Test is BaseTest { - using StringsUpgradeable for uint256; + using Strings for uint256; event TokensMinted(address indexed mintedTo, uint256 quantityMinted); event TokensMintedWithSignature( @@ -80,11 +79,10 @@ contract TokenERC20Test is BaseTest { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(TokenERC20.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + TokenERC20.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashMintRequest, _request.to, @@ -324,9 +322,9 @@ contract TokenERC20Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -374,9 +372,9 @@ contract TokenERC20Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -415,9 +413,9 @@ contract TokenERC20Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); diff --git a/src/test/token/TokenERC721.t.sol b/src/test/token/TokenERC721.t.sol index e75bda220..41732cff6 100644 --- a/src/test/token/TokenERC721.t.sol +++ b/src/test/token/TokenERC721.t.sol @@ -4,13 +4,12 @@ pragma solidity ^0.8.0; import { TokenERC721 } from "contracts/prebuilts/token/TokenERC721.sol"; // Test imports -import "contracts/lib/TWStrings.sol"; + import "../utils/BaseTest.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; -import "@openzeppelin/contracts/utils/Strings.sol"; contract TokenERC721Test is BaseTest { - using StringsUpgradeable for uint256; + using Strings for uint256; event TokensMinted(address indexed mintedTo, uint256 indexed tokenIdMinted, string uri); event TokensMintedWithSignature( @@ -81,11 +80,10 @@ contract TokenERC721Test is BaseTest { _signature = signMintRequest(_mintrequest, privateKey); } - function signMintRequest(TokenERC721.MintRequest memory _request, uint256 _privateKey) - internal - view - returns (bytes memory) - { + function signMintRequest( + TokenERC721.MintRequest memory _request, + uint256 _privateKey + ) internal view returns (bytes memory) { bytes memory encodedRequest = abi.encode( typehashMintRequest, _request.to, @@ -299,9 +297,9 @@ contract TokenERC721Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -456,9 +454,9 @@ contract TokenERC721Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -496,9 +494,9 @@ contract TokenERC721Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -559,9 +557,9 @@ contract TokenERC721Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -609,9 +607,9 @@ contract TokenERC721Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); @@ -650,9 +648,9 @@ contract TokenERC721Test is BaseTest { vm.expectRevert( abi.encodePacked( "AccessControl: account ", - TWStrings.toHexString(uint160(address(0x1)), 20), + Strings.toHexString(uint160(address(0x1)), 20), " is missing role ", - TWStrings.toHexString(uint256(role), 32) + Strings.toHexString(uint256(role), 32) ) ); vm.prank(address(0x1)); diff --git a/src/test/tokenerc1155-BTT/burn-batch/burnBatch.t.sol b/src/test/tokenerc1155-BTT/burn-batch/burnBatch.t.sol new file mode 100644 index 000000000..273cb2db4 --- /dev/null +++ b/src/test/tokenerc1155-BTT/burn-batch/burnBatch.t.sol @@ -0,0 +1,148 @@ +// 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 new file mode 100644 index 000000000..dca6fa537 --- /dev/null +++ b/src/test/tokenerc1155-BTT/burn-batch/burnBatch.tree @@ -0,0 +1,14 @@ +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 new file mode 100644 index 000000000..1bf2575b9 --- /dev/null +++ b/src/test/tokenerc1155-BTT/burn/burn.t.sol @@ -0,0 +1,121 @@ +// 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 new file mode 100644 index 000000000..8232a832d --- /dev/null +++ b/src/test/tokenerc1155-BTT/burn/burn.tree @@ -0,0 +1,14 @@ +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 new file mode 100644 index 000000000..5340ff595 --- /dev/null +++ b/src/test/tokenerc1155-BTT/initialize/initialize.t.sol @@ -0,0 +1,313 @@ +// 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 new file mode 100644 index 000000000..15c2ba936 --- /dev/null +++ b/src/test/tokenerc1155-BTT/initialize/initialize.tree @@ -0,0 +1,43 @@ +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 new file mode 100644 index 000000000..b0a8703bb --- /dev/null +++ b/src/test/tokenerc1155-BTT/mint-to/mintTo.t.sol @@ -0,0 +1,307 @@ +// 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 new file mode 100644 index 000000000..facd1e8eb --- /dev/null +++ b/src/test/tokenerc1155-BTT/mint-to/mintTo.tree @@ -0,0 +1,48 @@ +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 new file mode 100644 index 000000000..f39177c8a --- /dev/null +++ b/src/test/tokenerc1155-BTT/mint-with-signature/mintWithSignature.t.sol @@ -0,0 +1,894 @@ +// 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; + + 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); + + 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 _saleProceeds = totalPrice - _platformFee; + assertEq(caller.balance, 1000 ether - totalPrice); + assertEq(tokenContract.platformFeeRecipient().balance, _platformFee); + assertEq(tokenContract.primarySaleRecipient().balance, _saleProceeds); + } + + 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 _saleProceeds = totalPrice - _platformFee; + assertEq(erc20.balanceOf(caller), 1000 ether - totalPrice); + assertEq(erc20.balanceOf(tokenContract.platformFeeRecipient()), _platformFee); + assertEq(erc20.balanceOf(tokenContract.primarySaleRecipient()), _saleProceeds); + } + + 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 _saleProceeds = totalPrice - _platformFee; + assertEq(erc20.balanceOf(caller), 1000 ether - totalPrice); + assertEq(erc20.balanceOf(tokenContract.platformFeeRecipient()), _platformFee); + assertEq(erc20.balanceOf(tokenContract.primarySaleRecipient()), _saleProceeds); + } + + 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 new file mode 100644 index 000000000..115264aec --- /dev/null +++ b/src/test/tokenerc1155-BTT/mint-with-signature/mintWithSignature.tree @@ -0,0 +1,102 @@ +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 new file mode 100644 index 000000000..c95c15dab --- /dev/null +++ b/src/test/tokenerc1155-BTT/other-functions/other.t.sol @@ -0,0 +1,172 @@ +// 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 new file mode 100644 index 000000000..6af7d78cf --- /dev/null +++ b/src/test/tokenerc1155-BTT/other-functions/other.tree @@ -0,0 +1,37 @@ +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 new file mode 100644 index 000000000..0615f32c4 --- /dev/null +++ b/src/test/tokenerc1155-BTT/owner/owner.t.sol @@ -0,0 +1,58 @@ +// 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 new file mode 100644 index 000000000..576cfcb91 --- /dev/null +++ b/src/test/tokenerc1155-BTT/owner/owner.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..4f3739103 --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-contract-uri/setContractURI.t.sol @@ -0,0 +1,87 @@ +// 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 new file mode 100644 index 000000000..8fc480b19 --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-contract-uri/setContractURI.tree @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..552d9d75b --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol @@ -0,0 +1,115 @@ +// 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 new file mode 100644 index 000000000..78a4312de --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.tree @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..380d65921 --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-flat-platform-fee-info/setFlatPlatformFeeInfo.t.sol @@ -0,0 +1,93 @@ +// 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 new file mode 100644 index 000000000..95bfe1f2d --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-flat-platform-fee-info/setFlatPlatformFeeInfo.tree @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..00dd0f2ef --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-owner/setOwner.t.sol @@ -0,0 +1,99 @@ +// 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 new file mode 100644 index 000000000..964e97cac --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-owner/setOwner.tree @@ -0,0 +1,9 @@ +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 new file mode 100644 index 000000000..e52402a03 --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-platform-fee-info/setPlatformFeeInfo.t.sol @@ -0,0 +1,104 @@ +// 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 new file mode 100644 index 000000000..dcef9965e --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-platform-fee-info/setPlatformFeeInfo.tree @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..db96dd310 --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-platform-fee-type/setPlatformFeeType.t.sol @@ -0,0 +1,88 @@ +// 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 new file mode 100644 index 000000000..e25a6bd4c --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-platform-fee-type/setPlatformFeeType.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..2f838241e --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.t.sol @@ -0,0 +1,88 @@ +// 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 new file mode 100644 index 000000000..230035a07 --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..051dd7918 --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol @@ -0,0 +1,129 @@ +// 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 new file mode 100644 index 000000000..cada076de --- /dev/null +++ b/src/test/tokenerc1155-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.tree @@ -0,0 +1,15 @@ +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 new file mode 100644 index 000000000..1a2feb0a2 --- /dev/null +++ b/src/test/tokenerc1155-BTT/uri/tokenURI.t.sol @@ -0,0 +1,57 @@ +// 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 new file mode 100644 index 000000000..2df0b55ed --- /dev/null +++ b/src/test/tokenerc1155-BTT/uri/tokenURI.tree @@ -0,0 +1,3 @@ +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 new file mode 100644 index 000000000..f584f57a8 --- /dev/null +++ b/src/test/tokenerc1155-BTT/verify/verify.t.sol @@ -0,0 +1,157 @@ +// 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 new file mode 100644 index 000000000..c160faa0f --- /dev/null +++ b/src/test/tokenerc1155-BTT/verify/verify.tree @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..6dae4df15 --- /dev/null +++ b/src/test/tokenerc20-BTT/initialize/initialize.t.sol @@ -0,0 +1,233 @@ +// 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 new file mode 100644 index 000000000..a3ead7790 --- /dev/null +++ b/src/test/tokenerc20-BTT/initialize/initialize.tree @@ -0,0 +1,34 @@ +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 new file mode 100644 index 000000000..ad5095640 --- /dev/null +++ b/src/test/tokenerc20-BTT/mint-to/mintTo.t.sol @@ -0,0 +1,81 @@ +// 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 new file mode 100644 index 000000000..33bb14c7e --- /dev/null +++ b/src/test/tokenerc20-BTT/mint-to/mintTo.tree @@ -0,0 +1,7 @@ +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 new file mode 100644 index 000000000..bbe988eb8 --- /dev/null +++ b/src/test/tokenerc20-BTT/mint-with-signature/mintWithSignature.t.sol @@ -0,0 +1,449 @@ +// 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 new file mode 100644 index 000000000..d7cfcdcab --- /dev/null +++ b/src/test/tokenerc20-BTT/mint-with-signature/mintWithSignature.tree @@ -0,0 +1,51 @@ +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 new file mode 100644 index 000000000..70b62669e --- /dev/null +++ b/src/test/tokenerc20-BTT/other-functions/other.t.sol @@ -0,0 +1,103 @@ +// 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 new file mode 100644 index 000000000..57a1466a8 --- /dev/null +++ b/src/test/tokenerc20-BTT/other-functions/other.tree @@ -0,0 +1,20 @@ +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 new file mode 100644 index 000000000..faccf3f9b --- /dev/null +++ b/src/test/tokenerc20-BTT/set-contract-uri/setContractURI.t.sol @@ -0,0 +1,85 @@ +// 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 new file mode 100644 index 000000000..8fc480b19 --- /dev/null +++ b/src/test/tokenerc20-BTT/set-contract-uri/setContractURI.tree @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..d2a14a7f1 --- /dev/null +++ b/src/test/tokenerc20-BTT/set-platform-fee-info/setPlatformFeeInfo.t.sol @@ -0,0 +1,101 @@ +// 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 new file mode 100644 index 000000000..dcef9965e --- /dev/null +++ b/src/test/tokenerc20-BTT/set-platform-fee-info/setPlatformFeeInfo.tree @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..070f9bfaf --- /dev/null +++ b/src/test/tokenerc20-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.t.sol @@ -0,0 +1,86 @@ +// 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 new file mode 100644 index 000000000..230035a07 --- /dev/null +++ b/src/test/tokenerc20-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..fb54c7868 --- /dev/null +++ b/src/test/tokenerc20-BTT/verify/verify.t.sol @@ -0,0 +1,143 @@ +// 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 new file mode 100644 index 000000000..c160faa0f --- /dev/null +++ b/src/test/tokenerc20-BTT/verify/verify.tree @@ -0,0 +1,12 @@ +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 new file mode 100644 index 000000000..01fa7b43c --- /dev/null +++ b/src/test/tokenerc721-BTT/burn/burn.t.sol @@ -0,0 +1,107 @@ +// 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 new file mode 100644 index 000000000..0a6e2ff43 --- /dev/null +++ b/src/test/tokenerc721-BTT/burn/burn.tree @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..e16c27cf7 --- /dev/null +++ b/src/test/tokenerc721-BTT/initialize/initialize.t.sol @@ -0,0 +1,311 @@ +// 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 new file mode 100644 index 000000000..041a9e248 --- /dev/null +++ b/src/test/tokenerc721-BTT/initialize/initialize.tree @@ -0,0 +1,42 @@ +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 new file mode 100644 index 000000000..4c9900e4c --- /dev/null +++ b/src/test/tokenerc721-BTT/mint-to/mintTo.t.sol @@ -0,0 +1,184 @@ +// 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 new file mode 100644 index 000000000..408dd48c9 --- /dev/null +++ b/src/test/tokenerc721-BTT/mint-to/mintTo.tree @@ -0,0 +1,25 @@ +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 new file mode 100644 index 000000000..4ad653f7b --- /dev/null +++ b/src/test/tokenerc721-BTT/mint-with-signature/mintWithSignature.t.sol @@ -0,0 +1,717 @@ +// 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 new file mode 100644 index 000000000..347c0f674 --- /dev/null +++ b/src/test/tokenerc721-BTT/mint-with-signature/mintWithSignature.tree @@ -0,0 +1,85 @@ +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 new file mode 100644 index 000000000..43a70d662 --- /dev/null +++ b/src/test/tokenerc721-BTT/other-functions/other.t.sol @@ -0,0 +1,107 @@ +// 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 new file mode 100644 index 000000000..c6611fa64 --- /dev/null +++ b/src/test/tokenerc721-BTT/other-functions/other.tree @@ -0,0 +1,31 @@ +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 new file mode 100644 index 000000000..1f9f88ddc --- /dev/null +++ b/src/test/tokenerc721-BTT/owner/owner.t.sol @@ -0,0 +1,58 @@ +// 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 new file mode 100644 index 000000000..576cfcb91 --- /dev/null +++ b/src/test/tokenerc721-BTT/owner/owner.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..1dc0d8855 --- /dev/null +++ b/src/test/tokenerc721-BTT/set-contract-uri/setContractURI.t.sol @@ -0,0 +1,87 @@ +// 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 new file mode 100644 index 000000000..8fc480b19 --- /dev/null +++ b/src/test/tokenerc721-BTT/set-contract-uri/setContractURI.tree @@ -0,0 +1,8 @@ +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 new file mode 100644 index 000000000..d63175b76 --- /dev/null +++ b/src/test/tokenerc721-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.t.sol @@ -0,0 +1,115 @@ +// 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 new file mode 100644 index 000000000..78a4312de --- /dev/null +++ b/src/test/tokenerc721-BTT/set-default-royalty-info/setDefaultRoyaltyInfo.tree @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000..1ea57ba2c --- /dev/null +++ b/src/test/tokenerc721-BTT/set-owner/setOwner.t.sol @@ -0,0 +1,99 @@ +// 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 new file mode 100644 index 000000000..964e97cac --- /dev/null +++ b/src/test/tokenerc721-BTT/set-owner/setOwner.tree @@ -0,0 +1,9 @@ +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 new file mode 100644 index 000000000..e2a9b0ab4 --- /dev/null +++ b/src/test/tokenerc721-BTT/set-platform-fee-info/setPlatformFeeInfo.t.sol @@ -0,0 +1,104 @@ +// 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 new file mode 100644 index 000000000..dcef9965e --- /dev/null +++ b/src/test/tokenerc721-BTT/set-platform-fee-info/setPlatformFeeInfo.tree @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..325b801ac --- /dev/null +++ b/src/test/tokenerc721-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.t.sol @@ -0,0 +1,88 @@ +// 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 new file mode 100644 index 000000000..230035a07 --- /dev/null +++ b/src/test/tokenerc721-BTT/set-primary-sale-recipient/setPrimarySaleRecipient.tree @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..f5c5e3b83 --- /dev/null +++ b/src/test/tokenerc721-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.t.sol @@ -0,0 +1,129 @@ +// 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 new file mode 100644 index 000000000..e28295634 --- /dev/null +++ b/src/test/tokenerc721-BTT/set-royalty-info-for-token/setRoyaltyInfoForToken.tree @@ -0,0 +1,15 @@ +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 new file mode 100644 index 000000000..260048e0c --- /dev/null +++ b/src/test/tokenerc721-BTT/token-uri/tokenURI.t.sol @@ -0,0 +1,57 @@ +// 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 new file mode 100644 index 000000000..c97d2c9d1 --- /dev/null +++ b/src/test/tokenerc721-BTT/token-uri/tokenURI.tree @@ -0,0 +1,3 @@ +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 new file mode 100644 index 000000000..2d219b9f0 --- /dev/null +++ b/src/test/tokenerc721-BTT/verify/verify.t.sol @@ -0,0 +1,149 @@ +// 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 new file mode 100644 index 000000000..c160faa0f --- /dev/null +++ b/src/test/tokenerc721-BTT/verify/verify.tree @@ -0,0 +1,12 @@ +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 index eda6c4c20..9b89c55e5 100644 --- a/src/test/utils/BaseTest.sol +++ b/src/test/utils/BaseTest.sol @@ -4,16 +4,18 @@ pragma solidity ^0.8.11; import "@std/Test.sol"; import "@ds-test/test.sol"; // import "./Console.sol"; -import "./Wallet.sol"; +import { Wallet } from "./Wallet.sol"; import "./ChainlinkVRF.sol"; -import "../mocks/WETH9.sol"; -import "../mocks/MockERC20.sol"; -import "../mocks/MockERC721.sol"; -import "../mocks/MockERC1155.sol"; -import "contracts/infra/forwarder/Forwarder.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 "contracts/infra/TWRegistry.sol"; -import "contracts/infra/TWFactory.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"; @@ -32,14 +34,21 @@ import { IContractPublisher } from "contracts/infra/interface/IContractPublisher 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 "contracts/prebuilts/unaudited/airdrop/AirdropERC20Claimable.sol"; -import "contracts/prebuilts/unaudited/airdrop/AirdropERC1155.sol"; -import "contracts/prebuilts/unaudited/airdrop/AirdropERC1155Claimable.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 "../mocks/MockContractPublisher.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"; @@ -51,6 +60,8 @@ abstract contract BaseTest is DSTest, Test { MockERC20 public erc20Aux; MockERC721 public erc721; MockERC1155 public erc1155; + MockERC721NonBurnable public erc721NonBurnable; + MockERC1155NonBurnable public erc1155NonBurnable; WETH9 public weth; address public forwarder; @@ -100,6 +111,8 @@ abstract contract BaseTest is DSTest, Test { 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()); @@ -132,12 +145,6 @@ abstract contract BaseTest is DSTest, Test { 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 MockContract(bytes32("AirdropERC721Claimable"), 1))); - TWFactory(factory).addImplementation(address(new AirdropERC721Claimable())); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("AirdropERC20Claimable"), 1))); - TWFactory(factory).addImplementation(address(new AirdropERC20Claimable())); - TWFactory(factory).addImplementation(address(new MockContract(bytes32("AirdropERC1155Claimable"), 1))); - TWFactory(factory).addImplementation(address(new AirdropERC1155Claimable())); TWFactory(factory).addImplementation( address(new PackVRFDirect(address(weth), eoaForwarder, linkToken, vrfV2Wrapper)) ); @@ -310,55 +317,6 @@ abstract contract BaseTest is DSTest, Test { "AirdropERC1155", abi.encodeCall(AirdropERC1155.initialize, (deployer, CONTRACT_URI, forwarders())) ); - deployContractProxy( - "AirdropERC721Claimable", - abi.encodeCall( - AirdropERC721Claimable.initialize, - ( - deployer, - forwarders(), - address(airdropTokenOwner), - address(erc721), - _airdropTokenIdsERC721, - 1000, - 1, - _airdropMerkleRootERC721 - ) - ) - ); - deployContractProxy( - "AirdropERC1155Claimable", - abi.encodeCall( - AirdropERC1155Claimable.initialize, - ( - deployer, - forwarders(), - address(airdropTokenOwner), - address(erc1155), - _airdropTokenIdsERC1155, - _airdropAmountsERC1155, - 1000, - _airdropWalletClaimCountERC1155, - _airdropMerkleRootERC1155 - ) - ) - ); - deployContractProxy( - "AirdropERC20Claimable", - abi.encodeCall( - AirdropERC20Claimable.initialize, - ( - deployer, - forwarders(), - address(airdropTokenOwner), - address(erc20), - 10_000 ether, - 1000, - 1, - _airdropMerkleRootERC20 - ) - ) - ); deployContractProxy( "NFTStake", abi.encodeCall( @@ -382,10 +340,10 @@ abstract contract BaseTest is DSTest, Test { ); } - function deployContractProxy(string memory _contractType, bytes memory _initializer) - public - returns (address proxyAddress) - { + 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; @@ -404,22 +362,14 @@ abstract contract BaseTest is DSTest, Test { wallet = new Wallet(); } - function assertIsOwnerERC721( - address _token, - address _owner, - uint256[] memory _tokenIds - ) internal { + 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 { + 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); @@ -452,19 +402,11 @@ abstract contract BaseTest is DSTest, Test { } } - function assertBalERC20Eq( - address _token, - address _owner, - uint256 _amount - ) internal { + function assertBalERC20Eq(address _token, address _owner, uint256 _amount) internal { assertEq(MockERC20(_token).balanceOf(_owner), _amount); } - function assertBalERC20Gte( - address _token, - address _owner, - uint256 _amount - ) internal { + function assertBalERC20Gte(address _token, address _owner, uint256 _amount) internal { assertTrue(MockERC20(_token).balanceOf(_owner) >= _amount); } @@ -475,12 +417,11 @@ abstract contract BaseTest is DSTest, Test { } function setupAirdropClaimable() public { - string[] memory inputs = new string[](5); + string[] memory inputs = new string[](3); inputs[0] = "node"; - inputs[1] = "src/test/scripts/generateRoot.ts"; - inputs[2] = Strings.toString(5); - inputs[3] = "0"; - inputs[4] = "0x0000000000000000000000000000000000000000"; + 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)); diff --git a/src/test/utils/ChainlinkVRF.sol b/src/test/utils/ChainlinkVRF.sol index 79390589d..cd5eaf76a 100644 --- a/src/test/utils/ChainlinkVRF.sol +++ b/src/test/utils/ChainlinkVRF.sol @@ -2,11 +2,7 @@ pragma solidity ^0.8.0; contract Link { - function transferAndCall( - address, - uint256, - bytes calldata - ) external returns (bool) {} + function transferAndCall(address, uint256, bytes calldata) external returns (bool) {} } contract VRFV2Wrapper { diff --git a/src/test/utils/Console.sol b/src/test/utils/Console.sol index 753b533e0..c8e655ca6 100644 --- a/src/test/utils/Console.sol +++ b/src/test/utils/Console.sol @@ -249,2819 +249,1283 @@ library console { _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); } - function log( - uint256 p0, - uint256 p1, - uint256 p2 - ) internal view { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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/Wallet.sol b/src/test/utils/Wallet.sol index e0a3072e1..e3f8dc4a9 100644 --- a/src/test/utils/Wallet.sol +++ b/src/test/utils/Wallet.sol @@ -9,19 +9,11 @@ import "../mocks/MockERC721.sol"; import "../mocks/MockERC1155.sol"; contract Wallet is ERC721Holder, ERC1155Holder { - function transferERC20( - address token, - address to, - uint256 amount - ) public { + function transferERC20(address token, address to, uint256 amount) public { MockERC20(token).transfer(to, amount); } - function setAllowanceERC20( - address token, - address spender, - uint256 allowanceAmount - ) public { + function setAllowanceERC20(address token, address spender, uint256 allowanceAmount) public { MockERC20(token).approve(spender, allowanceAmount); } @@ -29,19 +21,11 @@ contract Wallet is ERC721Holder, ERC1155Holder { MockERC20(token).burn(amount); } - function transferERC721( - address token, - address to, - uint256 tokenId - ) public { + 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 { + function setApprovalForAllERC721(address token, address operator, bool toApprove) public { MockERC721(token).setApprovalForAll(operator, toApprove); } @@ -49,29 +33,15 @@ contract Wallet is ERC721Holder, ERC1155Holder { MockERC721(token).burn(tokenId); } - function transferERC1155( - address token, - address to, - uint256 tokenId, - uint256 amount, - bytes calldata data - ) external { + 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 { + function setApprovalForAllERC1155(address token, address operator, bool toApprove) public { MockERC1155(token).setApprovalForAll(operator, toApprove); } - function burnERC1155( - address token, - uint256 tokenId, - uint256 amount - ) public { + 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 new file mode 100644 index 000000000..9e9227ac6 --- /dev/null +++ b/src/test/vote-BTT/initialize/initialize.t.sol @@ -0,0 +1,200 @@ +// 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 new file mode 100644 index 000000000..6b935174b --- /dev/null +++ b/src/test/vote-BTT/initialize/initialize.tree @@ -0,0 +1,30 @@ +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 new file mode 100644 index 000000000..960a94e7e --- /dev/null +++ b/src/test/vote-BTT/other-functions/other.t.sol @@ -0,0 +1,90 @@ +// 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 new file mode 100644 index 000000000..2649d89ae --- /dev/null +++ b/src/test/vote-BTT/other-functions/other.tree @@ -0,0 +1,9 @@ +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 new file mode 100644 index 000000000..4bdd7f2ad --- /dev/null +++ b/src/test/vote-BTT/propose/propose.t.sol @@ -0,0 +1,281 @@ +// 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 new file mode 100644 index 000000000..5df017a7e --- /dev/null +++ b/src/test/vote-BTT/propose/propose.tree @@ -0,0 +1,26 @@ +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 new file mode 100644 index 000000000..cc35af82e --- /dev/null +++ b/src/test/vote-BTT/set-contract-uri/setContractURI.t.sol @@ -0,0 +1,130 @@ +// 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 new file mode 100644 index 000000000..f7819fc38 --- /dev/null +++ b/src/test/vote-BTT/set-contract-uri/setContractURI.tree @@ -0,0 +1,8 @@ +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 a90012b01..12219cc65 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,25 +2,31 @@ # yarn lockfile v1 +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + "@babel/code-frame@^7.0.0": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.4.tgz#03ae5af150be94392cb5c7ccd97db5a19a5da6aa" + integrity sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA== dependencies: - "@babel/highlight" "^7.16.7" + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" -"@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/highlight@^7.16.7": - version "7.17.9" - resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.9.tgz" - integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" - chalk "^2.0.0" + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" js-tokens "^4.0.0" "@chainlink/contracts@^0.6.1": @@ -33,33 +39,60 @@ "@openzeppelin/contracts-upgradeable" "^4.7.3" "@openzeppelin/contracts-v0.7" "npm:@openzeppelin/contracts@v3.4.2" -"@cspotcode/source-map-consumer@0.8.0": +"@chainlink/contracts@^0.8.0": version "0.8.0" - resolved "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz" - integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== + resolved "https://registry.yarnpkg.com/@chainlink/contracts/-/contracts-0.8.0.tgz#4050c83c8b1603ffb0fd6ab99f1d9ea9db2c37de" + integrity sha512-nUv1Uxw5Mn92wgLs2bgPYmo8hpdQ3s9jB/lcbdU0LmNOVu0hbfmouVnqwRLa28Ll50q6GczUA+eO0ikNIKLZsA== + dependencies: + "@eth-optimism/contracts" "^0.5.21" + "@openzeppelin/contracts" "~4.3.3" + "@openzeppelin/contracts-upgradeable-4.7.3" "npm:@openzeppelin/contracts-upgradeable@v4.7.3" + "@openzeppelin/contracts-v0.7" "npm:@openzeppelin/contracts@v3.4.2" -"@cspotcode/source-map-support@0.7.0": - version "0.7.0" - resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz" - integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== +"@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" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: - "@cspotcode/source-map-consumer" "0.8.0" + "@jridgewell/trace-mapping" "0.3.9" -"@eslint/eslintrc@^1.2.1": - version "1.2.1" - resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.1.tgz" - integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ== +"@esbuild/linux-loong64@0.14.54": + version "0.14.54" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028" + integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw== + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint/eslintrc@^2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.3.tgz#797470a75fe0fbd5a53350ee715e85e87baff22d" + integrity sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.3.1" - globals "^13.9.0" + espree "^9.6.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" - minimatch "^3.0.4" + minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/js@8.54.0": + version "8.54.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.54.0.tgz#4fab9a2ff7860082c304f750e94acd644cf984cf" + integrity sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ== + "@eth-optimism/contracts@^0.5.21": version "0.5.40" resolved "https://registry.yarnpkg.com/@eth-optimism/contracts/-/contracts-0.5.40.tgz#d13a04a15ea947a69055e6fc74d87e215d4c936a" @@ -433,28 +466,78 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@humanwhocodes/config-array@^0.9.2": - version "0.9.5" - resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz" - integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== +"@humanwhocodes/config-array@^0.11.13": + version "0.11.13" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" + integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ== dependencies: - "@humanwhocodes/object-schema" "^1.2.1" + "@humanwhocodes/object-schema" "^2.0.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044" + integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== + +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.20" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" + integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" "@multiformats/base-x@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== +"@noble/hashes@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -462,28 +545,23 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" - resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@openzeppelin/contracts-upgradeable@4.7.3": +"@openzeppelin/contracts-upgradeable-4.7.3@npm:@openzeppelin/contracts-upgradeable@v4.7.3", "@openzeppelin/contracts-upgradeable@4.7.3": version "4.7.3" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.7.3.tgz#f1d606e2827d409053f3e908ba4eb8adb1dd6995" integrity sha512-+wuegAMaLcZnLCJIvrVUDzA9z/Wp93f0Dla/4jJvIhijRrPabjQbZe6fWiECLaJyfn5ci9fqf9vTw3xpQOad2A== -"@openzeppelin/contracts-upgradeable@^4.4.2": - version "4.6.0" - resolved "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.6.0.tgz" - integrity sha512-5OnVuO4HlkjSCJO165a4i2Pu1zQGzMs//o54LPrwUgxvEO2P3ax1QuaSI0cEHHTveA77guS0PnNugpR2JMsPfA== - -"@openzeppelin/contracts-upgradeable@^4.7.3": +"@openzeppelin/contracts-upgradeable@^4.4.2", "@openzeppelin/contracts-upgradeable@^4.7.3", "@openzeppelin/contracts-upgradeable@^4.9.3": version "4.9.3" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.3.tgz#ff17a80fb945f5102571f8efecb5ce5915cc4811" integrity sha512-jjaHAVRMrE4UuZNfDwjlLGDxTHWIOwTJS2ldnc278a0gevfXfPr8hxKEVBGFBE96kl2G3VHDZhUimw/+G3TG2A== @@ -498,39 +576,39 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.7.3.tgz#939534757a81f8d69cc854c7692805684ff3111e" integrity sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw== -"@openzeppelin/contracts@^4.4.2": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.7.0.tgz#3092d70ea60e3d1835466266b1d68ad47035a2d5" - integrity sha512-52Qb+A1DdOss8QvJrijYYPSf32GUg2pGaG/yCxtaA3cu4jduouTdg4XZSMLW9op54m1jH7J8hoajhHKOPsoJFw== +"@openzeppelin/contracts@^4.4.2", "@openzeppelin/contracts@^4.9.3": + version "4.9.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.3.tgz#00d7a8cf35a475b160b3f0293a6403c511099364" + integrity sha512-He3LieZ1pP2TNt5JbkPA4PNT9WC3gOTOlDcFGJW4Le4QKqwmiNJCRt44APfxMxvq7OugU/cqYuPcSBzOw38DAg== "@openzeppelin/contracts@~4.3.3": version "4.3.3" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.3.3.tgz#ff6ee919fc2a1abaf72b22814bfb72ed129ec137" integrity sha512-tDBopO1c98Yk7Cv/PZlHqrvtVjlgK5R4J6jxLwoO7qxK4xqOiZG+zSkIvGFpPZ0ikc3QOED3plgdqjgNTnBc7g== -"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.1": - version "0.14.1" - resolved "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.1.tgz" - integrity sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw== +"@solidity-parser/parser@^0.16.0", "@solidity-parser/parser@^0.16.2": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.16.2.tgz#42cb1e3d88b3e8029b0c9befff00b634cd92d2fa" + integrity sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg== dependencies: antlr4ts "^0.5.0-alpha.4" -"@thirdweb-dev/chains@0.1.54", "@thirdweb-dev/chains@^0.1.54": - version "0.1.54" - resolved "https://registry.yarnpkg.com/@thirdweb-dev/chains/-/chains-0.1.54.tgz#90e5c372a1d9cd785c51715bfbeba276ca2a203f" - integrity sha512-iCuKgtN2KIdfgqbIbZYgB8ObYdOJW9iXW9b5u+WKA4zyGApw1MTOSX0W2aPnadGen1z4iQfAuUDBYQ6JVqDOjg== +"@thirdweb-dev/chains@0.1.58", "@thirdweb-dev/chains@^0.1.54": + version "0.1.58" + resolved "https://registry.yarnpkg.com/@thirdweb-dev/chains/-/chains-0.1.58.tgz#5a26fe187ef39b7c6af87972166785d110cad53e" + integrity sha512-prSShAWoLODuZQcDBwNDqcXLzfevV2BOw54cDaHetSP+Sw/BP6SaPKIxojRQGsXARjn0JMWniG/NCtppUUHALQ== -"@thirdweb-dev/contracts-js@1.3.15": - version "1.3.15" - resolved "https://registry.yarnpkg.com/@thirdweb-dev/contracts-js/-/contracts-js-1.3.15.tgz#1e9df70cc4333ab2d6f0af5f0e9f2e26b043d9c9" - integrity sha512-ACKwjYPgTkhwvFvAbJudCZ9tQJIKpkaTKeq/tVBt3A2OXfAb8jc33Iu78K5WYoX0E0wvGqSxU8K8vkxij+RsnA== +"@thirdweb-dev/contracts-js@1.3.16": + version "1.3.16" + resolved "https://registry.yarnpkg.com/@thirdweb-dev/contracts-js/-/contracts-js-1.3.16.tgz#264727b40b0f320c01eefcb4295e89c9e9947002" + integrity sha512-EpLcD5mdm8b+tvSO7gD9cxSAqjLRr7ygktMp4Pe7Wvobl5ffq8O95futxdVsYc5pyciPZYr8apHUJFYMDlaTqA== dependencies: - "@thirdweb-dev/contracts" "3.10.3-1" + "@thirdweb-dev/contracts" "3.10.3" -"@thirdweb-dev/contracts@3.10.3-1": - version "3.10.3-1" - resolved "https://registry.yarnpkg.com/@thirdweb-dev/contracts/-/contracts-3.10.3-1.tgz#0f970fa0b9c821cd9036bb51334ab92083123a96" - integrity sha512-eiGS1Q2+Ge48yEO+kBw0DqR6eTN6ip4vrY0eBoaUrpcFAudd+aW+qszxCFBUuKSHIxvRs3Ul2b64pdYFEXzMIA== +"@thirdweb-dev/contracts@3.10.3": + version "3.10.3" + resolved "https://registry.yarnpkg.com/@thirdweb-dev/contracts/-/contracts-3.10.3.tgz#985b890b2bac051bc69a9108ac9e9df0fd37a1c1" + integrity sha512-wSVNaEoosn0AgUtnxlvv7rgK+3EUMzJm2ZasofPgJgqGS3gYH5nDBmK29VMquA2BLc38OAPyYMWc/iQCiCikMg== dependencies: "@chainlink/contracts" "^0.6.1" "@openzeppelin/contracts" "4.7.3" @@ -538,241 +616,256 @@ "@thirdweb-dev/dynamic-contracts" "^1.1.2" erc721a-upgradeable "^3.3.0" -"@thirdweb-dev/dynamic-contracts@^1.1.2": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@thirdweb-dev/dynamic-contracts/-/dynamic-contracts-1.2.1.tgz#7b677d3f46469e8b30600e090482bebda3241102" - integrity sha512-IFCeETmkmOO4fLOmLtDA6kIABXU8NJ4m3LnUs/f0dooC5A3Yn4sjUIm6CA53alXuyVPUGqi1R7CuCrMYEjpdiA== +"@thirdweb-dev/crypto@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@thirdweb-dev/crypto/-/crypto-0.2.0.tgz#ad629854107b58647101fc6f3d2bbe619ae34424" + integrity sha512-hQwSCL/imqSCcnUXlGqJi6dfs4UOcJ91Eq/t1cPXyAb6nwvyaePZPVFqGDglZMQvkS/NWZhifXZINRiCfazn2w== + dependencies: + "@noble/hashes" "^1.3.2" + js-sha3 "^0.9.2" + +"@thirdweb-dev/dynamic-contracts@^1.1.2", "@thirdweb-dev/dynamic-contracts@^1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@thirdweb-dev/dynamic-contracts/-/dynamic-contracts-1.2.4.tgz#468de8a98e3be211ad1a0e541da1190093ceca49" + integrity sha512-cQtUznRXBDifzME3zmppVrfBM2Aw8C/okCLzsgcLU/Qr68TjLJTKTDGt2uGo/q5qAvRVJjQRD/bNvV1QTqjqSg== "@thirdweb-dev/generated-abis@0.0.1": version "0.0.1" resolved "https://registry.yarnpkg.com/@thirdweb-dev/generated-abis/-/generated-abis-0.0.1.tgz#0d788d6aff0ac08f11e9eeb9ae4c8321845272a8" integrity sha512-vO9/3lSLO8smyyH1QVeYravSTzFwV1nf1C/Im1NBDPdH8//YvcbhtETGGiNfHWpyCvSi0vRYwvf+/7FKdwpDGQ== -"@thirdweb-dev/sdk@^4": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@thirdweb-dev/sdk/-/sdk-4.0.3.tgz#51dc8eddca91a342467b4e712431d41cc3d8fd89" - integrity sha512-VcS0XjCxddiHYy+bavnMm0VliEnrSexp8hwzU2yBlxAXbnqm+H6EAfNHHVoJwFlWsG9y056s7YPzBlv6yZlk0w== +"@thirdweb-dev/merkletree@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@thirdweb-dev/merkletree/-/merkletree-0.2.0.tgz#b1f7275bd54b499bb0c98863692e77b146308eb4" + integrity sha512-4KoH2EOCWKiaHfhDO5Tnf1HjeCXKVfLt31y0kcSG5C0gCldnhm7i1fGUB8e0hW3trfyPQAuSgyP67Ep3UwzClg== + dependencies: + "@thirdweb-dev/crypto" "0.2.0" + buffer "^6.0.3" + buffer-reverse "^1.0.1" + treeify "^1.1.0" + +"@thirdweb-dev/sdk@^4.0.16": + version "4.0.16" + resolved "https://registry.yarnpkg.com/@thirdweb-dev/sdk/-/sdk-4.0.16.tgz#2fec8d9bb645092473479e216eccd8942a2b5b13" + integrity sha512-WTG3sJTtEUpbY1SUd62GU//5npkgVQNJHOUqDjGl1jrTa8Ml0w1VQNe9p/0xbv+3cCTGYOhMvM4HkOZnrQp+gg== dependencies: - "@thirdweb-dev/chains" "0.1.54" - "@thirdweb-dev/contracts-js" "1.3.15" + "@thirdweb-dev/chains" "0.1.58" + "@thirdweb-dev/contracts-js" "1.3.16" + "@thirdweb-dev/crypto" "0.2.0" "@thirdweb-dev/generated-abis" "0.0.1" - "@thirdweb-dev/storage" "2.0.0" + "@thirdweb-dev/merkletree" "0.2.0" + "@thirdweb-dev/storage" "2.0.5" abitype "^0.2.5" bn.js "^5.2.1" bs58 "^5.0.0" buffer "^6.0.3" eventemitter3 "^5.0.1" fast-deep-equal "^3.1.3" - merkletreejs "^0.2.24" tiny-invariant "^1.2.0" tweetnacl "^1.0.3" uuid "^9.0.1" yaml "^2.3.1" zod "^3.22.3" -"@thirdweb-dev/storage@2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@thirdweb-dev/storage/-/storage-2.0.0.tgz#b3e4a34bcbcdd3b2ce3171af76d69a56993e7fa0" - integrity sha512-pfTbiwgrp2N2lrTfa8nLt5E9V1+IGtYKtKU82ReOKKYkRTi0qkqI5ydNuzM2VUcwIyyPnlRR/W7NloHyyBW5/Q== +"@thirdweb-dev/storage@2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@thirdweb-dev/storage/-/storage-2.0.5.tgz#e492923ca0037db7cd8f30572333a3bb2f67639e" + integrity sha512-I3DK/ZNWOMa/XE2hfJnGKVfc9INn5c3if1qavyK/1fjJBxhUiUXjT59UYbuoWhHLEq0rS/QZVOGS/9qcOs/DAQ== dependencies: + "@thirdweb-dev/crypto" "0.2.0" cid-tool "^3.0.0" form-data "^4.0.0" uuid "^9.0.1" "@tsconfig/node10@^1.0.7": - version "1.0.8" - resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz" - integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== "@tsconfig/node12@^1.0.7": - version "1.0.9" - resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz" - integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": - version "1.0.1" - resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz" - integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": - version "1.0.2" - resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz" - integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== -"@typechain/ethers-v5@^10.0.0": - version "10.0.0" - resolved "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-10.0.0.tgz" - integrity sha512-Kot7fwAqnH96ZbI8xrRgj5Kpv9yCEdjo7mxRqrH7bYpEgijT5MmuOo8IVsdhOu7Uog4ONg7k/d5UdbAtTKUgsA== +"@typechain/ethers-v5@^10.2.1": + version "10.2.1" + resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-10.2.1.tgz#50241e6957683281ecfa03fb5a6724d8a3ce2391" + integrity sha512-n3tQmCZjRE6IU4h6lqUGiQ1j866n5MTCBJreNEHHVWXa2u9GJTaeYyU1/k+1qLutkyw+sS6VAN+AbeiTqsxd/A== dependencies: lodash "^4.17.15" ts-essentials "^7.0.1" -"@types/bn.js@^5.1.0": - version "5.1.0" - resolved "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz" - integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== - dependencies: - "@types/node" "*" - "@types/fs-extra@^9.0.13": version "9.0.13" - resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== dependencies: "@types/node" "*" "@types/json-schema@^7.0.9": - version "7.0.11" - resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== - -"@types/mocha@^9.1.0": - version "9.1.0" - resolved "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz" - integrity sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg== + 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/node@*", "@types/node@^17.0.21": - version "17.0.23" - resolved "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz" - integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw== +"@types/mocha@^9.1.1": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" + integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== -"@types/pbkdf2@^3.0.0": - version "3.1.0" - resolved "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz" - integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== +"@types/node@*": + version "20.9.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.9.4.tgz#cc8f970e869c26834bdb7ed480b30ede622d74c7" + integrity sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA== dependencies: - "@types/node" "*" + undici-types "~5.26.4" -"@types/prettier@^2.1.1": - version "2.6.0" - resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.0.tgz" - integrity sha512-G/AdOadiZhnJp0jXCaBQU449W2h716OW/EoXeYkCytxKL06X1WCXB4DZpp8TpZ8eyIJVS1cw4lrlkkSYU21cDw== +"@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/secp256k1@^4.0.1": - version "4.0.3" - resolved "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz" - integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w== - dependencies: - "@types/node" "*" - -"@typescript-eslint/eslint-plugin@^5.13.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.19.0.tgz" - integrity sha512-w59GpFqDYGnWFim9p6TGJz7a3qWeENJuAKCqjGSx+Hq/bwq3RZwXYqy98KIfN85yDqz9mq6QXiY5h0FjGQLyEg== - dependencies: - "@typescript-eslint/scope-manager" "5.19.0" - "@typescript-eslint/type-utils" "5.19.0" - "@typescript-eslint/utils" "5.19.0" - debug "^4.3.2" - functional-red-black-tree "^1.0.1" - ignore "^5.1.8" - regexpp "^3.2.0" - semver "^7.3.5" +"@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/semver@^7.3.12": + version "7.5.6" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" + integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== + +"@typescript-eslint/eslint-plugin@^5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/parser@^5.13.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.19.0.tgz" - integrity sha512-yhktJjMCJX8BSBczh1F/uY8wGRYrBeyn84kH6oyqdIJwTGKmzX5Qiq49LRQ0Jh0LXnWijEziSo6BRqny8nqLVQ== +"@typescript-eslint/parser@^5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== dependencies: - "@typescript-eslint/scope-manager" "5.19.0" - "@typescript-eslint/types" "5.19.0" - "@typescript-eslint/typescript-estree" "5.19.0" - debug "^4.3.2" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + debug "^4.3.4" -"@typescript-eslint/scope-manager@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.19.0.tgz" - integrity sha512-Fz+VrjLmwq5fbQn5W7cIJZ066HxLMKvDEmf4eu1tZ8O956aoX45jAuBB76miAECMTODyUxH61AQM7q4/GOMQ5g== +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== dependencies: - "@typescript-eslint/types" "5.19.0" - "@typescript-eslint/visitor-keys" "5.19.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/type-utils@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.19.0.tgz" - integrity sha512-O6XQ4RI4rQcBGshTQAYBUIGsKqrKeuIOz9v8bckXZnSeXjn/1+BDZndHLe10UplQeJLXDNbaZYrAytKNQO2T4Q== +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== dependencies: - "@typescript-eslint/utils" "5.19.0" - debug "^4.3.2" + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.19.0.tgz" - integrity sha512-zR1ithF4Iyq1wLwkDcT+qFnhs8L5VUtjgac212ftiOP/ZZUOCuuF2DeGiZZGQXGoHA50OreZqLH5NjDcDqn34w== +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/typescript-estree@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.19.0.tgz" - integrity sha512-dRPuD4ocXdaE1BM/dNR21elSEUPKaWgowCA0bqJ6YbYkvtrPVEvZ+zqcX5a8ECYn3q5iBSSUcBBD42ubaOp0Hw== +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== dependencies: - "@typescript-eslint/types" "5.19.0" - "@typescript-eslint/visitor-keys" "5.19.0" - debug "^4.3.2" - globby "^11.0.4" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" is-glob "^4.0.3" - semver "^7.3.5" + semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.19.0.tgz" - integrity sha512-ZuEckdupXpXamKvFz/Ql8YnePh2ZWcwz7APICzJL985Rp5C2AYcHO62oJzIqNhAMtMK6XvrlBTZeNG8n7gS3lQ== +"@typescript-eslint/utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== dependencies: + "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.19.0" - "@typescript-eslint/types" "5.19.0" - "@typescript-eslint/typescript-estree" "5.19.0" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" eslint-scope "^5.1.1" - eslint-utils "^3.0.0" + semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.19.0": - version "5.19.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.19.0.tgz" - integrity sha512-Ym7zZoMDZcAKWsULi2s7UMLREdVQdScPQ/fKWMYefarCztWlHPFVJo8racf8R0Gc8FAEJ2eD4of8As1oFtnQlQ== +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== dependencies: - "@typescript-eslint/types" "5.19.0" - eslint-visitor-keys "^3.0.0" + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" "@ungap/promise-all-settled@1.1.2": version "1.1.2" - resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + abitype@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.2.5.tgz#e571ef2ed99db1cae551fffde5bcbcee4e446177" integrity sha512-t1iiokWYpkrziu4WL2Gb6YdGvaP9ZKs7WnA39TI8TsW2E99GVRgDPW/xOKhzoCdyxOYt550CNYEFluCwGaFHaA== -acorn-jsx@^5.0.0, acorn-jsx@^5.3.1: +acorn-jsx@^5.3.2: version "5.3.2" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: - version "8.2.0" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== - -acorn@^6.0.7: - version "6.4.2" - resolved "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + version "8.3.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.0.tgz#2097665af50fd0cf7a2dfccd2b9368964e66540f" + integrity sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA== -acorn@^8.4.1, acorn@^8.7.0: - version "8.7.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz" - integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== +acorn@^8.4.1, acorn@^8.9.0: + version "8.11.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b" + integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w== 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== -ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.6.1, ajv@^6.9.1: +ajv@^6.12.4, ajv@^6.12.6: version "6.12.6" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -780,98 +873,86 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.6.1, ajv@^6.9.1: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.1: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ansi-colors@4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-escapes@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - -ansi-regex@^3.0.0: - version "3.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz" - integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== - -ansi-regex@^4.1.0: - version "4.1.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz" - integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== - ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" -antlr4@4.7.1: - version "4.7.1" - resolved "https://registry.npmjs.org/antlr4/-/antlr4-4.7.1.tgz" - integrity sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ== +antlr4@^4.11.0: + version "4.13.1" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.13.1.tgz#1e0a1830a08faeb86217cb2e6c34716004e4253d" + integrity sha512-kiXTspaRYvnIArgE97z5YVVf/cDVQABr3abFRR6mE7yesLMkgu4ujuyV/sgxafQ8wgve0DJQUJ38Z8tkgA2izA== antlr4ts@^0.5.0-alpha.4: version "0.5.0-alpha.4" - resolved "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz" + resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== any-promise@^1.0.0: version "1.3.0" - resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" - integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" arg@^4.1.0: version "4.1.3" - resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" + 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.npmjs.org/argparse/-/argparse-1.0.10.tgz" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - argparse@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== array-back@^3.0.1, array-back@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== -array-back@^4.0.1: +array-back@^4.0.1, array-back@^4.0.2: version "4.0.2" - resolved "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== array-union@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== assertion-error@^1.1.0: @@ -879,15 +960,15 @@ assertion-error@^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: +ast-parents@^0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/ast-parents/-/ast-parents-0.0.1.tgz" - integrity sha1-UI/Q8F0MSHddnszaLhdEIyYejdM= + resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" + integrity sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA== -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== asynckit@^0.4.0: version "0.4.0" @@ -896,16 +977,9 @@ asynckit@^0.4.0: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + 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.npmjs.org/base-x/-/base-x-3.0.9.tgz" - integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== - dependencies: - safe-buffer "^5.0.1" - base-x@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" @@ -913,7 +987,7 @@ base-x@^4.0.0: base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== bech32@1.1.4: @@ -921,36 +995,16 @@ bech32@1.1.4: resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== -bignumber.js@^9.0.1: - version "9.0.2" - resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz" - integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== - binary-extensions@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -blakejs@^1.1.0: - version "1.2.1" - resolved "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz" - integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== - -bn.js@4.11.6: - version "4.11.6" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz" - integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= - 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.1.2: - version "5.2.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== - 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" @@ -958,15 +1012,22 @@ bn.js@^5.2.0, bn.js@^5.2.1: brace-expansion@^1.1.7: version "1.1.11" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.2, braces@~3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" @@ -978,28 +1039,9 @@ brorand@^1.1.0: browser-stdout@1.3.1: version "1.3.1" - resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" + 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.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz" - 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.npmjs.org/bs58/-/bs58-4.0.1.tgz" - integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= - dependencies: - base-x "^3.0.2" - bs58@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" @@ -1007,122 +1049,86 @@ bs58@^5.0.0: dependencies: base-x "^4.0.0" -bs58check@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz" - integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== - dependencies: - bs58 "^4.0.0" - create-hash "^1.1.0" - safe-buffer "^5.1.2" - buffer-reverse@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz" + 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.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= - buffer@^6.0.3: version "6.0.3" - resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: base64-js "^1.3.1" ieee754 "^1.2.1" bufio@^1.0.7: - version "1.2.0" - resolved "https://registry.yarnpkg.com/bufio/-/bufio-1.2.0.tgz#b9ad1c06b0d9010363c387c39d2810a7086d143f" - integrity sha512-UlFk8z/PwdhYQTXSQQagwGAdtRI83gib2n4uy4rQnenxUM2yQi8lBDzF230BNk+3wAoZDxYRoBwVVUPgHa9MCA== + version "1.2.1" + resolved "https://registry.yarnpkg.com/bufio/-/bufio-1.2.1.tgz#8d4ab3ddfcd5faa90f996f922f9397d41cbaf2de" + integrity sha512-9oR3zNdupcg/Ge2sSHQF3GX+kmvL/fTPvD0nd5AGLq8SjUYnTz+SlFjK/GXidndbZtIj+pVKXiWeR9w6e9wKCA== bundle-require@^3.0.2: - version "3.0.4" - resolved "https://registry.npmjs.org/bundle-require/-/bundle-require-3.0.4.tgz" - integrity sha512-VXG6epB1yrLAvWVQpl92qF347/UXmncQj7J3U8kZEbdVZ1ZkQyr4hYeL/9RvcE8vVVdp53dY78Fd/3pqfRqI1A== + version "3.1.2" + resolved "https://registry.yarnpkg.com/bundle-require/-/bundle-require-3.1.2.tgz#1374a7bdcb8b330a7ccc862ccbf7c137cc43ad27" + integrity sha512-Of6l6JBAxiyQ5axFxUM6dYeP/W7X2Sozeo/4EYB9sJhL+dqL7TKjg+shwxp6jlu/6ZSERfsYtIpSJ1/x3XkAEA== dependencies: load-tsconfig "^0.2.0" cac@^6.7.12: - version "6.7.12" - resolved "https://registry.npmjs.org/cac/-/cac-6.7.12.tgz" - integrity sha512-rM7E2ygtMkJqD9c7WnFU6fruFcN3xe4FM5yUmgxhZzIKJk4uHl9U/fhwdajGFQbQuv43FAUo1Fe8gX/oIKDeSA== - -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== callsites@^3.0.0: version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camelcase@^6.0.0: version "6.3.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== chai@^4.3.4: - version "4.3.7" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" - integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + version "4.3.10" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384" + integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g== dependencies: assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^4.1.2" - get-func-name "^2.0.0" - loupe "^2.3.1" + 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.5" + type-detect "^4.0.8" -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.2: +chalk@^2.4.2: version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + 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" -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== +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: + get-func-name "^2.0.2" chokidar@3.5.3, chokidar@^3.5.1: version "3.5.3" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: anymatch "~3.1.2" @@ -1158,29 +1164,9 @@ cids@^1.0.0: multihashes "^4.0.1" uint8arrays "^3.0.0" -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= - dependencies: - restore-cursor "^2.0.0" - -cli-width@^2.0.0: - version "2.2.1" - resolved "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz" - integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== - cliui@^7.0.2: version "7.0.4" - resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" @@ -1189,38 +1175,38 @@ cliui@^7.0.2: color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + 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-line-args@^5.1.1: version "5.2.1" - resolved "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz" + resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-5.2.1.tgz#c44c32e437a57d7c51157696893c5909e9cec42e" integrity sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg== dependencies: array-back "^3.1.0" @@ -1229,113 +1215,74 @@ command-line-args@^5.1.1: typical "^4.0.0" command-line-usage@^6.1.0: - version "6.1.2" - resolved "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.2.tgz" - integrity sha512-I+0XN613reAhpBQ6icsPOTwu9cvhc9NtLtUcY2fGYuwm9JZiWBzFDA8w0PHqQjru7Xth7fM/y9TJ13+VKdjh7Q== + version "6.1.3" + resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-6.1.3.tgz#428fa5acde6a838779dfa30e44686f4b6761d957" + integrity sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw== dependencies: - array-back "^4.0.1" + array-back "^4.0.2" chalk "^2.4.2" - table-layout "^1.0.1" + table-layout "^1.0.2" typical "^5.2.0" -commander@2.18.0: - version "2.18.0" - resolved "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz" - integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== commander@^4.0.0: version "4.1.1" - resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== concat-map@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -cosmiconfig@^5.0.7: - version "5.2.1" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz" - 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.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== +cosmiconfig@^8.0.0: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== 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" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" create-require@^1.1.0: version "1.1.1" - resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" which "^2.0.1" -crypto-js@^3.1.9-1: - version "3.3.0" - resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz" - integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== - debug@4.3.3: version "4.3.3" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== dependencies: ms "2.1.2" -debug@^4.0.1, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: +debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" decamelize@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -deep-eql@^4.1.2: +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== @@ -1344,12 +1291,12 @@ deep-eql@^4.1.2: deep-extend@~0.6.0: version "0.6.0" - resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz" + 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.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== delayed-stream@~1.0.0: @@ -1359,34 +1306,34 @@ delayed-stream@~1.0.0: diff@5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== diff@^4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" doctrine@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== dependencies: esutils "^2.0.2" -dotenv@^16.0.0: - version "16.0.0" - resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz" - integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q== +dotenv@^16.3.1: + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== -elliptic@6.5.4, elliptic@^6.5.4: +elliptic@6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== @@ -1399,24 +1346,14 @@ elliptic@6.5.4, elliptic@^6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emoji-regex@^10.0.0: - version "10.1.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.1.0.tgz" - integrity sha512-xAEnNCT3w2Tg6MA7ly6QqYJvEoY1tm9iIjJ3yMKK9JPlWuRHAMoe5iETwQnx3M9TVbFMfsrBgWKR+IsmswwNjg== - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== erc721a-upgradeable@^3.3.0: version "3.3.0" - resolved "https://registry.npmjs.org/erc721a-upgradeable/-/erc721a-upgradeable-3.3.0.tgz" + resolved "https://registry.yarnpkg.com/erc721a-upgradeable/-/erc721a-upgradeable-3.3.0.tgz#c7b481668694756120868261fe98ab3a245a06b4" integrity sha512-ILE0SjKuvhx+PABG0A/41QUp0MFiYmzrgo71htQ0Ov6JfDOmgUzGxDW8gZuYfKrdlYjNwSAqMpUFWBbyW3sWBA== dependencies: "@openzeppelin/contracts-upgradeable" "^4.4.2" @@ -1430,385 +1367,262 @@ erc721a@3.3.0: error-ex@^1.3.1: version "1.3.2" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" -esbuild-android-64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.36.tgz#fc5f95ce78c8c3d790fa16bc71bd904f2bb42aa1" - integrity sha512-jwpBhF1jmo0tVCYC/ORzVN+hyVcNZUWuozGcLHfod0RJCedTDTvR4nwlTXdx1gtncDqjk33itjO+27OZHbiavw== - -esbuild-android-arm64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.36.tgz#44356fbb9f8de82a5cdf11849e011dfb3ad0a8a8" - integrity sha512-/hYkyFe7x7Yapmfv4X/tBmyKnggUmdQmlvZ8ZlBnV4+PjisrEhAvC3yWpURuD9XoB8Wa1d5dGkTsF53pIvpjsg== - -esbuild-darwin-64@0.14.36: - version "0.14.36" - resolved "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.36.tgz" - integrity sha512-kkl6qmV0dTpyIMKagluzYqlc1vO0ecgpviK/7jwPbRDEv5fejRTaBBEE2KxEQbTHcLhiiDbhG7d5UybZWo/1zQ== - -esbuild-darwin-arm64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.36.tgz#2a8040c2e465131e5281034f3c72405e643cb7b2" - integrity sha512-q8fY4r2Sx6P0Pr3VUm//eFYKVk07C5MHcEinU1BjyFnuYz4IxR/03uBbDwluR6ILIHnZTE7AkTUWIdidRi1Jjw== - -esbuild-freebsd-64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.36.tgz#d82c387b4d01fe9e8631f97d41eb54f2dbeb68a3" - integrity sha512-Hn8AYuxXXRptybPqoMkga4HRFE7/XmhtlQjXFHoAIhKUPPMeJH35GYEUWGbjteai9FLFvBAjEAlwEtSGxnqWww== - -esbuild-freebsd-arm64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.36.tgz#e8ce2e6c697da6c7ecd0cc0ac821d47c5ab68529" - integrity sha512-S3C0attylLLRiCcHiJd036eDEMOY32+h8P+jJ3kTcfhJANNjP0TNBNL30TZmEdOSx/820HJFgRrqpNAvTbjnDA== - -esbuild-linux-32@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.36.tgz#a4a261e2af91986ea62451f2db712a556cb38a15" - integrity sha512-Eh9OkyTrEZn9WGO4xkI3OPPpUX7p/3QYvdG0lL4rfr73Ap2HAr6D9lP59VMF64Ex01LhHSXwIsFG/8AQjh6eNw== - -esbuild-linux-64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.36.tgz#4a9500f9197e2c8fcb884a511d2c9d4c2debde72" - integrity sha512-vFVFS5ve7PuwlfgoWNyRccGDi2QTNkQo/2k5U5ttVD0jRFaMlc8UQee708fOZA6zTCDy5RWsT5MJw3sl2X6KDg== - -esbuild-linux-arm64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.36.tgz#c91c21e25b315464bd7da867365dd1dae14ca176" - integrity sha512-24Vq1M7FdpSmaTYuu1w0Hdhiqkbto1I5Pjyi+4Cdw5fJKGlwQuw+hWynTcRI/cOZxBcBpP21gND7W27gHAiftw== - -esbuild-linux-arm@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.36.tgz#90e23bca2e6e549affbbe994f80ba3bb6c4d934a" - integrity sha512-NhgU4n+NCsYgt7Hy61PCquEz5aevI6VjQvxwBxtxrooXsxt5b2xtOUXYZe04JxqQo+XZk3d1gcr7pbV9MAQ/Lg== - -esbuild-linux-mips64le@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.36.tgz#40e11afb08353ff24709fc89e4db0f866bc131d2" - integrity sha512-hZUeTXvppJN+5rEz2EjsOFM9F1bZt7/d2FUM1lmQo//rXh1RTFYzhC0txn7WV0/jCC7SvrGRaRz0NMsRPf8SIA== - -esbuild-linux-ppc64le@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.36.tgz#9e8a588c513d06cc3859f9dcc52e5fdfce8a1a5e" - integrity sha512-1Bg3QgzZjO+QtPhP9VeIBhAduHEc2kzU43MzBnMwpLSZ890azr4/A9Dganun8nsqD/1TBcqhId0z4mFDO8FAvg== - -esbuild-linux-riscv64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.36.tgz#e578c09b23b3b97652e60e3692bfda628b541f06" - integrity sha512-dOE5pt3cOdqEhaufDRzNCHf5BSwxgygVak9UR7PH7KPVHwSTDAZHDoEjblxLqjJYpc5XaU9+gKJ9F8mp9r5I4A== - -esbuild-linux-s390x@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.36.tgz#3c9dab40d0d69932ffded0fd7317bb403626c9bc" - integrity sha512-g4FMdh//BBGTfVHjF6MO7Cz8gqRoDPzXWxRvWkJoGroKA18G9m0wddvPbEqcQf5Tbt2vSc1CIgag7cXwTmoTXg== - -esbuild-netbsd-64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.36.tgz#e27847f6d506218291619b8c1e121ecd97628494" - integrity sha512-UB2bVImxkWk4vjnP62ehFNZ73lQY1xcnL5ZNYF3x0AG+j8HgdkNF05v67YJdCIuUJpBuTyCK8LORCYo9onSW+A== - -esbuild-openbsd-64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.36.tgz#c94c04c557fae516872a586eae67423da6d2fabb" - integrity sha512-NvGB2Chf8GxuleXRGk8e9zD3aSdRO5kLt9coTQbCg7WMGXeX471sBgh4kSg8pjx0yTXRt0MlrUDnjVYnetyivg== - -esbuild-sunos-64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.36.tgz#9b79febc0df65a30f1c9bd63047d1675511bf99d" - integrity sha512-VkUZS5ftTSjhRjuRLp+v78auMO3PZBXu6xl4ajomGenEm2/rGuWlhFSjB7YbBNErOchj51Jb2OK8lKAo8qdmsQ== - -esbuild-windows-32@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.36.tgz#910d11936c8d2122ffdd3275e5b28d8a4e1240ec" - integrity sha512-bIar+A6hdytJjZrDxfMBUSEHHLfx3ynoEZXx/39nxy86pX/w249WZm8Bm0dtOAByAf4Z6qV0LsnTIJHiIqbw0w== - -esbuild-windows-64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.36.tgz#21b4ce8b42a4efc63f4b58ec617f1302448aad26" - integrity sha512-+p4MuRZekVChAeueT1Y9LGkxrT5x7YYJxYE8ZOTcEfeUUN43vktSn6hUNsvxzzATrSgq5QqRdllkVBxWZg7KqQ== - -esbuild-windows-arm64@0.14.36: - version "0.14.36" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.36.tgz#ba21546fecb7297667d0052d00150de22c044b24" - integrity sha512-fBB4WlDqV1m18EF/aheGYQkQZHfPHiHJSBYzXIo8yKehek+0BtBwo/4PNwKGJ5T0YK0oc8pBKjgwPbzSrPLb+Q== +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" + integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ== + +esbuild-android-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771" + integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg== + +esbuild-darwin-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25" + integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug== + +esbuild-darwin-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73" + integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw== + +esbuild-freebsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d" + integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg== + +esbuild-freebsd-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48" + integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q== + +esbuild-linux-32@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5" + integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw== + +esbuild-linux-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652" + integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg== + +esbuild-linux-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b" + integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig== + +esbuild-linux-arm@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59" + integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw== + +esbuild-linux-mips64le@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34" + integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw== + +esbuild-linux-ppc64le@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e" + integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ== + +esbuild-linux-riscv64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8" + integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg== + +esbuild-linux-s390x@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6" + integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA== + +esbuild-netbsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81" + integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w== + +esbuild-openbsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b" + integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw== + +esbuild-sunos-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da" + integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw== + +esbuild-windows-32@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31" + integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w== + +esbuild-windows-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4" + integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ== + +esbuild-windows-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982" + integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg== esbuild@^0.14.25: - version "0.14.36" - resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.36.tgz" - integrity sha512-HhFHPiRXGYOCRlrhpiVDYKcFJRdO0sBElZ668M4lh2ER0YgnkLxECuFe7uWCf23FrcLc59Pqr7dHkTqmRPDHmw== + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2" + integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA== optionalDependencies: - esbuild-android-64 "0.14.36" - esbuild-android-arm64 "0.14.36" - esbuild-darwin-64 "0.14.36" - esbuild-darwin-arm64 "0.14.36" - esbuild-freebsd-64 "0.14.36" - esbuild-freebsd-arm64 "0.14.36" - esbuild-linux-32 "0.14.36" - esbuild-linux-64 "0.14.36" - esbuild-linux-arm "0.14.36" - esbuild-linux-arm64 "0.14.36" - esbuild-linux-mips64le "0.14.36" - esbuild-linux-ppc64le "0.14.36" - esbuild-linux-riscv64 "0.14.36" - esbuild-linux-s390x "0.14.36" - esbuild-netbsd-64 "0.14.36" - esbuild-openbsd-64 "0.14.36" - esbuild-sunos-64 "0.14.36" - esbuild-windows-32 "0.14.36" - esbuild-windows-64 "0.14.36" - esbuild-windows-arm64 "0.14.36" + "@esbuild/linux-loong64" "0.14.54" + esbuild-android-64 "0.14.54" + esbuild-android-arm64 "0.14.54" + esbuild-darwin-64 "0.14.54" + esbuild-darwin-arm64 "0.14.54" + esbuild-freebsd-64 "0.14.54" + esbuild-freebsd-arm64 "0.14.54" + esbuild-linux-32 "0.14.54" + esbuild-linux-64 "0.14.54" + esbuild-linux-arm "0.14.54" + esbuild-linux-arm64 "0.14.54" + esbuild-linux-mips64le "0.14.54" + esbuild-linux-ppc64le "0.14.54" + esbuild-linux-riscv64 "0.14.54" + esbuild-linux-s390x "0.14.54" + esbuild-netbsd-64 "0.14.54" + esbuild-openbsd-64 "0.14.54" + esbuild-sunos-64 "0.14.54" + esbuild-windows-32 "0.14.54" + esbuild-windows-64 "0.14.54" + esbuild-windows-arm64 "0.14.54" escalade@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -eslint-config-prettier@^8.5.0: - version "8.5.0" - resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz" - integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" +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" + integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== eslint-scope@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-utils@^1.3.1: - version "1.4.3" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz" - integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== - dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - -eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: - version "1.3.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== - -eslint@^5.6.0: - version "5.16.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz" - integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.9.1" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" - doctrine "^3.0.0" - eslint-scope "^4.0.3" - eslint-utils "^1.3.1" - eslint-visitor-keys "^1.0.0" - espree "^5.0.1" - esquery "^1.0.1" - esutils "^2.0.2" - file-entry-cache "^5.0.1" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.7.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - inquirer "^6.2.2" - js-yaml "^3.13.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.11" - minimatch "^3.0.4" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - progress "^2.0.0" - regexpp "^2.0.1" - semver "^5.5.1" - strip-ansi "^4.0.0" - strip-json-comments "^2.0.1" - table "^5.2.3" - text-table "^0.2.0" - -eslint@^8.10.0: - version "8.13.0" - resolved "https://registry.npmjs.org/eslint/-/eslint-8.13.0.tgz" - integrity sha512-D+Xei61eInqauAyTJ6C0q6x9mx7kTUC1KZ0m0LSEexR0V+e94K12LmWX076ZIsldwfQ2RONdaJe0re0TRGQbRQ== - dependencies: - "@eslint/eslintrc" "^1.2.1" - "@humanwhocodes/config-array" "^0.9.2" - ajv "^6.10.0" +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.54.0: + version "8.54.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.54.0.tgz#588e0dd4388af91a2e8fa37ea64924074c783537" + integrity sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.3" + "@eslint/js" "8.54.0" + "@humanwhocodes/config-array" "^0.11.13" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.3.1" - esquery "^1.4.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^6.0.1" - globals "^13.6.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" + is-path-inside "^3.0.3" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" - minimatch "^3.0.4" + minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" + optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz" - integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^6.0.7" - acorn-jsx "^5.0.0" - eslint-visitor-keys "^1.0.0" - -espree@^9.3.1: - version "9.3.1" - resolved "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz" - integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== - dependencies: - acorn "^8.7.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^3.3.0" + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.0.1, esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" -esrecurse@^4.1.0, esrecurse@^4.3.0: +esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^4.1.1: version "4.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== estraverse@^5.1.0, estraverse@^5.2.0: version "5.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -ethereum-bloom-filters@^1.0.6: - version "1.0.10" - resolved "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz" - integrity sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA== - dependencies: - js-sha3 "^0.8.0" - -ethereum-cryptography@^0.1.3: - version "0.1.3" - resolved "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz" - 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" - -ethereumjs-util@^7.1.0: - version "7.1.4" - resolved "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.4.tgz" - integrity sha512-p6KmuPCX4mZIqsQzXfmSx9Y0l2hqf+VkAiwSisW3UKUFdk8ZkAt+AYaor83z2nSi6CU2zSsXMlD80hAbNEGM0A== - 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.0.0: +ethers@^5.7.2: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -1844,30 +1658,14 @@ ethers@^5.0.0: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" -ethjs-unit@0.1.6: - version "0.1.6" - resolved "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz" - integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk= - dependencies: - bn.js "4.11.6" - number-to-bn "1.7.0" - eventemitter3@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== -evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz" - 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.npmjs.org/execa/-/execa-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -1885,29 +1683,20 @@ explain-error@^1.0.4: resolved "https://registry.yarnpkg.com/explain-error/-/explain-error-1.0.4.tgz#a793d3ac0cad4c6ab571e9968fbbab6cb2532929" integrity sha512-/wSgNMxFusiYRy1rd19LT2SQlIXDppHpumpWo06wxjflD1OYxDLbl6rMVw+U3bxD5Nuhex4TKqv9Aem4D0lVzQ== -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-diff@^1.1.2: - version "1.2.0" - resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz" - integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== +fast-diff@^1.1.2, fast-diff@^1.2.0: + version "1.3.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: - version "3.2.11" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -1917,95 +1706,68 @@ fast-glob@^3.2.9: fast-json-stable-stringify@^2.0.0: version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + 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.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== dependencies: reusify "^1.0.4" -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== - dependencies: - flat-cache "^2.0.1" - file-entry-cache@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: flat-cache "^3.0.4" fill-range@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: to-regex-range "^5.0.1" find-replace@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-3.0.0.tgz#3e7e23d3b05167a76f770c9fbd5258b0def68c38" integrity sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ== dependencies: array-back "^3.0.1" -find-up@5.0.0: +find-up@5.0.0, find-up@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" path-exists "^4.0.0" -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== - dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" - flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== dependencies: - flatted "^3.1.0" + flatted "^3.2.9" + keyv "^4.5.3" rimraf "^3.0.2" flat@^5.0.2: version "5.0.2" - resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -flatted@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== - -flatted@^3.1.0: - version "3.2.5" - resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz" - integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== form-data@^4.0.0: version "4.0.0" @@ -2016,10 +1778,10 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -fs-extra@^10.0.1: - version "10.0.1" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz" - integrity sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag== +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -2027,7 +1789,7 @@ fs-extra@^10.0.1: fs-extra@^7.0.0: version "7.0.1" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== dependencies: graceful-fs "^4.1.2" @@ -2036,51 +1798,46 @@ fs-extra@^7.0.0: fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + 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.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== +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-stream@^6.0.0: version "6.0.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob-parent@^6.0.1: +glob-parent@^6.0.2: version "6.0.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: is-glob "^4.0.3" glob@7.1.6: version "7.1.6" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" @@ -2092,7 +1849,7 @@ glob@7.1.6: glob@7.1.7: version "7.1.7" - resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" @@ -2102,9 +1859,9 @@ glob@7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.2.0, glob@^7.1.2, glob@^7.1.3: +glob@7.2.0: version "7.2.0" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" @@ -2114,21 +1871,39 @@ glob@7.2.0, glob@^7.1.2, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^11.7.0: - version "11.12.0" - resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +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== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" -globals@^13.6.0, globals@^13.9.0: - version "13.13.0" - resolved "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz" - integrity sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A== +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" + +globals@^13.19.0: + version "13.23.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.23.0.tgz#ef31673c926a0976e1f61dab4dca57e0c0a8af02" + integrity sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA== dependencies: type-fest "^0.20.2" -globby@^11.0.3, globby@^11.0.4: +globby@^11.0.3, globby@^11.1.0: version "11.1.0" - resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" @@ -2139,35 +1914,31 @@ globby@^11.0.3, globby@^11.0.4: slash "^3.0.0" graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: - version "4.2.10" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + 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== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== growl@1.10.5: version "1.10.5" - resolved "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz" - 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: +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== @@ -2177,7 +1948,7 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: he@1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== hmac-drbg@^1.0.1: @@ -2191,42 +1962,22 @@ hmac-drbg@^1.0.1: human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -ignore@^5.1.8, ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== - -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" +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== -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" @@ -2234,113 +1985,84 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inquirer@^6.2.2: - version "6.5.2" - resolved "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz" - integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== - dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.12" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" - is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-binary-path@~2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: binary-extensions "^2.0.0" -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= - is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" -is-hex-prefixed@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz" - integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= - is-number@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +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" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-obj@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-stream@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== isexe@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== joycon@^3.0.1: version "3.1.1" - resolved "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== js-sha3@0.8.0, js-sha3@^0.8.0: @@ -2348,51 +2070,58 @@ js-sha3@0.8.0, js-sha3@^0.8.0: resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== +js-sha3@^0.9.2: + version "0.9.2" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.9.2.tgz#a5ba3967ddf5a095f7b3389ef14a6297b10d6409" + integrity sha512-8kgvwd03wNGQG1GRvl3yy1Yt40sICAcIMsDU2ZLgoL0Z6z9rkRmf9Vd+bi/gYSzgAqMUGl/jiDKu0J8AWFd+BQ== + js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" -js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + 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@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== optionalDependencies: graceful-fs "^4.1.6" jsonfile@^6.0.1: version "6.1.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" @@ -2401,146 +2130,124 @@ jsonfile@^6.0.1: keccak256@^1.0.6: version "1.0.6" - resolved "https://registry.npmjs.org/keccak256/-/keccak256-1.0.6.tgz" + resolved "https://registry.yarnpkg.com/keccak256/-/keccak256-1.0.6.tgz#dd32fb771558fed51ce4e45a035ae7515573da58" integrity sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw== dependencies: bn.js "^5.2.0" buffer "^6.0.3" keccak "^3.0.2" -keccak@^3.0.0, keccak@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz" - integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== +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== dependencies: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" readable-stream "^3.6.0" -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" + json-buffer "3.0.1" levn@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" type-check "~0.4.0" lilconfig@^2.0.5: - version "2.0.5" - resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz" - integrity sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg== + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== load-tsconfig@^0.2.0: - version "0.2.3" - resolved "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.3.tgz" - integrity sha512-iyT2MXws+dc2Wi6o3grCFtGXpeMvHmJqS27sMPGtV2eUu4PeFnG+33I8BlFK1t1NWMjOpcx9bridn5yxLDX2gQ== + version "0.2.5" + resolved "https://registry.yarnpkg.com/load-tsconfig/-/load-tsconfig-0.2.5.tgz#453b8cd8961bfb912dea77eb6c168fe8cca3d3a1" + integrity sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg== locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" lodash.camelcase@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" - integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== lodash.merge@^4.6.2: version "4.6.2" - resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15: +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== + +lodash.truncate@^4.4.2: + version "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: version "4.17.21" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" is-unicode-supported "^0.1.0" -loupe@^2.3.1: - version "2.3.6" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" - integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== +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.0" + get-func-name "^2.0.1" lru-cache@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== dependencies: yallist "^4.0.0" make-error@^1.1.1: version "1.3.6" - resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + 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: version "1.4.1" - resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -merkletreejs@^0.2.24: - version "0.2.32" - resolved "https://registry.yarnpkg.com/merkletreejs/-/merkletreejs-0.2.32.tgz#cf1c0760e2904e4a1cc269108d6009459fd06223" - integrity sha512-TostQBiwYRIwSE5++jGmacu3ODcKAgqb0Y/pnIohXS7sWxh1gCkSptbmF1a43faehRDpcHf7J/kv0Ml2D/zblQ== - dependencies: - bignumber.js "^9.0.1" - buffer-reverse "^1.0.1" - crypto-js "^3.1.9-1" - treeify "^1.1.0" - web3-utils "^1.3.4" - -merkletreejs@^0.2.31: - version "0.2.31" - resolved "https://registry.npmjs.org/merkletreejs/-/merkletreejs-0.2.31.tgz" - integrity sha512-dnK2sE43OebmMe5Qnq1wXvvMIjZjm1u6CcB2KeW6cghlN4p21OpCUr2p56KTVf20KJItNChVsGnimcscp9f+yw== - dependencies: - bignumber.js "^9.0.1" - buffer-reverse "^1.0.1" - crypto-js "^3.1.9-1" - treeify "^1.1.0" - web3-utils "^1.3.4" - micromatch@^4.0.4: version "4.0.5" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: braces "^3.0.2" @@ -2553,19 +2260,14 @@ mime-db@1.52.0: mime-types@^2.1.12: version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + 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@^1.0.0: - version "1.2.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: @@ -2580,38 +2282,33 @@ minimalistic-crypto-utils@^1.0.1: minimatch@4.2.1: version "4.2.1" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.4: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - -mkdirp@^0.5.1: - version "0.5.6" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: - minimist "^1.2.6" + brace-expansion "^2.0.1" mkdirp@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mocha@^9.2.1: +mocha@^9.2.2: version "9.2.2" - resolved "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== dependencies: "@ungap/promise-all-settled" "1.1.2" @@ -2641,12 +2338,12 @@ mocha@^9.2.1: ms@2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== ms@2.1.3: version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== multibase@^4.0.1, multibase@^4.0.2: @@ -2678,14 +2375,9 @@ multihashes@^4.0.1, multihashes@^4.0.2: uint8arrays "^3.0.0" varint "^5.0.2" -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz" - integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= - mz@^2.7.0: version "2.7.0" - resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== dependencies: any-promise "^1.0.0" @@ -2694,161 +2386,121 @@ mz@^2.7.0: nanoid@3.3.1: version "3.3.1" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== -natural-compare@^1.4.0: +natural-compare-lite@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== node-addon-api@^2.0.0: version "2.0.2" - resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== node-gyp-build@^4.2.0: - version "4.4.0" - resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz" - integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== + version "4.7.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.7.0.tgz#749f0033590b2a89ac8edb5e0775f95f5ae86d15" + integrity sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg== normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" -number-to-bn@1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz" - integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= - dependencies: - bn.js "4.11.6" - strip-hex-prefix "1.0.0" - object-assign@^4.0.1: version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== once@^1.3.0: version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= - dependencies: - mimic-fn "^1.0.0" - onetime@^5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" -optionator@^0.8.2: - version "0.8.3" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" - 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.1: - version "0.9.1" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" - -os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= p-limit@^3.0.2: version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= +parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: + "@babel/code-frame" "^7.0.0" error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - -path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-type@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== pathval@^1.1.1: @@ -2856,30 +2508,24 @@ pathval@^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.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz" - 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.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pirates@^4.0.1: - version "4.0.5" - resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pluralize@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" + integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== postcss-load-config@^3.0.1: version "3.1.4" - resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== dependencies: lilconfig "^2.0.5" @@ -2887,66 +2533,48 @@ postcss-load-config@^3.0.1: prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + 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.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - prettier-linter-helpers@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== dependencies: fast-diff "^1.1.2" -prettier-plugin-solidity@^1.0.0-beta.19: - version "1.0.0-beta.19" - resolved "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.19.tgz" - integrity sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g== +prettier-plugin-solidity@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.2.0.tgz#dc620b4fc7708a60687a87cdc803e57a1856b6fd" + integrity sha512-fgxcUZpVAP+LlRfy5JI5oaAkXGkmsje2VJ5krv/YMm+rcTZbIUwFguSw5f+WFuttMjpDm6wB4UL7WVkArEfiVA== dependencies: - "@solidity-parser/parser" "^0.14.0" - emoji-regex "^10.0.0" - escape-string-regexp "^4.0.0" - semver "^7.3.5" + "@solidity-parser/parser" "^0.16.2" + semver "^7.5.4" solidity-comments-extractor "^0.0.7" - string-width "^4.2.3" - -prettier@^1.14.3: - version "1.19.1" - resolved "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz" - integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== -prettier@^2.3.1, prettier@^2.5.1: - version "2.6.2" - resolved "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz" - integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== - -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +prettier@^2.3.1, prettier@^2.8.3, prettier@^2.8.8: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== randombytes@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" -readable-stream@^3.0.0: +readable-stream@^3.0.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== @@ -2955,267 +2583,162 @@ readable-stream@^3.0.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - readdirp@~3.6.0: version "3.6.0" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" reduce-flatten@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== - -regexpp@^3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= +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@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - reusify@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - rimraf@^3.0.2: version "3.0.2" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -rlp@^2.2.4: - version "2.2.7" - resolved "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz" - integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== - dependencies: - bn.js "^5.2.0" - -rollup@^2.60.0: - version "2.70.1" - resolved "https://registry.npmjs.org/rollup/-/rollup-2.70.1.tgz" - integrity sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA== +rollup@^2.74.1: + version "2.79.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" + integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== optionalDependencies: fsevents "~2.3.2" -run-async@^2.2.0: - version "2.4.1" - resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" -rxjs@^6.4.0: - version "6.6.7" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - -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: +safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -scrypt-js@3.0.1, scrypt-js@^3.0.0: +scrypt-js@3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== -secp256k1@^4.0.1: - version "4.0.3" - resolved "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz" - 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, semver@^5.5.1: - version "5.7.1" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.3.0: - version "6.3.0" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.5: - version "7.3.7" - resolved "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== +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" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" serialize-javascript@6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.3: version "3.0.7" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== slash@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" solhint-plugin-prettier@^0.0.5: version "0.0.5" - resolved "https://registry.npmjs.org/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz" + resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz#e3b22800ba435cd640a9eca805a7f8bc3e3e6a6b" integrity sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA== dependencies: prettier-linter-helpers "^1.0.0" -solhint@^3.3.7: - version "3.3.7" - resolved "https://registry.npmjs.org/solhint/-/solhint-3.3.7.tgz" - integrity sha512-NjjjVmXI3ehKkb3aNtRJWw55SUVJ8HMKKodwe0HnejA+k0d2kmhw7jvpa+MCTbcEgt8IWSwx0Hu6aCo/iYOZzQ== - dependencies: - "@solidity-parser/parser" "^0.14.1" - ajv "^6.6.1" - antlr4 "4.7.1" - ast-parents "0.0.1" - chalk "^2.4.2" - commander "2.18.0" - cosmiconfig "^5.0.7" - eslint "^5.6.0" - fast-diff "^1.1.2" - glob "^7.1.3" - ignore "^4.0.6" - js-yaml "^3.12.0" - lodash "^4.17.11" - semver "^6.3.0" +solhint@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.6.2.tgz#2b2acbec8fdc37b2c68206a71ba89c7f519943fe" + integrity sha512-85EeLbmkcPwD+3JR7aEMKsVC9YrRSxd4qkXuMzrlf7+z2Eqdfm1wHWq1ffTuo5aDhoZxp2I9yF3QkxZOxOL7aQ== + dependencies: + "@solidity-parser/parser" "^0.16.0" + ajv "^6.12.6" + antlr4 "^4.11.0" + ast-parents "^0.0.1" + chalk "^4.1.2" + commander "^10.0.0" + cosmiconfig "^8.0.0" + fast-diff "^1.2.0" + glob "^8.0.3" + ignore "^5.2.4" + js-yaml "^4.1.0" + lodash "^4.17.21" + pluralize "^8.0.0" + semver "^7.5.2" + strip-ansi "^6.0.1" + table "^6.8.1" + text-table "^0.2.0" optionalDependencies: - prettier "^1.14.3" + prettier "^2.8.3" solidity-comments-extractor@^0.0.7: version "0.0.7" - resolved "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz" + resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19" integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== -source-map@^0.7.3: - version "0.7.3" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== +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" + integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== + dependencies: + whatwg-url "^7.0.0" split2@^3.1.1: version "3.2.2" @@ -3224,36 +2747,14 @@ split2@^3.1.1: dependencies: readable-stream "^3.0.0" -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - string-format@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/string-format/-/string-format-2.0.0.tgz#f2df2e7097440d3b65de31b6d40d54c96eaffb9b" integrity sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA== -string-width@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string-width@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -3262,59 +2763,34 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: string_decoder@^1.1.1: version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-ansi@^5.1.0: - version "5.2.0" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + 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.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz" - integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= - dependencies: - is-hex-prefixed "1.0.0" - -strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strip-json-comments@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - sucrase@^3.20.3: - version "3.21.0" - resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.21.0.tgz" - integrity sha512-FjAhMJjDcifARI7bZej0Bi1yekjWQHoEvWIXhLPwDhC6O4iZ5PtGb86WV56riW87hzpgB13wwBKO9vKAiWu5VQ== + version "3.34.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.34.0.tgz#1e0e2d8fcf07f8b9c3569067d92fbd8690fb576f" + integrity sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw== dependencies: + "@jridgewell/gen-mapping" "^0.3.2" commander "^4.0.0" glob "7.1.6" lines-and-columns "^1.1.6" @@ -3324,28 +2800,28 @@ sucrase@^3.20.3: supports-color@8.1.1: version "8.1.1" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + 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" -table-layout@^1.0.1: +table-layout@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04" integrity sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A== dependencies: array-back "^4.0.1" @@ -3353,73 +2829,69 @@ table-layout@^1.0.1: typical "^5.2.0" wordwrapjs "^4.0.0" -table@^5.2.3: - version "5.4.6" - resolved "https://registry.npmjs.org/table/-/table-5.4.6.tgz" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== +table@^6.8.1: + version "6.8.1" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" + integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA== dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" text-table@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== thenify-all@^1.0.0: version "1.6.0" - resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz" - integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== dependencies: thenify ">= 3.1.0 < 4" "thenify@>= 3.1.0 < 4": version "3.3.1" - resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== dependencies: any-promise "^1.0.0" -through@^2.3.6: - version "2.3.8" - resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - tiny-invariant@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" - integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" + version "1.3.1" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642" + integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw== to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== + dependencies: + punycode "^2.1.0" + tree-kill@^1.2.2: version "1.2.2" - resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== treeify@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== ts-command-line-args@^2.2.0: - version "2.2.1" - resolved "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.2.1.tgz" - integrity sha512-mnK68QA86FYzQYTSA/rxIjT/8EpKsvQw9QkawPic8I8t0gjAOw3Oa509NIRoaY1FmH7hdrncMp7t7o+vYoceNQ== + version "2.5.1" + resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz#e64456b580d1d4f6d948824c274cf6fa5f45f7f0" + integrity sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw== dependencies: chalk "^4.1.0" command-line-args "^5.1.1" @@ -3428,20 +2900,20 @@ ts-command-line-args@^2.2.0: ts-essentials@^7.0.1: version "7.0.3" - resolved "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== ts-interface-checker@^0.1.9: version "0.1.13" - resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== -ts-node@^10.6.0: - version "10.7.0" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz" - integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A== +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== dependencies: - "@cspotcode/source-map-support" "0.7.0" + "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" "@tsconfig/node12" "^1.0.7" "@tsconfig/node14" "^1.0.0" @@ -3452,23 +2924,23 @@ ts-node@^10.6.0: create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" - v8-compile-cache-lib "^3.0.0" + v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.8.1: version "1.14.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.3.1: - version "2.3.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -tsup@^5.11.11: - version "5.12.5" - resolved "https://registry.npmjs.org/tsup/-/tsup-5.12.5.tgz" - integrity sha512-lKwzJsB49sDto51QjqOB4SdiBLKRvgTymEBuBCovcksdDwFEz3esrkbf3m497PXntUKVTzcgOfPdTgknMtvufw== +tsup@^5.12.9: + version "5.12.9" + resolved "https://registry.yarnpkg.com/tsup/-/tsup-5.12.9.tgz#8cdd9b4bc6493317cb92edf5f3476920dddcdb18" + integrity sha512-dUpuouWZYe40lLufo64qEhDpIDsWhRbr2expv5dHEMjwqeKJS2aXA/FPqs1dxO4T6mBojo7rvo3jP9NNzaKyDg== dependencies: bundle-require "^3.0.2" cac "^6.7.12" @@ -3480,51 +2952,44 @@ tsup@^5.11.11: joycon "^3.0.1" postcss-load-config "^3.0.1" resolve-from "^5.0.0" - rollup "^2.60.0" - source-map "^0.7.3" + rollup "^2.74.1" + source-map "0.8.0-beta.0" sucrase "^3.20.3" tree-kill "^1.2.2" tsutils@^3.21.0: version "3.21.0" - resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" tweetnacl@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz" + 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.npmjs.org/type-check/-/type-check-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - -type-detect@^4.0.0, type-detect@^4.0.5: +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.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== -typechain@^8.0.0: - version "8.0.0" - resolved "https://registry.npmjs.org/typechain/-/typechain-8.0.0.tgz" - integrity sha512-rqDfDYc9voVAhmfVfAwzg3VYFvhvs5ck1X9T/iWkX745Cul4t+V/smjnyqrbDzWDbzD93xfld1epg7Y/uFAesQ== +typechain@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.3.2.tgz#1090dd8d9c57b6ef2aed3640a516bdbf01b00d73" + integrity sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q== dependencies: "@types/prettier" "^2.1.1" debug "^4.3.1" @@ -3537,19 +3002,24 @@ typechain@^8.0.0: ts-command-line-args "^2.2.0" ts-essentials "^7.0.1" -typescript@^4.4.4: - version "4.6.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz" - integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw== +typescript@^4.9.5: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +typescript@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43" + integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ== typical@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" integrity sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw== typical@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/typical/-/typical-5.2.0.tgz#4daaac4f2b5315460804f0acf6cb69c52bb93066" integrity sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg== uint8arrays@^2.1.3: @@ -3566,47 +3036,42 @@ uint8arrays@^3.0.0: dependencies: multiformats "^9.4.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== + universalify@^0.1.0: version "0.1.2" - resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" -utf8@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz" - integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== - util-deprecate@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== -v8-compile-cache-lib@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz" - integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA== - -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== +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== varint@^5.0.2: version "5.0.2" @@ -3618,41 +3083,30 @@ varint@^6.0.0: resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0" integrity sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg== -web3-utils@^1.3.4: - version "1.7.3" - resolved "https://registry.npmjs.org/web3-utils/-/web3-utils-1.7.3.tgz" - integrity sha512-g6nQgvb/bUpVUIxJE+ezVN+rYwYmlFyMvMIRSuqpi1dk6ApDD00YNArrk7sPcZnjvxOJ76813Xs2vIN2rgh4lg== +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@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== dependencies: - bn.js "^4.11.9" - ethereum-bloom-filters "^1.0.6" - ethereumjs-util "^7.1.0" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randombytes "^2.1.0" - utf8 "3.0.0" + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" which@2.0.2, which@^2.0.1: version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" -which@^1.2.9: - version "1.3.1" - resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - wordwrapjs@^4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-4.0.1.tgz#d9790bccfb110a0fc7836b5ebce0937b37a8b98f" integrity sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA== dependencies: reduce-flatten "^2.0.0" @@ -3660,12 +3114,12 @@ wordwrapjs@^4.0.0: workerpool@6.2.0: version "6.2.0" - resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -3674,15 +3128,8 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write@1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/write/-/write-1.0.3.tgz" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== ws@7.4.6: version "7.4.6" @@ -3691,37 +3138,37 @@ ws@7.4.6: y18n@^5.0.5: version "5.0.8" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^1.10.2: version "1.10.2" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yaml@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" - integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== + version "2.3.4" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" + integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== yargs-parser@20.2.4: version "20.2.4" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== yargs-parser@^20.2.2: version "20.2.9" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== yargs-unparser@2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== dependencies: camelcase "^6.0.0" @@ -3744,12 +3191,12 @@ yargs@16.2.0, yargs@^16.2.0: yn@3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== zod@^3.22.3: