├── .gas-snapshot ├── .github └── workflows │ ├── slither.yml │ └── test.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── audit ├── final-report-cantinacode-coinbase-sbr-usernames-review.pdf ├── report-cantinacode-coinbase-0528.pdf └── report-cantinacode-coinbase-sbr-usernames-review-2.pdf ├── broadcast ├── AddDiscountValidator.s.sol │ └── 84532 │ │ ├── run-1718046023.json │ │ ├── run-1718056271.json │ │ ├── run-1718057476.json │ │ ├── run-1719360931.json │ │ ├── run-1719360999.json │ │ ├── run-1719361052.json │ │ ├── run-1719361114.json │ │ ├── run-1719361537.json │ │ ├── run-1719361576.json │ │ ├── run-1719361602.json │ │ ├── run-1719361775.json │ │ └── run-latest.json ├── DeployBaseRegistrar.s.sol │ └── 84532 │ │ ├── run-1718043431.json │ │ ├── run-1718056792.json │ │ └── run-latest.json ├── DeployCB1DiscountValidator.s.sol │ └── 84532 │ │ ├── run-1719355636.json │ │ └── run-latest.json ├── DeployCBIDDiscountValidator.s.sol │ └── 84532 │ │ ├── run-1719356158.json │ │ └── run-latest.json ├── DeployERC1155DiscountValidator.s.sol │ └── 84532 │ │ ├── run-1719356535.json │ │ └── run-latest.json ├── DeployL1Resolver.s.sol │ └── 11155111 │ │ ├── run-1715204775.json │ │ ├── run-1715636813.json │ │ ├── run-1715636990.json │ │ ├── run-1718040808.json │ │ └── run-latest.json ├── DeployL2Resolver.s.sol │ └── 84532 │ │ ├── run-1715204584.json │ │ ├── run-1718043910.json │ │ ├── run-1718045098.json │ │ ├── run-1718056159.json │ │ ├── run-1718057355.json │ │ └── run-latest.json ├── DeployPriceOracle.s.sol │ └── 84532 │ │ ├── run-1718042548.json │ │ ├── run-1718054960.json │ │ └── run-latest.json ├── DeployRegistrarController.s.sol │ └── 84532 │ │ ├── run-1718043666.json │ │ ├── run-1718044967.json │ │ ├── run-1718055964.json │ │ ├── run-1718057196.json │ │ └── run-latest.json ├── DeployRegistry.s.sol │ └── 84532 │ │ ├── run-1718041937.json │ │ └── run-latest.json ├── DeployReverseRegistrar.s.sol │ └── 84532 │ │ ├── run-1715198978.json │ │ ├── run-1718042177.json │ │ ├── run-1718042409.json │ │ ├── run-1718057156.json │ │ └── run-latest.json ├── DeployTestnetDiscountValidator.s.sol │ └── 84532 │ │ ├── run-1718045681.json │ │ └── run-latest.json ├── DeployTestnetRegistry.s.sol │ └── 84532 │ │ ├── run-1715198016.json │ │ └── run-latest.json ├── DeployVerifiedDiscountValidator.s.sol │ └── 84532 │ │ ├── run-1719355845.json │ │ └── run-latest.json ├── EstablishBaseNamespace.s.sol │ └── 84532 │ │ ├── run-1715205763.json │ │ └── run-latest.json ├── EstablishController.s.sol │ └── 84532 │ │ ├── run-1718057674.json │ │ └── run-latest.json ├── EstablishNamespace.s.sol │ └── 84532 │ │ ├── run-1718045289.json │ │ ├── run-1718056335.json │ │ ├── run-1718057089.json │ │ └── run-latest.json ├── EstablishReverseNamespace.s.sol │ └── 84532 │ │ ├── run-1719265337.json │ │ └── run-latest.json ├── MakeNewName.s.sol │ └── 84532 │ │ ├── run-1715206520.json │ │ ├── run-1715208844.json │ │ └── run-latest.json ├── RegisterNewName.s.sol │ └── 84532 │ │ ├── run-1718058868.json │ │ └── run-latest.json ├── SetCBIdTreeRoot.s.sol │ └── 84532 │ │ ├── run-1719442265.json │ │ └── run-latest.json ├── SetL1ResolverSigner.s.sol │ └── 11155111 │ │ ├── run-1718918784.json │ │ └── run-latest.json └── SetL1ResolverUrl.s.sol │ └── 11155111 │ ├── run-1718395347.json │ ├── run-1718750100.json │ └── run-latest.json ├── env.template ├── foundry.toml ├── py ├── BNS.py ├── Price.py ├── README.md ├── compute_premium.py ├── ens_namehash_converter.py └── writer.py ├── script ├── configure │ ├── AddDiscountValidator.s.sol │ ├── EstablishController.s.sol │ ├── EstablishNamespace.s.sol │ ├── EstablishReverseNamespace.s.sol │ ├── RegisterNewName.s.sol │ ├── SetCBIdTreeRoot.s.sol │ ├── SetL1ResolverSigner.s.sol │ └── SetL1ResolverUrl.s.sol ├── deploy │ ├── DeployBaseRegistrar.s.sol │ ├── DeployL1Resolver.s.sol │ ├── DeployL2Resolver.s.sol │ ├── DeployPriceOracle.s.sol │ ├── DeployRegistrarController.s.sol │ ├── DeployRegistry.s.sol │ ├── DeployReverseRegistrar.s.sol │ └── discounts │ │ ├── DeployCB1DiscountValidator.s.sol │ │ ├── DeployCBIDDiscountValidator.s.sol │ │ ├── DeployERC1155DiscountValidator.s.sol │ │ ├── DeployTestnetDiscountValidator.s.sol │ │ └── DeployVerifiedDiscountValidator.s.sol ├── premint │ ├── Premint.s.sol │ ├── generate_batch_renewals.py │ ├── output.csv │ ├── premint1 │ ├── premint2 │ ├── premint3 │ ├── premint4 │ ├── premint5 │ ├── premint6 │ ├── premint7 │ ├── premint8 │ └── premint9 └── resolve │ └── ResolveCallback.s.sol ├── slither.config.json ├── src ├── L1 │ └── L1Resolver.sol ├── L2 │ ├── BaseRegistrar.sol │ ├── EARegistrarController.sol │ ├── ExponentialPremiumPriceOracle.sol │ ├── L2Resolver.sol │ ├── LaunchAuctionPriceOracle.sol │ ├── MigrationController.sol │ ├── RegistrarController.sol │ ├── Registry.sol │ ├── ReverseRegistrar.sol │ ├── ReverseRegistrarV2.sol │ ├── StablePriceOracle.sol │ ├── UpgradeableL2Resolver.sol │ ├── UpgradeableRegistrarController.sol │ ├── discounts │ │ ├── AttestationValidator.sol │ │ ├── CBIdDiscountValidator.sol │ │ ├── CouponDiscountValidator.sol │ │ ├── ERC1155DiscountValidator.sol │ │ ├── ERC1155DiscountValidatorV2.sol │ │ ├── ERC721DiscountValidator.sol │ │ └── TalentProtocolDiscountValidator.sol │ ├── interface │ │ ├── IBaseRegistrar.sol │ │ ├── IDiscountValidator.sol │ │ ├── IL2ReverseRegistrar.sol │ │ ├── IPriceOracle.sol │ │ ├── IRegistrarController.sol │ │ ├── IReverseRegistrar.sol │ │ └── IReverseRegistrarV2.sol │ └── resolver │ │ ├── ABIResolver.sol │ │ ├── AddrResolver.sol │ │ ├── ContentHashResolver.sol │ │ ├── DNSResolver.sol │ │ ├── InterfaceResolver.sol │ │ ├── NameResolver.sol │ │ ├── PubkeyResolver.sol │ │ ├── ResolverBase.sol │ │ └── TextResolver.sol ├── lib │ ├── EDAPrice.sol │ ├── Sha3.sol │ ├── SignatureVerifier.sol │ └── SybilResistanceVerifier.sol └── util │ └── Constants.sol └── test ├── BaseRegistrar ├── AddController.t.sol ├── BaseRegistrarBase.t.sol ├── ContractURI.t.sol ├── IsAvailable.t.sol ├── Name.t.sol ├── OwnerOf.t.sol ├── Reclaim.t.sol ├── Register.t.sol ├── RegisterOnly.t.sol ├── RegisterWithRecord.t.sol ├── RemoveController.t.sol ├── Renew.t.sol ├── SetBaseTokenURI.t.sol ├── SetContractURI.t.sol ├── SetResolver.t.sol ├── SupportsInterface.t.sol ├── Symbol.t.sol └── TokenURI.t.sol ├── EARegistrarController ├── Available.t.sol ├── DiscountedRegister.t.sol ├── DiscountedRegisterPrice.t.sol ├── EARegistrarControllerBase.t.sol ├── RecoverFunds.t.sol ├── RentPrice.t.sol ├── SetDiscountDetails.t.sol ├── SetPaymentReceiver.t.sol ├── SetPriceOracle.t.sol ├── SetReverseRegistrar.t.sol ├── Valid.t.sol └── WithdrawETH.t.sol ├── ExponentialPremiumPriceOracle ├── ExponentialPremiumFuzzTest.t.sol ├── ExponentialPremiumOracleBase.t.sol └── decayedPremium.t.sol ├── Fork ├── AbstractENSIP19DataMigrations.t.sol ├── AbstractENSIP19LegacyFlows.t.sol ├── AbstractENSIP19NewFlows.t.sol ├── AbstractForkSuite.t.sol ├── BaseMainnetConfig.t.sol ├── BaseMainnetConstants.sol ├── BaseSepoliaConfig.t.sol ├── BaseSepoliaConstants.sol ├── ENSIP19DataMigrationsMainnet.t.sol ├── ENSIP19DataMigrationsSepolia.t.sol ├── ENSIP19LegacyFlowsMainnet.t.sol ├── ENSIP19LegacyFlowsSepolia.t.sol ├── ENSIP19NewFlowsMainnet.t.sol └── ENSIP19NewFlowsSepolia.t.sol ├── Integration ├── ForwardResolutionMigration.t.sol ├── IntegrationTestBase.t.sol ├── LaunchAuctionRegistrations.t.sol ├── PostLaunchAuctionConfig.t.sol └── SwitchToUpgradeableRegistrarController.t.sol ├── L1Resolver ├── AdminMethods.t.sol ├── Fallback.t.sol ├── L1ResolverBase.t.sol ├── L1ResolverMainnet.t.sol ├── MakeSignatureHash.t.sol ├── Resolve.t.sol ├── ResolveWithProof.t.sol └── SupportsInterface.t.sol ├── L2Resolver ├── Approve.t.sol ├── IsAuthorised.t.sol ├── L2ResolverBase.t.sol ├── SetApprovalForAll.t.sol ├── SetRegistrarController.t.sol ├── SetReverseRegistrar.t.sol └── SupportsInterface.t.sol ├── LaunchAuctionPriceOracle ├── DecayedPremium.t.sol ├── ExponentialPremiumFuzzTest.t.sol └── LaunchAuctionPriceOracleBase.t.sol ├── MigrationController ├── MigrationControllerBase.t.sol └── SetBaseForwardAddr.t.sol ├── RegistrarController ├── Available.t.sol ├── DiscountedRegister.t.sol ├── DiscountedRegisterPrice.t.sol ├── RecoverFunds.t.sol ├── Register.t.sol ├── RegisterPrice.t.sol ├── RegistrarControllerBase.t.sol ├── Renew.t.sol ├── RentPrice.t.sol ├── SetDiscountDetails.t.sol ├── SetLaunchTime.t.sol ├── SetPaymentReceiver.t.sol ├── SetPriceOracle.t.sol ├── SetReverseRegistrar.t.sol ├── Valid.t.sol └── WithdrawETH.t.sol ├── Registry ├── RecordExists.t.sol ├── RegistryBase.t.sol ├── SetApprovalForAll.t.sol ├── SetOwner.t.sol ├── SetRecord.t.sol ├── SetResolver.t.sol ├── SetSubnodeOwner.t.sol ├── SetSubnodeRecord.t.sol └── SetTTL.t.sol ├── ReverseRegistrar ├── Claim.t.sol ├── ClaimForBaseAddr.t.sol ├── ClaimWithResolver.t.sol ├── Node.t.sol ├── ReverseRegistrarBase.t.sol ├── SetControllerApproval.t.sol ├── SetDefaultResolver.t.sol ├── SetName.t.sol └── SetNameForAddr.t.sol ├── ReverseRegistrarV2 ├── Claim.t.sol ├── ClaimForBaseAddr.t.sol ├── ClaimWithResolver.t.sol ├── Node.t.sol ├── ReverseRegistrarV2Base.t.sol ├── SetBaseForwardAddr.t.sol ├── SetControllerApproval.t.sol ├── SetDefaultResolver.t.sol ├── SetName.t.sol ├── SetNameForAddr.t.sol └── SetNameForAddrWithSignature.t.sol ├── StablePriceOracle ├── Premium.t.sol ├── Price.t.sol ├── StablePriceFuzzTest.t.sol └── StablePriceOracleBase.t.sol ├── UpgradeableL2Resolver ├── Approve.t.sol ├── ClearRecords.t.sol ├── IsAuthorised.t.sol ├── SetABI.t.sol ├── SetAddr.t.sol ├── SetApprovalForAll.t.sol ├── SetContentHash.t.sol ├── SetControllerApproval.t.sol ├── SetDNSRecords.t.sol ├── SetInterface.t.sol ├── SetName.t.sol ├── SetPubkey.t.sol ├── SetReverseRegistrar.t.sol ├── SetText.t.sol ├── SetZonehash.t.sol ├── Storage.t.sol ├── SupportsInterface.t.sol └── UpgradeableL2ResolverBase.t.sol ├── UpgradeableRegistrarController ├── Available.t.sol ├── DiscountedRegister.t.sol ├── DiscountedRegisterPrice.t.sol ├── Register.t.sol ├── RegisterPrice.t.sol ├── Renew.t.sol ├── RentPrice.t.sol ├── SetDiscountDetails.t.sol ├── SetPaymentReceiver.t.sol ├── SetPriceOracle.t.sol ├── SetReverseRegistrar.t.sol ├── UpgradeableRegistrarControllerBase.t.sol ├── Valid.t.sol └── WithdrawETH.t.sol ├── discounts ├── AttestationValidator │ ├── AttestationValidatorBase.t.sol │ ├── IsValidDiscountRegistration.t.sol │ └── SetSigner.t.sol ├── CBIdDiscountValidator │ ├── CBIdDiscountValidatorBase.t.sol │ ├── IsValidDiscountRegistration.t.sol │ └── SetRoot.t.sol ├── CouponDiscountValidator │ ├── CouponDiscountValidatorBase.t.sol │ ├── IsValidDiscountRegistration.t.sol │ └── SetSigner.t.sol ├── ERC1155DiscountValidator │ ├── ERC1155DiscountValidatorBase.t.sol │ └── IsValidDiscountRegistration.t.sol ├── ERC1155DiscountValidatorV2 │ ├── ERC1155DiscountValidatorV2Base.t.sol │ └── IsValidDiscountRegistration.t.sol ├── ERC721DiscountValidator │ ├── ERC721DiscountValidatorBase.t.sol │ └── IsValidDiscountRegistration.t.sol └── TalenProtocolDiscountValidator │ ├── IsValidDiscountRegistration.t.sol │ ├── SetThreshold.t.sol │ └── TalentProtocolDiscountValidatorBase.t.sol └── mocks ├── MockAddrResolver.sol ├── MockAttestationIndexer.sol ├── MockBaseRegistrar.sol ├── MockBuilderScorePassport.sol ├── MockDiscountValidator.sol ├── MockEAS.sol ├── MockERC1155.sol ├── MockERC721.sol ├── MockL2ReverseRegistrar.sol ├── MockNameResolver.sol ├── MockNameWrapper.sol ├── MockOwnedContract.sol ├── MockPriceOracle.sol ├── MockPublicResolver.sol ├── MockRegistrarController.sol ├── MockReverseRegistrar.sol ├── MockReverseRegistrarV2.sol ├── MockUSDC.sol └── TestnetDiscountValidator.sol /.github/workflows/slither.yml: -------------------------------------------------------------------------------- 1 | name: Slither Analysis 2 | # Config File for Public Github Repos 3 | ## Private Github needs External Action support, this will not work in Private Github 4 | on: 5 | # Only run this action on pushes / pull requests to main branch 6 | push: 7 | branches: [ main, slither-action ] 8 | pull_request: 9 | branches: [ main, slither-action ] 10 | jobs: 11 | analyze: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v4 16 | 17 | - name: Run Slither 18 | uses: crytic/slither-action@v0.4.0 19 | id: slither 20 | # continue-on-error: true 21 | with: 22 | sarif: results.sarif 23 | # Don't fail so that SARIF step still works 24 | fail-on: none 25 | # advanced: this can be set by Slither config file by using the next line instead 26 | # fail-on: config 27 | 28 | - name: Upload SARIF file 29 | uses: github/codeql-action/upload-sarif@v3 30 | with: 31 | sarif_file: ${{ steps.slither.outputs.sarif }} 32 | 33 | # Set GH_TOKEN permissions to be as restricted as possible 34 | # security-events must be set to write for the SARIF upload step 35 | permissions: 36 | security-events: write 37 | contents: read 38 | actions: read -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | env: 9 | FOUNDRY_PROFILE: ci 10 | BASE_SEPOLIA_RPC_URL: "https://sepolia.base.org" 11 | BASE_MAINNET_RPC_URL: "https://mainnet.base.org" 12 | 13 | jobs: 14 | forge-test: 15 | name: Run Forge Tests and Checks 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | submodules: recursive 21 | 22 | - name: Set up Python 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: '3.8' 26 | 27 | - name: Install eth-abi 28 | run: | 29 | python3 -m pip install --upgrade pip 30 | python3 -m pip install eth-abi 31 | 32 | - name: Install Foundry 33 | uses: foundry-rs/foundry-toolchain@v1 34 | with: 35 | version: stable 36 | 37 | - name: Run Forge build 38 | run: | 39 | forge --version 40 | id: build 41 | 42 | - name: Run Forge tests 43 | run: | 44 | forge test -vvv --ffi 45 | id: test 46 | 47 | - name: Check formatting 48 | run: | 49 | forge fmt --check 50 | id: fmt 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiler files 2 | cache/ 3 | out/ 4 | 5 | # Ignores development broadcast logs 6 | !/broadcast 7 | /broadcast/*/31337/ 8 | /broadcast/**/dry-run/ 9 | 10 | # Docs 11 | docs/ 12 | 13 | # Dotenv file 14 | .env* 15 | 16 | # Local env 17 | script/Scratch* 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "contracts/lib/forge-std"] 2 | path = contracts/lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "contracts/lib/ens-contracts"] 5 | path = contracts/lib/ens-contracts 6 | url = https://github.com/ensdomains/ens-contracts 7 | [submodule "contracts/lib/openzeppelin-contracts"] 8 | path = contracts/lib/openzeppelin-contracts 9 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 10 | [submodule "contracts/lib/solady"] 11 | path = contracts/lib/solady 12 | url = https://github.com/vectorized/solady 13 | [submodule "contracts/lib/buffer"] 14 | path = contracts/lib/buffer 15 | url = https://github.com/ensdomains/buffer 16 | [submodule "lib/solady"] 17 | path = lib/solady 18 | url = https://github.com/vectorized/solady 19 | [submodule "lib/openzeppelin-contracts"] 20 | path = lib/openzeppelin-contracts 21 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 22 | [submodule "lib/buffer"] 23 | path = lib/buffer 24 | url = https://github.com/ensdomains/buffer 25 | [submodule "lib/ens-contracts"] 26 | path = lib/ens-contracts 27 | url = https://github.com/ensdomains/ens-contracts 28 | [submodule "lib/forge-std"] 29 | path = lib/forge-std 30 | url = https://github.com/foundry-rs/forge-std 31 | [submodule "lib/verifications"] 32 | path = lib/verifications 33 | url = https://github.com/coinbase/verifications 34 | [submodule "lib/eas-contracts"] 35 | path = lib/eas-contracts 36 | url = https://github.com/ethereum-attestation-service/eas-contracts 37 | [submodule "lib/openzeppelin-contracts-upgradeable"] 38 | path = lib/openzeppelin-contracts-upgradeable 39 | url = https://github.com/openzeppelin/openzeppelin-contracts-upgradeable 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Base 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /audit/final-report-cantinacode-coinbase-sbr-usernames-review.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/base/basenames/996ec912e48ef6cfc41b403f58cc3d000f66ece3/audit/final-report-cantinacode-coinbase-sbr-usernames-review.pdf -------------------------------------------------------------------------------- /audit/report-cantinacode-coinbase-0528.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/base/basenames/996ec912e48ef6cfc41b403f58cc3d000f66ece3/audit/report-cantinacode-coinbase-0528.pdf -------------------------------------------------------------------------------- /audit/report-cantinacode-coinbase-sbr-usernames-review-2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/base/basenames/996ec912e48ef6cfc41b403f58cc3d000f66ece3/audit/report-cantinacode-coinbase-sbr-usernames-review-2.pdf -------------------------------------------------------------------------------- /env.template: -------------------------------------------------------------------------------- 1 | PRIVATE_KEY= 2 | SEPOLIA_RPC_URL= 3 | BASE_SEPOLIA_RPC_URL= 4 | ETHERSCAN_API_KEY= 5 | BASE_ETHERSCAN_API_KEY= 6 | 7 | L1_RESOLVER_ADDR= 8 | L2_RESOLVER_ADDR= 9 | REGISTRY_ADDR= 10 | BASE_REGISTRAR_ADDR= 11 | REGISTRAR_CONTROLLER_ADDR= 12 | PRICE_ORACLE_ADDR= 13 | REVERSE_REGISTRAR_ADDR= 14 | DISCOUNT_VALIDATOR= 15 | -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = "src" 3 | out = "out" 4 | libs = ["lib"] 5 | solc_version = "0.8.26" 6 | remappings = [ 7 | "@ensdomains/buffer/=lib/buffer", 8 | "solady/=lib/solady/src/", 9 | "forge-std/=lib/forge-std/src/", 10 | "ens-contracts/=lib/ens-contracts/contracts/", 11 | "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", 12 | "openzeppelin-contracts/=lib/openzeppelin-contracts", 13 | "eas-contracts/=lib/eas-contracts/contracts/", 14 | "verifications/=lib/verifications/src", 15 | "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/" 16 | ] 17 | fs_permissions = [{access = "read", path = "./script/premint/"}] 18 | auto_detect_remappings = false 19 | 20 | [profile.production] 21 | src = "src" 22 | out = "out" 23 | libs = ["lib"] 24 | solc_version = "0.8.26" 25 | via_ir = true 26 | optimizer = true 27 | optimizer_runs = 10000 28 | evm_version = "cancun" 29 | bytecode_hash = "ipfs" 30 | offline = true 31 | remappings = [ 32 | "@ensdomains/buffer/=lib/buffer", 33 | "solady/=lib/solady/src/", 34 | "forge-std/=lib/forge-std/src/", 35 | "ens-contracts/=lib/ens-contracts/contracts/", 36 | "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", 37 | "openzeppelin-contracts/=lib/openzeppelin-contracts", 38 | "eas-contracts/=lib/eas-contracts/contracts/", 39 | "verifications/=lib/verifications/src", 40 | "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/" 41 | ] 42 | fs_permissions = [{access = "read", path = "./script/premint/"}] 43 | auto_detect_remappings = false 44 | 45 | [rpc_endpoints] 46 | sepolia="${SEPOLIA_RPC_URL}" 47 | base-sepolia="${BASE_SEPOLIA_RPC_URL}" 48 | base-mainnet="${BASE_MAINNET_RPC_URL}" 49 | 50 | [etherscan] 51 | sepolia={url = "https://api-sepolia.etherscan.io/api", key = "${ETHERSCAN_API_KEY}"} 52 | base-sepolia={url = "https://api-sepolia.basescan.org/api", key = "${BASE_ETHERSCAN_API_KEY}"} 53 | -------------------------------------------------------------------------------- /py/BNS.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def main(): 5 | 6 | owners = [] 7 | with open("cache/Update-BNS-Users.json", "r") as f: 8 | data = json.load(f) 9 | for r in data: 10 | if(len(r["owners"]) > 1): 11 | print("multiple owners for %s" % r["name"]) 12 | elif(len(r["owners"]) == 0): 13 | print("no owners for %s" % r["name"]) 14 | else: 15 | owners.append(r["owners"][0]["owner_address"]) 16 | 17 | 18 | seen = set() 19 | owner_count = dict() 20 | unique_owners = [] 21 | for owner in owners: 22 | if owner not in seen: 23 | unique_owners.append(owner) 24 | seen.add(owner) 25 | owner_count[owner] = 1 26 | else: 27 | owner_count[owner] += 1 28 | 29 | 30 | with open("cache/bns.csv", "a") as f: 31 | for owner in unique_owners: 32 | f.write(owner + "\n") 33 | 34 | 35 | print("Total owned tokens: %s" % len(owners)) 36 | print("Total unique owners: %s" % len(unique_owners)) 37 | c = 0 38 | for w in sorted(owner_count, key=owner_count.get): 39 | if owner_count[w] > 9: 40 | c+=1 41 | print(w, owner_count[w]) 42 | print("Multiple name holders: %s" % str(c)) 43 | 44 | if __name__ == "__main__": 45 | main() -------------------------------------------------------------------------------- /py/Price.py: -------------------------------------------------------------------------------- 1 | import math 2 | import matplotlib.pyplot as plt 3 | 4 | PRECISION = 10 ** 18 5 | SECONDS_PER_DAY = 86400 6 | 7 | def decayed_premium(start_premium, elapsed_seconds, seconds_in_period, per_period_decay_percent_wad): 8 | ratio = elapsed_seconds / seconds_in_period 9 | 10 | percent_wad_remaining_per_period = (PRECISION - per_period_decay_percent_wad) / PRECISION 11 | multiplier = (percent_wad_remaining_per_period ** ratio) 12 | 13 | price = (start_premium * multiplier) 14 | return price 15 | 16 | def calculate_prices(start_premium, end_value, num_days, seconds_in_period, per_period_decay_percent_wad): 17 | elapsed_times = [] 18 | prices = [] 19 | 20 | for day in range(num_days): 21 | for i in range(10): 22 | elapsed_time = (day + i / 10) * SECONDS_PER_DAY 23 | 24 | premium = decayed_premium(start_premium, elapsed_time, seconds_in_period, per_period_decay_percent_wad) 25 | 26 | price = max(premium, end_value) / PRECISION 27 | elapsed_times.append(day + i / 10) 28 | prices.append(price) 29 | 30 | return elapsed_times, prices 31 | 32 | start = int(input("Input a value for the start premium: ")) 33 | total = int(input("How many days would it take for the price to reach its end value: ")) 34 | num = int(input("Input a value for the number of days over which the price should decay: ")) 35 | 36 | start_premium = start * (10 ** 18) 37 | total_days = total 38 | seconds_in_period = SECONDS_PER_DAY 39 | per_period_decay_percent = 50 40 | 41 | per_period_decay_percent_wad = int(per_period_decay_percent * PRECISION / 100) 42 | end_value = start_premium >> total_days 43 | 44 | num_days = num 45 | 46 | elapsed_times, prices = calculate_prices(start_premium, end_value, num_days, seconds_in_period, per_period_decay_percent_wad) 47 | plt.figure(figsize=(start, num)) 48 | plt.plot(elapsed_times, prices, marker='o') 49 | plt.xlabel('Elapsed Time (days)') 50 | plt.ylabel('Premium Price') 51 | plt.title('Pricing Chart') 52 | plt.grid(True) 53 | plt.xticks(range(num_days + 1)) 54 | plt.show() -------------------------------------------------------------------------------- /py/README.md: -------------------------------------------------------------------------------- 1 | ### Premium Pricing Chart 2 | 3 | To visualize the pricing decay for premium prices, you can run the Python script located in this directory. The script generates a pricing chart based on user input. 4 | 5 | ### Prerequisites 6 | 7 | * Python 3.x 8 | * matplotlib library 9 | 10 | ### Running the Script 11 | 12 | 1. Navigate to the `py` directory 13 | 14 | ```shell 15 | $ cd py 16 | ``` 17 | 18 | 2. Run the Python script 19 | 20 | ```shell 21 | $ python3 Price.py 22 | ``` 23 | 24 | 3. Follow the prompts to input the required values: 25 | 26 | * Start Premium: The initial premium price 27 | * Total days: The number of days it takes for the price to reach its end value 28 | * Number of days: The number of days over which price should decay 29 | 30 | The script will generate and display the pricing chart based on the provided inputs. 31 | -------------------------------------------------------------------------------- /py/compute_premium.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from eth_abi import encode 3 | 4 | PRECISION = 10 ** 18 5 | SECONDS_PER_DAY = 86400 6 | PER_PERIOD_DECAY_PERCENT = 50 7 | PER_PERIOD_DECAY_PERCENT_WAD = int(PER_PERIOD_DECAY_PERCENT * PRECISION / 100) 8 | 9 | class DecayedPriceCalculator: 10 | @staticmethod 11 | def decayed_premium(start_premium, elapsed_seconds): 12 | ratio = elapsed_seconds / SECONDS_PER_DAY 13 | percent_wad_remaining_per_period = (PRECISION - PER_PERIOD_DECAY_PERCENT_WAD) / PRECISION 14 | multiplier = (percent_wad_remaining_per_period ** ratio) 15 | price = (start_premium * multiplier) 16 | return int(price) 17 | 18 | @classmethod 19 | def calculate_from_cli(cls): 20 | if len(sys.argv) != 3: 21 | print("Usage: python3 price.py ") 22 | sys.exit(1) 23 | 24 | start_premium = int(sys.argv[1]) 25 | elapsed_seconds = int(sys.argv[2]) 26 | 27 | result = cls.decayed_premium(start_premium, elapsed_seconds) 28 | enc = encode(['uint256'], [result]) 29 | print("0x" + enc.hex()) 30 | 31 | if __name__ == "__main__": 32 | DecayedPriceCalculator.calculate_from_cli() -------------------------------------------------------------------------------- /py/writer.py: -------------------------------------------------------------------------------- 1 | #!/bin/python3 2 | 3 | import sys 4 | 5 | print("In writer.py") 6 | with open("script/premint/output.csv", 'a') as f: 7 | f.write(sys.argv[1]) -------------------------------------------------------------------------------- /script/configure/AddDiscountValidator.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import {RegistrarController} from "src/L2/RegistrarController.sol"; 6 | 7 | contract AddDiscountValidator is Script { 8 | function run() external { 9 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 10 | vm.startBroadcast(deployerPrivateKey); 11 | 12 | //////////////////////////////////////////////// 13 | bytes32 key = keccak256("erc1155.discount.validator"); 14 | RegistrarController.DiscountDetails memory details = RegistrarController.DiscountDetails({ 15 | active: true, 16 | discountValidator: vm.envAddress("ERC1155_DISCOUNT_VALIDATOR"), 17 | key: key, 18 | discount: 0.001 ether 19 | }); 20 | //////////////////////////////////////////////// 21 | 22 | address controllerAddr = vm.envAddress("REGISTRAR_CONTROLLER_ADDR"); 23 | RegistrarController controller = RegistrarController(controllerAddr); 24 | 25 | controller.setDiscountDetails(details); 26 | 27 | vm.stopBroadcast(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /script/configure/EstablishController.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import {BaseRegistrar} from "src/L2/BaseRegistrar.sol"; 6 | import {ReverseRegistrar} from "src/L2/ReverseRegistrar.sol"; 7 | 8 | contract EstablishController is Script { 9 | function run() external { 10 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 11 | vm.startBroadcast(deployerPrivateKey); 12 | 13 | address base = vm.envAddress("BASE_REGISTRAR_ADDR"); 14 | address controller = vm.envAddress("REGISTRAR_CONTROLLER_ADDR"); 15 | BaseRegistrar(base).addController(controller); 16 | 17 | address reverse = vm.envAddress("REVERSE_REGISTRAR_ADDR"); 18 | ReverseRegistrar(reverse).setControllerApproval(controller, true); 19 | vm.stopBroadcast(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /script/configure/EstablishNamespace.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import {Registry} from "src/L2/Registry.sol"; 6 | import "src/util/Constants.sol"; 7 | 8 | contract EstablishNamespace is Script { 9 | function run() external { 10 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 11 | address deployerAddress = vm.addr(deployerPrivateKey); 12 | vm.startBroadcast(deployerPrivateKey); 13 | 14 | address ensAddress = vm.envAddress("REGISTRY_ADDR"); // deployer-owned registry 15 | Registry registry = Registry(ensAddress); 16 | address baseRegistrar = vm.envAddress("BASE_REGISTRAR_ADDR"); 17 | 18 | // establish the base.eth namespace 19 | bytes32 ethLabel = keccak256("eth"); 20 | bytes32 baseLabel = keccak256("basetest"); // basetest.eth is our sepolia test domain 21 | registry.setSubnodeOwner(0x0, ethLabel, deployerAddress); 22 | registry.setSubnodeOwner(ETH_NODE, baseLabel, baseRegistrar); // base registrar must own 2LD 23 | 24 | vm.stopBroadcast(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /script/configure/EstablishReverseNamespace.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import {Registry} from "src/L2/Registry.sol"; 6 | import "src/util/Constants.sol"; 7 | 8 | contract EstablishReverseNamespace is Script { 9 | function run() external { 10 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 11 | address deployerAddress = vm.addr(deployerPrivateKey); 12 | vm.startBroadcast(deployerPrivateKey); 13 | 14 | address ensAddress = vm.envAddress("REGISTRY_ADDR"); // deployer-owned registry 15 | Registry registry = Registry(ensAddress); 16 | address reverse = vm.envAddress("REVERSE_REGISTRAR_ADDR"); // Reverse registrar 17 | 18 | // establish the base.eth namespace 19 | bytes32 reverseLabel = keccak256("reverse"); 20 | bytes32 addrLabel = keccak256("addr"); // basetest.eth is our sepolia test domain 21 | registry.setSubnodeOwner(0x0, reverseLabel, deployerAddress); 22 | registry.setSubnodeOwner(REVERSE_NODE, addrLabel, address(reverse)); // reverse registrar must own addr.reverse 23 | 24 | vm.stopBroadcast(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /script/configure/SetCBIdTreeRoot.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import "src/L2/discounts/CBIdDiscountValidator.sol"; 6 | 7 | contract SetCBIdTreeRoot is Script { 8 | bytes32 root = 0x8cbc28e840d1cd2accddec6592a4bffce2ca38c6c559c252504f342dd46acbb6; 9 | 10 | function run() public { 11 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 12 | vm.startBroadcast(deployerPrivateKey); 13 | address cbid = vm.envAddress("CBID_DISCOUNT_VALIDATOR"); 14 | 15 | CBIdDiscountValidator(cbid).setRoot(root); 16 | 17 | vm.stopBroadcast(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /script/configure/SetL1ResolverSigner.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import "src/L1/L1Resolver.sol"; 6 | 7 | contract SetL1ResolverSigner is Script { 8 | function run() external { 9 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 10 | address resolverAddress = vm.envAddress("L1_RESOLVER_ADDR"); 11 | vm.startBroadcast(deployerPrivateKey); 12 | 13 | address NEW_SIGNER = 0x0ae910AFA602F5460c4A6eDEc98A4F429901fAE2; 14 | address[] memory signers = new address[](1); 15 | signers[0] = NEW_SIGNER; 16 | 17 | L1Resolver resolver = L1Resolver(resolverAddress); 18 | console.log("connected to L1 resolver"); 19 | resolver.addSigners(signers); 20 | 21 | vm.stopBroadcast(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /script/configure/SetL1ResolverUrl.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import "src/L1/L1Resolver.sol"; 6 | 7 | contract SetL1ResolverUrl is Script { 8 | function run() external { 9 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 10 | address resolverAddress = vm.envAddress("L1_RESOLVER_ADDR"); 11 | vm.startBroadcast(deployerPrivateKey); 12 | 13 | string memory NEW_URL = "https://api.coinbase.com/api/v1/domain/resolver/resolveDomain/{sender}/{data}"; 14 | 15 | L1Resolver resolver = L1Resolver(resolverAddress); 16 | resolver.setUrl(NEW_URL); 17 | 18 | vm.stopBroadcast(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /script/deploy/DeployBaseRegistrar.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import {ENS} from "ens-contracts/registry/ENS.sol"; 6 | 7 | import {BASE_ETH_NODE} from "src/util/Constants.sol"; 8 | import {BaseRegistrar} from "src/L2/BaseRegistrar.sol"; 9 | import {NameEncoder} from "ens-contracts/utils/NameEncoder.sol"; 10 | 11 | contract DeployBaseRegistrar is Script { 12 | function run() external { 13 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 14 | address deployerAddress = vm.addr(deployerPrivateKey); 15 | vm.startBroadcast(deployerPrivateKey); 16 | 17 | /// L2 Resolver constructor data 18 | address ensAddress = vm.envAddress("REGISTRY_ADDR"); // deployer-owned registry 19 | (, bytes32 node) = NameEncoder.dnsEncodeName("basetest.eth"); 20 | 21 | BaseRegistrar base = new BaseRegistrar(ENS(ensAddress), deployerAddress, node, "", ""); 22 | 23 | console.log("Base Registrar deployed to:"); 24 | console.log(address(base)); 25 | 26 | vm.stopBroadcast(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /script/deploy/DeployL1Resolver.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import "src/L1/L1Resolver.sol"; 6 | 7 | contract DeployL1Resolver is Script { 8 | function run() external { 9 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 10 | address deployerAddresss = vm.addr(deployerPrivateKey); 11 | vm.startBroadcast(deployerPrivateKey); 12 | 13 | /// L1 Resolver constructor data 14 | string memory url = 15 | "https://api-entry-gateway-development.cbhq.net/api/v1/subdomain/resolver/resolveDomain/{sender}/{data}"; // 16 | address[] memory signers = new address[](1); 17 | signers[0] = 0xa412c16ECd2198A6aBce8235651E105684Fb77ed; // DEV signer 18 | address owner = deployerAddresss; 19 | address rootResolver = 0x8FADE66B79cC9f707aB26799354482EB93a5B7dD; //basetest.eth root resolver on sepolia 20 | 21 | L1Resolver l1 = new L1Resolver(url, signers, owner, rootResolver); 22 | console.log(address(l1)); 23 | 24 | vm.stopBroadcast(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /script/deploy/DeployL2Resolver.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | import {NameEncoder} from "ens-contracts/utils/NameEncoder.sol"; 7 | 8 | import "src/L2/L2Resolver.sol"; 9 | import {Registry} from "src/L2/Registry.sol"; 10 | import "src/util/Constants.sol"; 11 | 12 | contract DeployL2Resolver is Script { 13 | function run() external { 14 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 15 | address deployerAddress = vm.addr(deployerPrivateKey); 16 | vm.startBroadcast(deployerPrivateKey); 17 | 18 | /// L2 Resolver constructor data 19 | address ensAddress = vm.envAddress("REGISTRY_ADDR"); 20 | address controller = vm.envAddress("REGISTRAR_CONTROLLER_ADDR"); // controller can set data on deployment 21 | address reverse = vm.envAddress("REVERSE_REGISTRAR_ADDR"); 22 | 23 | L2Resolver l2 = new L2Resolver(Registry(ensAddress), controller, reverse, deployerAddress); 24 | 25 | console.log(address(l2)); 26 | 27 | vm.stopBroadcast(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /script/deploy/DeployPriceOracle.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | import {StablePriceOracle} from "src/L2/StablePriceOracle.sol"; 7 | import {ExponentialPremiumPriceOracle} from "src/L2/ExponentialPremiumPriceOracle.sol"; 8 | 9 | contract DeployPriceOracle is Script { 10 | function run() external { 11 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 12 | vm.startBroadcast(deployerPrivateKey); 13 | 14 | uint256[] memory prices = new uint256[](6); 15 | prices[0] = 316_808_781_402; 16 | prices[1] = 31_680_878_140; 17 | prices[2] = 3_168_087_814; 18 | prices[3] = 316_808_781; 19 | prices[4] = 31_680_878; 20 | prices[5] = 3_168_087; // 3,168,808.781402895 = 1e14 / (365.25 * 24 * 3600) 21 | uint256 premiumStart = 500 ether; 22 | uint256 totalDays = 28 days; 23 | 24 | StablePriceOracle oracle = new ExponentialPremiumPriceOracle(prices, premiumStart, totalDays); 25 | console.log("Price Oracle deployed to:"); 26 | console.log(address(oracle)); 27 | 28 | vm.stopBroadcast(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /script/deploy/DeployRegistrarController.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 7 | import {BaseRegistrar} from "src/L2/BaseRegistrar.sol"; 8 | import {Registry} from "src/L2/Registry.sol"; 9 | import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; 10 | import {IReverseRegistrar} from "src/L2/interface/IReverseRegistrar.sol"; 11 | import {RegistrarController} from "src/L2/RegistrarController.sol"; 12 | import {NameEncoder} from "ens-contracts/utils/NameEncoder.sol"; 13 | 14 | import "src/util/Constants.sol"; 15 | 16 | contract DeployRegistrarController is Script { 17 | function run() external { 18 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 19 | address deployerAddress = vm.addr(deployerPrivateKey); 20 | vm.startBroadcast(deployerPrivateKey); 21 | 22 | /// L2 Resolver constructor data 23 | address oracle = vm.envAddress("PRICE_ORACLE_ADDR"); 24 | address reverse = vm.envAddress("REVERSE_REGISTRAR_ADDR"); // deployer-owned rev registrar 25 | address base = vm.envAddress("BASE_REGISTRAR_ADDR"); 26 | (, bytes32 rootNode) = NameEncoder.dnsEncodeName("basetest.eth"); 27 | string memory rootName = ".basetest.eth"; 28 | 29 | RegistrarController controller = new RegistrarController( 30 | BaseRegistrar(base), 31 | IPriceOracle(oracle), 32 | IReverseRegistrar(reverse), 33 | deployerAddress, 34 | rootNode, 35 | rootName, 36 | deployerAddress 37 | ); 38 | 39 | console.log("RegistrarController deployed to:"); 40 | console.log(address(controller)); 41 | 42 | vm.stopBroadcast(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /script/deploy/DeployRegistry.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import {Registry} from "src/L2/Registry.sol"; 6 | 7 | contract DeployRegistry is Script { 8 | function run() external { 9 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 10 | address deployerAddress = vm.addr(deployerPrivateKey); 11 | vm.startBroadcast(deployerPrivateKey); 12 | 13 | Registry registry = new Registry(deployerAddress); 14 | console.log(address(registry)); 15 | 16 | vm.stopBroadcast(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /script/deploy/DeployReverseRegistrar.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | 6 | import {Registry} from "src/L2/Registry.sol"; 7 | import {ReverseRegistrar} from "src/L2/ReverseRegistrar.sol"; 8 | import "src/util/Constants.sol"; 9 | 10 | contract DeployReverseRegistrar is Script { 11 | function run() external { 12 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 13 | address deployerAddress = vm.addr(deployerPrivateKey); 14 | vm.startBroadcast(deployerPrivateKey); 15 | 16 | address ensAddress = vm.envAddress("REGISTRY_ADDR"); // deployer-owned registry 17 | Registry registry = Registry(ensAddress); 18 | 19 | ReverseRegistrar revRegstrar = new ReverseRegistrar( 20 | Registry(ensAddress), 21 | deployerAddress, // deployer as owner 22 | BASE_REVERSE_NODE 23 | ); 24 | 25 | // establish the reverse registrar as the owner of the 'addr.reverse' node 26 | bytes32 reverseLabel = keccak256("reverse"); 27 | bytes32 baseReverseLabel = keccak256("80002105"); 28 | registry.setSubnodeOwner(0x0, reverseLabel, deployerAddress); 29 | registry.setSubnodeOwner(REVERSE_NODE, baseReverseLabel, address(revRegstrar)); 30 | 31 | console.log(address(revRegstrar)); 32 | 33 | vm.stopBroadcast(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /script/deploy/discounts/DeployCB1DiscountValidator.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import {AttestationValidator} from "src/L2/discounts/AttestationValidator.sol"; 6 | 7 | contract DeployCB1DiscountValidator is Script { 8 | function run() external { 9 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 10 | address deployerAddr = vm.addr(deployerPrivateKey); 11 | address TRUSTED_SIGNER_ADDRESS = 0xB6944B3074F40959E1166fe010a3F86B02cF2b7c; 12 | bytes32 CB1_SCHEMA = 0xef8a28852c57170eafe8745aff8b47e22d36b8fb05476cc9ade66637974a1e8c; 13 | address INDEXER = 0xd147a19c3B085Fb9B0c15D2EAAFC6CB086ea849B; 14 | vm.startBroadcast(deployerPrivateKey); 15 | 16 | AttestationValidator validator = 17 | new AttestationValidator(deployerAddr, TRUSTED_SIGNER_ADDRESS, CB1_SCHEMA, INDEXER); 18 | console.log("Discount Validator deployed to:"); 19 | console.log(address(validator)); 20 | 21 | vm.stopBroadcast(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /script/deploy/discounts/DeployERC1155DiscountValidator.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import {ERC1155DiscountValidator} from "src/L2/discounts/ERC1155DiscountValidator.sol"; 6 | import {MockERC1155} from "test/mocks/MockERC1155.sol"; 7 | 8 | contract DeployERC1155DiscountValidator is Script { 9 | function run() external { 10 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 11 | address deployerAddr = vm.addr(deployerPrivateKey); 12 | vm.startBroadcast(deployerPrivateKey); 13 | 14 | MockERC1155 token = new MockERC1155(); 15 | token.mint(deployerAddr, 1, 1); 16 | console.log("Mock ERC1155 token address:"); 17 | console.log(address(token)); 18 | 19 | ERC1155DiscountValidator validator = new ERC1155DiscountValidator(address(token), 1); 20 | 21 | console.log("Discount Validator deployed to:"); 22 | console.log(address(validator)); 23 | 24 | vm.stopBroadcast(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /script/deploy/discounts/DeployTestnetDiscountValidator.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import {TestnetDiscountValidator} from "test/mocks/TestnetDiscountValidator.sol"; 6 | 7 | contract DeployTestnetDiscountValidator is Script { 8 | function run() external { 9 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 10 | vm.startBroadcast(deployerPrivateKey); 11 | 12 | TestnetDiscountValidator validator = new TestnetDiscountValidator(); 13 | console.log("Discount Validator deployed to:"); 14 | console.log(address(validator)); 15 | 16 | vm.stopBroadcast(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /script/deploy/discounts/DeployVerifiedDiscountValidator.s.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Script.sol"; 5 | import {AttestationValidator} from "src/L2/discounts/AttestationValidator.sol"; 6 | 7 | contract DeployVerifiedDiscountValidator is Script { 8 | function run() external { 9 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 10 | address deployerAddr = vm.addr(deployerPrivateKey); 11 | address TRUSTED_SIGNER_ADDRESS = 0xB6944B3074F40959E1166fe010a3F86B02cF2b7c; 12 | bytes32 VERIFIED_ACCOUNT_SCHEMA = 0x2f34a2ffe5f87b2f45fbc7c784896b768d77261e2f24f77341ae43751c765a69; 13 | address INDEXER = 0xd147a19c3B085Fb9B0c15D2EAAFC6CB086ea849B; 14 | vm.startBroadcast(deployerPrivateKey); 15 | 16 | AttestationValidator validator = 17 | new AttestationValidator(deployerAddr, TRUSTED_SIGNER_ADDRESS, VERIFIED_ACCOUNT_SCHEMA, INDEXER); 18 | console.log("Discount Validator deployed to:"); 19 | console.log(address(validator)); 20 | 21 | vm.stopBroadcast(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /script/premint/premint3: -------------------------------------------------------------------------------- 1 | stripe 2 | haun 3 | paradigm 4 | a16z 5 | google 6 | apple 7 | microsoft 8 | amazon 9 | meta 10 | ibm 11 | intel 12 | cisco 13 | oracle 14 | adobe 15 | jpmorganchase 16 | bankofamerica 17 | bofa 18 | wellsfargo 19 | citibank 20 | goldmansachs 21 | morganstanley 22 | amex 23 | americanexpress 24 | mastercard 25 | visa 26 | paypal 27 | walmart 28 | alibaba 29 | ebay 30 | shopify 31 | netflix 32 | disney 33 | adidas 34 | nike 35 | spotify 36 | hulu 37 | espn 38 | att 39 | tesla 40 | cocacola 41 | coca-cola 42 | linkedin 43 | instagram 44 | snapchat 45 | tiktok 46 | pinterest 47 | reddit 48 | whatsapp 49 | wechat 50 | telegram 51 | slack 52 | louisvuitton 53 | chanel 54 | hermes 55 | gucci 56 | rolex 57 | cartier 58 | dior 59 | prada 60 | burberry 61 | ysl 62 | yvessaintlaurent 63 | saintlaurent 64 | tiffanyco 65 | versace 66 | givenchy 67 | balenciaga 68 | bulgari 69 | bvlgari 70 | fendi 71 | ferragamo 72 | valentino 73 | dolcegabbana 74 | armani 75 | tomford 76 | celine 77 | bottegaveneta 78 | moncler 79 | ralphlauren 80 | alexandermcqueen 81 | jimmychoo 82 | chopard 83 | jaeger-lecoultre 84 | tagheuer 85 | vancleefarpels 86 | harrywinston 87 | montblanc 88 | rimowa 89 | balmain 90 | diesel 91 | ferrari 92 | soneva -------------------------------------------------------------------------------- /script/premint/premint4: -------------------------------------------------------------------------------- 1 | magiceden 2 | moonpay 3 | curve 4 | 1inch 5 | ledger 6 | balancer 7 | chainlink 8 | metamask 9 | centrifuge 10 | gauntlet 11 | chainalysis 12 | messari 13 | aerodrome 14 | synthetix 15 | degen 16 | guild 17 | makerdao 18 | redbull 19 | vitalik 20 | vbuterin 21 | balajis 22 | cdixon 23 | pmarca 24 | aantonop 25 | sriramk 26 | alive 27 | elonmusk 28 | parishilton -------------------------------------------------------------------------------- /script/premint/premint6: -------------------------------------------------------------------------------- 1 | burn 2 | prime 3 | institutional 4 | consumer 5 | protocol 6 | scaling 7 | trade 8 | onchain -------------------------------------------------------------------------------- /script/premint/premint7: -------------------------------------------------------------------------------- 1 | elon 2 | jeffbezos 3 | bezos 4 | billgates 5 | markzuckerberg 6 | warrenbuffet 7 | timcook 8 | sundarpichai 9 | satyanadella 10 | larrypage 11 | sergeybrin 12 | oprahwinfrey 13 | oprah 14 | beyonce 15 | jayz 16 | jay-z 17 | taylorswift 18 | rihanna 19 | kanyewest 20 | kimkardashian 21 | lebronjames 22 | joebiden 23 | biden 24 | kamalaharris 25 | kamala 26 | donaldtrump 27 | obama 28 | barackobama 29 | michelleobama 30 | vladimirputin 31 | putin 32 | xijinping 33 | hillaryclinton 34 | billclinton 35 | angelamerkel 36 | emmanuelmacron 37 | borisjohnson 38 | narendramodi 39 | justintrudeau 40 | jairbolsonaro 41 | jacindaardern 42 | scottmorrison 43 | shinzoabe 44 | theresamay 45 | benjaminnetanyahu 46 | netanyahu 47 | volodymyrzelensky 48 | amazon 49 | apple 50 | microsoft 51 | google 52 | facebook 53 | meta 54 | tesla 55 | berkshirehathaway 56 | jnj 57 | jpmorganchase 58 | visa 59 | walmart 60 | alibaba 61 | tencent 62 | samsung 63 | nike 64 | pepsico 65 | intel 66 | att 67 | verizon 68 | mcdonalds 69 | proctergamble 70 | pfizer 71 | exxonmobil 72 | chevron 73 | shell 74 | ford 75 | generalmotors 76 | netflix 77 | starbucks 78 | adobe 79 | oracle 80 | salesforce 81 | paypal 82 | uber 83 | lyft 84 | airbnb 85 | zoom 86 | wagmi 87 | ngmi 88 | nvidia 89 | schwab 90 | grayscale 91 | robinhood 92 | revolut 93 | square 94 | block 95 | bakkt 96 | gemini 97 | kraken 98 | bitfinex 99 | etoro 100 | blockfi 101 | galaxydigital 102 | vaneck 103 | arkinvest 104 | proshares 105 | atari 106 | cnn -------------------------------------------------------------------------------- /script/premint/premint9: -------------------------------------------------------------------------------- 1 | pay 2 | paymaster 3 | node 4 | faucet 5 | swap 6 | onramp 7 | commerce 8 | gasless 9 | transact 10 | ock 11 | basepay 12 | basecamp 13 | basenames 14 | basename 15 | spend 16 | buy 17 | send 18 | receive 19 | trade 20 | swap 21 | deposit 22 | withdraw 23 | claim 24 | lend 25 | borrow 26 | invest 27 | earn 28 | save 29 | mint 30 | burn 31 | sell 32 | transfer 33 | fund 34 | balance 35 | convert 36 | redeem 37 | borrow 38 | repay 39 | allocate 40 | reclaim 41 | yield 42 | gather 43 | accumulate 44 | lock 45 | unlock 46 | pledge 47 | authorize 48 | secure 49 | hedge 50 | optimize 51 | allocate 52 | hash 53 | sign 54 | verify 55 | validate 56 | commit 57 | authorize 58 | mine 59 | delegate 60 | govern 61 | vote 62 | bridge 63 | wrap 64 | unwrap 65 | sync 66 | broadcast 67 | encrypt 68 | decrypt 69 | issue 70 | airdrop 71 | forge 72 | drop 73 | stake 74 | unstake 75 | liquidate 76 | pool 77 | farm 78 | yield 79 | bond 80 | unbond 81 | compound 82 | rebase 83 | migrate 84 | anchor 85 | validate 86 | signal 87 | index 88 | flash 89 | drain 90 | oracle 91 | aggregate -------------------------------------------------------------------------------- /slither.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "exclude_informational": false, 3 | "exclude_low": false, 4 | "exclude_medium": false, 5 | "exclude_high": false, 6 | "filter_paths": "(lib/|test/)", 7 | "skip_assembly": true 8 | } -------------------------------------------------------------------------------- /src/L2/ExponentialPremiumPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ~0.8.17; 3 | 4 | import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol"; 5 | 6 | import {EDAPrice} from "src/lib/EDAPrice.sol"; 7 | import {StablePriceOracle} from "src/L2/StablePriceOracle.sol"; 8 | 9 | contract ExponentialPremiumPriceOracle is StablePriceOracle { 10 | uint256 public immutable startPremium; 11 | uint256 public immutable endValue; 12 | 13 | constructor(uint256[] memory rentPrices, uint256 startPremium_, uint256 totalDays) StablePriceOracle(rentPrices) { 14 | startPremium = startPremium_; 15 | endValue = startPremium >> totalDays; 16 | } 17 | /** 18 | * @dev Returns the pricing premium in internal base units. 19 | */ 20 | 21 | function _premium(string memory, uint256 expires, uint256) internal view override returns (uint256) { 22 | if (expires > block.timestamp) { 23 | return 0; 24 | } 25 | uint256 elapsed = block.timestamp - expires; 26 | uint256 premium = decayedPremium(elapsed); 27 | if (premium > endValue) { 28 | return premium - endValue; 29 | } 30 | return 0; 31 | } 32 | /** 33 | * @dev Returns the premium price at current time elapsed 34 | * @param elapsed time past since expiry 35 | */ 36 | 37 | function decayedPremium(uint256 elapsed) public view returns (uint256) { 38 | /// @dev The half-life of the premium price decay 39 | uint256 secondsInPeriod = 1 days; 40 | /// @dev 50% decay per period in wad format 41 | uint256 perPeriodDecayPercentWad = FixedPointMathLib.WAD / 2; 42 | uint256 premium = EDAPrice.currentPrice(startPremium, elapsed, secondsInPeriod, perPeriodDecayPercentWad); 43 | return premium; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/L2/discounts/CBIdDiscountValidator.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {MerkleProofLib} from "lib/solady/src/utils/MerkleProofLib.sol"; 5 | import {Ownable} from "solady/auth/Ownable.sol"; 6 | 7 | import {IDiscountValidator} from "src/L2/interface/IDiscountValidator.sol"; 8 | 9 | /// @title Discount Validator for: cb.id 10 | /// 11 | /// @notice Implements a simple Merkle Proof validator checking that the claimant is in the stored merkle tree. 12 | /// 13 | /// @author Coinbase 14 | contract CBIdDiscountValidator is Ownable, IDiscountValidator { 15 | /// @dev merkle tree root 16 | bytes32 public root; 17 | 18 | constructor(address owner_, bytes32 root_) { 19 | _initializeOwner(owner_); 20 | root = root_; 21 | } 22 | 23 | /// @notice Allows the owner to update the merkle root. 24 | /// 25 | /// @param root_ The new merkle tree root. 26 | function setRoot(bytes32 root_) external onlyOwner { 27 | root = root_; 28 | } 29 | 30 | /// @notice Required implementation for compatibility with IDiscountValidator. 31 | /// 32 | /// @dev The proof data must be encoded as `abi.encode(bytes32[] proof)`. 33 | /// 34 | /// @param claimer the discount claimer's address. 35 | /// @param validationData opaque bytes for performing the validation. 36 | /// 37 | /// @return `true` if the validation data provided is determined to be valid for the specified claimer, else `false`. 38 | function isValidDiscountRegistration(address claimer, bytes calldata validationData) external view returns (bool) { 39 | (bytes32[] memory proof) = abi.decode(validationData, (bytes32[])); 40 | return MerkleProofLib.verify(proof, root, keccak256(abi.encodePacked(claimer))); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/L2/discounts/ERC1155DiscountValidator.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; 5 | 6 | import {IDiscountValidator} from "src/L2/interface/IDiscountValidator.sol"; 7 | 8 | /// @title Discount Validator for: ERC1155 NFTs 9 | /// 10 | /// @notice Implements an NFT ownership validator for a stored `tokenId` for an ERC1155 `token` contract. 11 | /// This discount validator should only be used for "soul-bound" tokens. 12 | /// 13 | /// @author Coinbase (https://github.com/base-org/usernames) 14 | contract ERC1155DiscountValidator is IDiscountValidator { 15 | /// @notice The ERC1155 token contract to validate against. 16 | IERC1155 immutable token; 17 | 18 | /// @notice The ERC1155 token ID of the relevant NFT. 19 | uint256 immutable tokenId; 20 | 21 | /// @notice ERC1155 Discount Validator constructor. 22 | /// 23 | /// @param tokenAddress The address of the token contract. 24 | /// @param tokenId_ The ID of the token `claimer` must hold. 25 | constructor(address tokenAddress, uint256 tokenId_) { 26 | token = IERC1155(tokenAddress); 27 | tokenId = tokenId_; 28 | } 29 | 30 | /// @notice Required implementation for compatibility with IDiscountValidator. 31 | /// 32 | /// @dev No additional data is necessary to complete this validation. This validator checks that `claimer` has a nonzero 33 | /// `balanceOf` the stored `tokenId` for the stored `token` ERC1155 contract. 34 | /// 35 | /// @param claimer the discount claimer's address. 36 | /// 37 | /// @return `true` if the validation data provided is determined to be valid for the specified claimer, else `false`. 38 | function isValidDiscountRegistration(address claimer, bytes calldata) external view returns (bool) { 39 | return (token.balanceOf(claimer, tokenId) > 0); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/L2/discounts/ERC721DiscountValidator.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 5 | 6 | import {IDiscountValidator} from "src/L2/interface/IDiscountValidator.sol"; 7 | 8 | /// @title Discount Validator for: ERC721 NFTs 9 | /// 10 | /// @notice Implements an NFT ownership validator for a ERC721 `token` contract. 11 | /// This discount validator should only be used for "soul-bound" tokens. 12 | /// 13 | /// @author Coinbase (https://github.com/base-org/usernames) 14 | contract ERC721DiscountValidator is IDiscountValidator { 15 | /// @notice The ERC721 token contract to validate against. 16 | IERC721 immutable token; 17 | 18 | /// @notice ERC721 Discount Validator constructor. 19 | /// 20 | /// @param tokenAddress The address of the token contract. 21 | constructor(address tokenAddress) { 22 | token = IERC721(tokenAddress); 23 | } 24 | 25 | /// @notice Required implementation for compatibility with IDiscountValidator. 26 | /// 27 | /// @dev No additional data is necessary to complete this validation. This validator checks that `claimer` has a nonzero 28 | /// `balanceOf` the stored `token` ERC721 contract. 29 | /// 30 | /// @param claimer the discount claimer's address. 31 | /// 32 | /// @return `true` if the validation data provided is determined to be valid for the specified claimer, else `false`. 33 | function isValidDiscountRegistration(address claimer, bytes calldata) external view returns (bool) { 34 | return (token.balanceOf(claimer) > 0); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/L2/interface/IDiscountValidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | /// @title Discount Validator Interface 5 | /// 6 | /// @notice Common interface which all Discount Validators must implement. 7 | /// The logic specific to each integration must ultimately be consumable as the `bool` returned from 8 | /// `isValidDiscountRegistration`. 9 | interface IDiscountValidator { 10 | /// @notice Required implementation for compatibility with IDiscountValidator. 11 | /// 12 | /// @dev Each implementation will have unique requirements for the data necessary to perform 13 | /// a meaningul validation. Implementations must describe here how to pack relevant `validationData`. 14 | /// Ex: `bytes validationData = abi.encode(bytes32 key, bytes32[] proof)` 15 | /// 16 | /// @param claimer the discount claimer's address. 17 | /// @param validationData opaque bytes for performing the validation. 18 | /// 19 | /// @return `true` if the validation data provided is determined to be valid for the specified claimer, else `false`. 20 | function isValidDiscountRegistration(address claimer, bytes calldata validationData) external returns (bool); 21 | } 22 | -------------------------------------------------------------------------------- /src/L2/interface/IPriceOracle.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.17 <0.9.0; 3 | 4 | interface IPriceOracle { 5 | struct Price { 6 | uint256 base; 7 | uint256 premium; 8 | } 9 | 10 | /** 11 | * @dev Returns the price to register or renew a name. 12 | * @param name The name being registered or renewed. 13 | * @param expires When the name presently expires (`launchTime` if this is a new registration). 14 | * @param duration How long the name is being registered or extended for, in seconds. 15 | * @return price Price struct containing base price and premium price 16 | */ 17 | function price(string calldata name, uint256 expires, uint256 duration) external view returns (Price calldata); 18 | } 19 | -------------------------------------------------------------------------------- /src/L2/interface/IRegistrarController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | interface IRegistrarController { 5 | /// @notice Getter method for checking whether an address has registered with a discount. 6 | /// 7 | /// @param registrant The address of the registrant. 8 | /// 9 | /// @return hasRegisteredWithDiscount Returns `true` if the registrant has previously claimed a discount, else `false`. 10 | function discountedRegistrants(address registrant) external returns (bool); 11 | 12 | /// @notice Checks whether any of the provided addresses have registered with a discount. 13 | /// 14 | /// @param addresses The array of addresses to check for discount registration. 15 | /// 16 | /// @return `true` if any of the addresses have already registered with a discount, else `false`. 17 | function hasRegisteredWithDiscount(address[] memory addresses) external view returns (bool); 18 | } 19 | -------------------------------------------------------------------------------- /src/L2/interface/IReverseRegistrar.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | interface IReverseRegistrar { 5 | function claim(address claimant) external; 6 | 7 | function setNameForAddr(address addr, address owner, address resolver, string memory name) 8 | external 9 | returns (bytes32); 10 | } 11 | -------------------------------------------------------------------------------- /src/L2/interface/IReverseRegistrarV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | /// @title Interface for ReverseRegistrarV2 5 | interface IReverseRegistrarV2 { 6 | /// @notice Transfers ownership of the base-specific reverse ENS record for `msg.sender` to the provided `owner`. 7 | /// 8 | /// @param owner The address to set as the owner of the reverse record in ENS. 9 | /// 10 | /// @return The ENS node hash of the Base network-specific reverse record. 11 | function claim(address owner) external returns (bytes32); 12 | 13 | /// @notice Sets the reverse record `name` for `addr`. 14 | /// 15 | /// @param addr The name records will be set for this address. 16 | /// @param signatureExpiry The timestamp expiration of the signature. 17 | /// @param name The name that will be stored for `addr`. 18 | /// @param cointypes The array of networks-as-cointypes used in replayable reverse sets. 19 | /// @param signature The signature bytes. 20 | function setNameForAddrWithSignature( 21 | address addr, 22 | uint256 signatureExpiry, 23 | string calldata name, 24 | uint256[] memory cointypes, 25 | bytes memory signature 26 | ) external returns (bytes32); 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/EDAPrice.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import "solady/utils/FixedPointMathLib.sol"; 5 | 6 | library EDAPrice { 7 | /// @notice returns the current price of an exponential price decay auction defined by the passed params 8 | 9 | /// @dev reverts if perPeriodDecayPercentWad >= 1e18 10 | /// @dev reverts if uint256 secondsInPeriod = 0 11 | /// @dev reverts if startPrice * multiplier overflows 12 | /// @dev reverts if lnWad(percentWadRemainingPerPeriod) * ratio) overflows 13 | 14 | /// @param startPrice the starting price of the auction 15 | /// @param secondsElapsed the seconds elapsed since auction start 16 | /// @param secondsInPeriod the seconds over which the price should decay perPeriodDecayPercentWad 17 | /// @param perPeriodDecayPercentWad the percent the price should decay during secondsInPeriod, 100% = 1e18 18 | 19 | /// @return price the current auction price 20 | 21 | function currentPrice( 22 | uint256 startPrice, 23 | uint256 secondsElapsed, 24 | uint256 secondsInPeriod, 25 | uint256 perPeriodDecayPercentWad 26 | ) internal pure returns (uint256) { 27 | uint256 ratio = FixedPointMathLib.divWad(secondsElapsed, secondsInPeriod); 28 | uint256 percentWadRemainingPerPeriod = FixedPointMathLib.WAD - perPeriodDecayPercentWad; 29 | 30 | // percentWadRemainingPerPeriod can be safely cast because < 1e18 31 | // ratio can be safely cast because will not overflow unless ratio > type(int256).max, 32 | // which would require secondsElapsed > type(int256).max, i.e. > 5.78e76 or 1.8e69 years 33 | 34 | int256 multiplier = FixedPointMathLib.powWad(int256(percentWadRemainingPerPeriod), int256(ratio)); 35 | uint256 price = FixedPointMathLib.mulWad(startPrice, uint256(multiplier)); 36 | return price; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/lib/Sha3.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | /// @title Sha3 Hex Encoding 5 | /// 6 | /// @notice This method is copied from the ENS `ReverseRegistrar` contract. It's been moved to its own 7 | /// lib for readability and testing purposes. 8 | /// See: https://github.com/ensdomains/ens-contracts/blob/545a0104d0fbdd10865743e25729a921a76fd950/contracts/reverseRegistrar/ReverseRegistrar.sol#L164-L181 9 | /// 10 | /// @author ENS (https://github.com/ensdomains/ens-contracts) 11 | library Sha3 { 12 | /// @notice Hex encoding of "0123456789abcdef" 13 | bytes32 constant ALPHABET = 0x30_31_32_33_34_35_36_37_38_39_61_62_63_64_65_66_00000000000000000000000000000000; 14 | 15 | /// @notice Calculates the hash of a lower-case Ethereum address 16 | /// 17 | /// @param addr The address to hash 18 | /// 19 | /// @return ret The SHA3 hash of the lower-case hexadecimal encoding of the input address. 20 | function hexAddress(address addr) internal pure returns (bytes32 ret) { 21 | assembly { 22 | for { let i := 40 } i {} { 23 | i := sub(i, 1) 24 | mstore8(i, byte(and(addr, 0xf), ALPHABET)) 25 | addr := shr(4, addr) 26 | i := sub(i, 1) 27 | mstore8(i, byte(and(addr, 0xf), ALPHABET)) 28 | addr := shr(4, addr) 29 | } 30 | 31 | ret := keccak256(0, 40) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/SignatureVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ECDSA} from "solady/utils/ECDSA.sol"; 5 | 6 | library SignatureVerifier { 7 | error SignatureExpired(); 8 | 9 | /// @notice Generates a hash for signing/verifying. 10 | /// 11 | /// @param target The address the signature is for. 12 | /// @param request The original request that was sent. 13 | /// @param result The `result` field of the response (not including the signature part). 14 | function makeSignatureHash(address target, uint64 expires, bytes memory request, bytes memory result) 15 | internal 16 | pure 17 | returns (bytes32) 18 | { 19 | return keccak256(abi.encodePacked(hex"1900", target, expires, keccak256(request), keccak256(result))); 20 | } 21 | 22 | /// @notice Verifies a signed message returned from a callback. 23 | /// 24 | /// @param request The original request that was sent. 25 | /// @param response An ABI encoded tuple of `(bytes result, uint64 expires, bytes sig)`, where `result` is the data to return 26 | /// to the caller, and `sig` is the (r,s,v) encoded message signature. 27 | /// @return signer The address that signed this message. 28 | /// @return result The `result` decoded from `response`. 29 | function verify(bytes calldata request, bytes calldata response) internal view returns (address, bytes memory) { 30 | (bytes memory result, uint64 expires, bytes memory sig) = abi.decode(response, (bytes, uint64, bytes)); 31 | if (expires < block.timestamp) revert SignatureExpired(); 32 | address signer = ECDSA.recover(makeSignatureHash(address(this), expires, request, result), sig); 33 | return (signer, result); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/util/Constants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | // @param ETH_NODE The node hash of "eth" 5 | bytes32 constant ETH_NODE = 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae; 6 | // @param BASE_ETH_NODE The node hash of "base.eth" 7 | bytes32 constant BASE_ETH_NODE = 0xff1e3c0eb00ec714e34b6114125fbde1dea2f24a72fbf672e7b7fd5690328e10; 8 | // @param REVERSE_NODE The node hash of "reverse" 9 | bytes32 constant REVERSE_NODE = 0xa097f6721ce401e757d1223a763fef49b8b5f90bb18567ddb86fd205dff71d34; 10 | // @param ADDR_REVERSE_NODE The node hash of "addr.reverse" 11 | bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2; 12 | // @param BASE_REVERSE_NODE The ENSIP-19 compliant base-specific reverse node hash of "80002105.reverse" 13 | bytes32 constant BASE_REVERSE_NODE = 0x08d9b0993eb8c4da57c37a4b84a6e384c2623114ff4e9370ed51c9b8935109ba; 14 | // @param GRACE_PERIOD the grace period for expired names 15 | uint256 constant GRACE_PERIOD = 90 days; 16 | // @param BASE_ETH_NAME The dnsName of "base.eth" returned by NameEncoder.dnsEncode("base.eth") 17 | bytes constant BASE_ETH_NAME = hex"04626173650365746800"; 18 | -------------------------------------------------------------------------------- /test/BaseRegistrar/AddController.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BaseRegistrar} from "src/L2/BaseRegistrar.sol"; 5 | import {BaseRegistrarBase} from "./BaseRegistrarBase.t.sol"; 6 | import {Ownable} from "solady/auth/Ownable.sol"; 7 | 8 | contract AddController is BaseRegistrarBase { 9 | function test_allowsOwnerToSetController(address controller) public { 10 | vm.expectEmit(); 11 | emit BaseRegistrar.ControllerAdded(controller); 12 | vm.prank(owner); 13 | baseRegistrar.addController(controller); 14 | assertTrue(baseRegistrar.controllers(controller)); 15 | } 16 | 17 | function test_reverts_whenCalledByNonOwner(address caller) public { 18 | vm.assume(caller != owner); 19 | vm.prank(caller); 20 | vm.expectRevert(Ownable.Unauthorized.selector); 21 | baseRegistrar.addController(caller); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/BaseRegistrar/ContractURI.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {BaseRegistrarBase} from "./BaseRegistrarBase.t.sol"; 6 | 7 | contract ContractURI is BaseRegistrarBase { 8 | function test_contractURI_isReturnedAsExpected() public view { 9 | assertEq(keccak256(bytes(baseRegistrar.contractURI())), keccak256(bytes(collectionURI))); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/BaseRegistrar/IsAvailable.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BaseRegistrar} from "src/L2/BaseRegistrar.sol"; 5 | import {BaseRegistrarBase} from "./BaseRegistrarBase.t.sol"; 6 | import {GRACE_PERIOD} from "src/util/Constants.sol"; 7 | 8 | contract IsAvailable is BaseRegistrarBase { 9 | function test_returnsAvailabilityAsExpected() public { 10 | _registrationSetup(); 11 | uint256 expires = _registerName(label, user, duration); 12 | assertFalse(baseRegistrar.isAvailable(id)); 13 | 14 | vm.warp(expires + GRACE_PERIOD - 1); // in grace period 15 | assertFalse(baseRegistrar.isAvailable(id)); 16 | 17 | vm.warp(expires + GRACE_PERIOD + 1); // past grace period 18 | assertTrue(baseRegistrar.isAvailable(id)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/BaseRegistrar/Name.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {BaseRegistrarBase} from "./BaseRegistrarBase.t.sol"; 6 | 7 | contract Name is BaseRegistrarBase { 8 | function test_nameIsSetAsExpected() public view { 9 | string memory expectedName = "Basenames"; 10 | assertEq(keccak256(bytes(baseRegistrar.name())), keccak256(bytes(expectedName))); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/BaseRegistrar/OwnerOf.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BaseRegistrar} from "src/L2/BaseRegistrar.sol"; 5 | import {BaseRegistrarBase} from "./BaseRegistrarBase.t.sol"; 6 | 7 | contract OwnerOf is BaseRegistrarBase { 8 | function test_reverts_whenNameHasExpired() public { 9 | _registrationSetup(); 10 | uint256 expires = _registerName(label, user, duration); 11 | 12 | vm.warp(expires + 1); 13 | vm.expectRevert(abi.encodeWithSelector(BaseRegistrar.Expired.selector, id)); 14 | baseRegistrar.ownerOf(id); 15 | } 16 | 17 | function test_returnsTheOwner(address nameOwner) public { 18 | vm.assume(nameOwner != address(0)); 19 | _registrationSetup(); 20 | _registerName(label, nameOwner, duration); 21 | address returnedOwner = baseRegistrar.ownerOf(id); 22 | assertTrue(returnedOwner == nameOwner); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/BaseRegistrar/RemoveController.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BaseRegistrar} from "src/L2/BaseRegistrar.sol"; 5 | import {BaseRegistrarBase} from "./BaseRegistrarBase.t.sol"; 6 | import {Ownable} from "solady/auth/Ownable.sol"; 7 | 8 | contract RemoveController is BaseRegistrarBase { 9 | function test_allowsOwnerToRemoveController(address controller) public { 10 | vm.prank(owner); 11 | baseRegistrar.addController(controller); 12 | assertTrue(baseRegistrar.controllers(controller)); 13 | 14 | vm.expectEmit(); 15 | emit BaseRegistrar.ControllerRemoved(controller); 16 | vm.prank(owner); 17 | baseRegistrar.removeController(controller); 18 | assertFalse(baseRegistrar.controllers(controller)); 19 | } 20 | 21 | function test_reverts_whenCalledByNonOwner(address caller) public { 22 | vm.assume(caller != owner); 23 | vm.prank(caller); 24 | vm.expectRevert(Ownable.Unauthorized.selector); 25 | baseRegistrar.removeController(caller); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/BaseRegistrar/SetBaseTokenURI.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BaseRegistrar} from "src/L2/BaseRegistrar.sol"; 5 | import {BaseRegistrarBase} from "./BaseRegistrarBase.t.sol"; 6 | import {Ownable} from "solady/auth/Ownable.sol"; 7 | import {LibString} from "solady/utils/LibString.sol"; 8 | 9 | contract SetBaseTokenURI is BaseRegistrarBase { 10 | using LibString for uint256; 11 | 12 | string public newBaseURI = "https://newurl.org/"; 13 | 14 | function test_allowsTheOwnerToSetTheBaseURI() public { 15 | vm.expectEmit(address(baseRegistrar)); 16 | emit BaseRegistrar.BatchMetadataUpdate(1, type(uint256).max); 17 | vm.prank(owner); 18 | baseRegistrar.setBaseTokenURI(newBaseURI); 19 | 20 | _registrationSetup(); 21 | vm.warp(blockTimestamp); 22 | vm.prank(controller); 23 | baseRegistrar.register(id, user, duration); 24 | 25 | string memory returnedURI = baseRegistrar.tokenURI(id); 26 | string memory expectedURI = string.concat(newBaseURI, id.toString()); 27 | assertEq(keccak256(bytes(returnedURI)), keccak256(bytes(expectedURI))); 28 | } 29 | 30 | function test_reverts_whenCalledByNonOwner(address caller) public { 31 | vm.assume(caller != owner); 32 | vm.prank(caller); 33 | vm.expectRevert(Ownable.Unauthorized.selector); 34 | baseRegistrar.setBaseTokenURI(newBaseURI); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/BaseRegistrar/SetContractURI.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {BaseRegistrarBase} from "./BaseRegistrarBase.t.sol"; 6 | import {BaseRegistrar} from "src/L2/BaseRegistrar.sol"; 7 | import {Ownable} from "solady/auth/Ownable.sol"; 8 | 9 | contract SetContractURI is BaseRegistrarBase { 10 | string newContractURI = "NewURI"; 11 | 12 | function test_allowsTheOwnerToSetTheContractURI() public { 13 | vm.expectEmit(address(baseRegistrar)); 14 | emit BaseRegistrar.ContractURIUpdated(); 15 | 16 | vm.prank(owner); 17 | baseRegistrar.setContractURI(newContractURI); 18 | assertEq(keccak256(bytes(baseRegistrar.contractURI())), keccak256(bytes(newContractURI))); 19 | } 20 | 21 | function test_reverts_whenCalledByNonOwner(address caller) public { 22 | vm.assume(caller != owner); 23 | vm.prank(caller); 24 | vm.expectRevert(Ownable.Unauthorized.selector); 25 | baseRegistrar.setBaseTokenURI(newContractURI); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/BaseRegistrar/SetResolver.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BaseRegistrar} from "src/L2/BaseRegistrar.sol"; 5 | import {BaseRegistrarBase} from "./BaseRegistrarBase.t.sol"; 6 | import {Ownable} from "solady/auth/Ownable.sol"; 7 | import {ENS} from "ens-contracts/registry/ENS.sol"; 8 | import {BASE_ETH_NODE} from "src/util/Constants.sol"; 9 | 10 | contract SetResolver is BaseRegistrarBase { 11 | function test_allowsTheOwnerToSetTheResolver(address resolver) public { 12 | vm.expectEmit(address(registry)); 13 | emit ENS.NewResolver(BASE_ETH_NODE, resolver); 14 | vm.prank(owner); 15 | baseRegistrar.setResolver(resolver); 16 | address returnedResolver = registry.resolver(BASE_ETH_NODE); 17 | assertTrue(returnedResolver == resolver); 18 | } 19 | 20 | function test_reverts_whenCalledByNonOwner(address caller) public { 21 | vm.assume(caller != owner); 22 | vm.prank(caller); 23 | vm.expectRevert(Ownable.Unauthorized.selector); 24 | baseRegistrar.setResolver(caller); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/BaseRegistrar/SupportsInterface.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {BaseRegistrarBase} from "./BaseRegistrarBase.t.sol"; 6 | import {IERC721} from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol"; 7 | import {IERC165} from "openzeppelin-contracts/contracts/utils/introspection/ERC165.sol"; 8 | 9 | interface Reclaim { 10 | function reclaim(uint256, address) external; 11 | } 12 | 13 | contract SupportsInterface is BaseRegistrarBase { 14 | function test_supportsMetaInterfaceId() public view { 15 | assertTrue(baseRegistrar.supportsInterface(type(IERC165).interfaceId)); 16 | } 17 | 18 | function test_supportsIERC721InterfaceId() public view { 19 | assertTrue(baseRegistrar.supportsInterface(type(IERC721).interfaceId)); 20 | } 21 | 22 | function test_supportsReclaimInterfaceId() public view { 23 | assertTrue(baseRegistrar.supportsInterface(type(Reclaim).interfaceId)); 24 | } 25 | 26 | function test_doesNotSupportArbitraryInterfaceIds(bytes4 ifaceId) public view { 27 | vm.assume( 28 | ifaceId != type(IERC165).interfaceId && ifaceId != type(IERC721).interfaceId 29 | && ifaceId != type(Reclaim).interfaceId 30 | ); 31 | assertFalse(baseRegistrar.supportsInterface(ifaceId)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/BaseRegistrar/Symbol.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {BaseRegistrarBase} from "./BaseRegistrarBase.t.sol"; 6 | 7 | contract Symbol is BaseRegistrarBase { 8 | function test_symbolIsSetAsExpected() public view { 9 | string memory expectedSymbol = "BASENAME"; 10 | assertEq(keccak256(bytes(baseRegistrar.symbol())), keccak256(bytes(expectedSymbol))); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/BaseRegistrar/TokenURI.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {BaseRegistrarBase} from "./BaseRegistrarBase.t.sol"; 6 | import {BaseRegistrar} from "src/L2/BaseRegistrar.sol"; 7 | import {LibString} from "solady/utils/LibString.sol"; 8 | 9 | contract TokenURI is BaseRegistrarBase { 10 | using LibString for uint256; 11 | 12 | function test_tokenURIIsSetAsExpected() public { 13 | _registrationSetup(); 14 | vm.warp(blockTimestamp); 15 | vm.prank(controller); 16 | baseRegistrar.register(id, user, duration); 17 | 18 | string memory expectedURI = string.concat(baseURI, id.toString()); 19 | assertEq(keccak256(bytes(baseRegistrar.tokenURI(id))), keccak256(bytes(expectedURI))); 20 | } 21 | 22 | function test_returnsTokenURI_ifTheTokenIsExpired() public { 23 | _registrationSetup(); 24 | vm.warp(blockTimestamp); 25 | vm.prank(controller); 26 | uint256 expires = baseRegistrar.register(id, user, duration); 27 | vm.warp(expires + 1); 28 | baseRegistrar.tokenURI(id); 29 | 30 | string memory expectedURI = string.concat(baseURI, id.toString()); 31 | assertEq(keccak256(bytes(baseRegistrar.tokenURI(id))), keccak256(bytes(expectedURI))); 32 | } 33 | 34 | function test_reverts_ifTheTokenHasNotBeenRegistered() public { 35 | vm.expectRevert(abi.encodeWithSelector(BaseRegistrar.NonexistentToken.selector, id)); 36 | baseRegistrar.tokenURI(id); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/EARegistrarController/Available.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {EARegistrarControllerBase} from "./EARegistrarControllerBase.t.sol"; 5 | 6 | contract Available is EARegistrarControllerBase { 7 | function test_returnsFalse_whenNotAvailableOnBase() public { 8 | base.setAvailable(uint256(nameLabel), false); 9 | assertFalse(controller.available(name)); 10 | } 11 | 12 | function test_returnsFalse_whenInvalidLength() public { 13 | base.setAvailable(uint256(shortNameLabel), true); 14 | assertFalse(controller.available(shortName)); 15 | } 16 | 17 | function test_returnsTrue_whenValidAndAvailable() public { 18 | base.setAvailable(uint256(nameLabel), true); 19 | assertTrue(controller.available(name)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/EARegistrarController/DiscountedRegisterPrice.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {EARegistrarControllerBase} from "./EARegistrarControllerBase.t.sol"; 5 | import {EARegistrarController} from "src/L2/EARegistrarController.sol"; 6 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 7 | 8 | contract DiscountedRegisterPrice is EARegistrarControllerBase { 9 | function test_returnsADiscountedPrice_whenThePriceIsGreaterThanTheDiscount(uint256 price) public { 10 | vm.assume(price > discountAmount); 11 | prices.setPrice(name, IPriceOracle.Price({base: price, premium: 0})); 12 | vm.prank(owner); 13 | controller.setDiscountDetails(_getDefaultDiscount()); 14 | 15 | uint256 expectedPrice = price - discountAmount; 16 | uint256 retPrice = controller.discountedRegisterPrice(name, duration, discountKey); 17 | assertEq(retPrice, expectedPrice); 18 | } 19 | 20 | function test_returnsZero_whenThePriceIsLessThanOrEqualToTheDiscount(uint256 price) public { 21 | vm.assume(price <= discountAmount && price > 0); 22 | prices.setPrice(name, IPriceOracle.Price({base: price, premium: 0})); 23 | vm.prank(owner); 24 | controller.setDiscountDetails(_getDefaultDiscount()); 25 | 26 | uint256 retPrice = controller.discountedRegisterPrice(name, duration, discountKey); 27 | assertEq(retPrice, 0); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/EARegistrarController/RecoverFunds.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {EARegistrarControllerBase} from "./EARegistrarControllerBase.t.sol"; 5 | import {EARegistrarController} from "src/L2/EARegistrarController.sol"; 6 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 7 | import {Ownable} from "solady/auth/Ownable.sol"; 8 | import {MockUSDC} from "test/mocks/MockUSDC.sol"; 9 | 10 | contract RecoverFunds is EARegistrarControllerBase { 11 | MockUSDC public usdc; 12 | 13 | function test_reverts_ifCalledByNonOwner(address caller, uint256 amount) public { 14 | vm.assume(caller != owner); 15 | vm.assume(amount > 0 && amount < type(uint128).max); 16 | vm.expectRevert(Ownable.Unauthorized.selector); 17 | vm.prank(caller); 18 | controller.recoverFunds(address(usdc), caller, amount); 19 | } 20 | 21 | function test_allowsTheOwnerToRecoverFunds(uint256 amount) public { 22 | vm.assume(amount > 0 && amount < type(uint128).max); 23 | _setupTokenAndAssignBalanceToController(amount); 24 | assertEq(usdc.balanceOf(owner), 0); 25 | 26 | vm.prank(owner); 27 | controller.recoverFunds(address(usdc), owner, amount); 28 | assertEq(usdc.balanceOf(owner), amount); 29 | } 30 | 31 | function _setupTokenAndAssignBalanceToController(uint256 balance) internal { 32 | usdc = new MockUSDC(); 33 | usdc.mint(address(controller), balance); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/EARegistrarController/RentPrice.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {EARegistrarControllerBase} from "./EARegistrarControllerBase.t.sol"; 5 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 6 | 7 | contract RentPrice is EARegistrarControllerBase { 8 | function test_returnsPrice_fromPricingOracle() public view { 9 | IPriceOracle.Price memory retPrices = controller.rentPrice(name, duration); 10 | assertEq(retPrices.base, prices.DEFAULT_BASE_WEI()); 11 | assertEq(retPrices.premium, prices.DEFAULT_INCLUDED_PREMIUM()); 12 | } 13 | 14 | function test_fuzz_returnsPrice_fromPricingOracle(uint256 fuzzBase, uint256 fuzzPremium) public { 15 | vm.assume(fuzzBase != 0 && fuzzBase < type(uint128).max); 16 | vm.assume(fuzzPremium < type(uint128).max); 17 | IPriceOracle.Price memory expectedPrice = IPriceOracle.Price({base: fuzzBase, premium: fuzzPremium}); 18 | prices.setPrice(name, expectedPrice); 19 | IPriceOracle.Price memory retPrices = controller.rentPrice(name, 0); 20 | assertEq(retPrices.base, expectedPrice.base); 21 | assertEq(retPrices.premium, expectedPrice.premium); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/EARegistrarController/SetPaymentReceiver.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {EARegistrarControllerBase} from "./EARegistrarControllerBase.t.sol"; 5 | import {EARegistrarController} from "src/L2/EARegistrarController.sol"; 6 | import {Ownable} from "solady/auth/Ownable.sol"; 7 | 8 | contract SetPaymentReceiver is EARegistrarControllerBase { 9 | function test_reverts_ifCalledByNonOwner(address caller) public { 10 | vm.assume(caller != owner && caller != address(0)); 11 | vm.expectRevert(Ownable.Unauthorized.selector); 12 | vm.prank(caller); 13 | controller.setPaymentReceiver(caller); 14 | } 15 | 16 | function test_reverts_ifNewPaymentReceiver_isZeroAddress() public { 17 | vm.expectRevert(EARegistrarController.InvalidPaymentReceiver.selector); 18 | vm.prank(owner); 19 | controller.setPaymentReceiver(address(0)); 20 | } 21 | 22 | function test_allowsTheOwner_toSetThePaymentReceiver(address newReceiver) public { 23 | vm.assume(newReceiver != address(0)); 24 | vm.expectEmit(address(controller)); 25 | emit EARegistrarController.PaymentReceiverUpdated(newReceiver); 26 | vm.prank(owner); 27 | controller.setPaymentReceiver(newReceiver); 28 | assertEq(newReceiver, controller.paymentReceiver()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/EARegistrarController/SetPriceOracle.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {EARegistrarControllerBase} from "./EARegistrarControllerBase.t.sol"; 5 | import {EARegistrarController} from "src/L2/EARegistrarController.sol"; 6 | import {MockPriceOracle} from "test/mocks/MockPriceOracle.sol"; 7 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 8 | import {Ownable} from "solady/auth/Ownable.sol"; 9 | 10 | contract SetPriceOracle is EARegistrarControllerBase { 11 | function test_reverts_ifCalledByNonOwner(address caller) public { 12 | vm.assume(caller != owner); 13 | MockPriceOracle newPrices = new MockPriceOracle(); 14 | vm.expectRevert(Ownable.Unauthorized.selector); 15 | vm.prank(caller); 16 | controller.setPriceOracle(IPriceOracle(address(newPrices))); 17 | } 18 | 19 | function test_setsThePriceOracleAccordingly() public { 20 | vm.expectEmit(); 21 | MockPriceOracle newPrices = new MockPriceOracle(); 22 | emit EARegistrarController.PriceOracleUpdated(address(newPrices)); 23 | vm.prank(owner); 24 | controller.setPriceOracle(IPriceOracle(address(newPrices))); 25 | assertEq(address(controller.prices()), address(newPrices)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/EARegistrarController/SetReverseRegistrar.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {EARegistrarControllerBase} from "./EARegistrarControllerBase.t.sol"; 5 | import {EARegistrarController} from "src/L2/EARegistrarController.sol"; 6 | import {MockReverseRegistrar} from "test/mocks/MockReverseRegistrar.sol"; 7 | import {IReverseRegistrar} from "src/L2/interface/IReverseRegistrar.sol"; 8 | import {Ownable} from "solady/auth/Ownable.sol"; 9 | 10 | contract SetReverseRegistrar is EARegistrarControllerBase { 11 | function test_reverts_ifCalledByNonOwner(address caller) public { 12 | vm.assume(caller != owner); 13 | MockReverseRegistrar newReverse = new MockReverseRegistrar(); 14 | vm.expectRevert(Ownable.Unauthorized.selector); 15 | vm.prank(caller); 16 | controller.setReverseRegistrar(IReverseRegistrar(address(newReverse))); 17 | } 18 | 19 | function test_setsTheReverseRegistrarAccordingly() public { 20 | vm.expectEmit(); 21 | MockReverseRegistrar newReverse = new MockReverseRegistrar(); 22 | emit EARegistrarController.ReverseRegistrarUpdated(address(newReverse)); 23 | vm.prank(owner); 24 | controller.setReverseRegistrar(IReverseRegistrar(address(newReverse))); 25 | assertEq(address(controller.reverseRegistrar()), address(newReverse)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/EARegistrarController/Valid.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {EARegistrarControllerBase} from "./EARegistrarControllerBase.t.sol"; 5 | 6 | contract Valid is EARegistrarControllerBase { 7 | function test_returnsTrue_whenValid() public view { 8 | assertTrue(controller.valid("abc")); 9 | assertTrue(controller.valid("abcdef")); 10 | assertTrue(controller.valid("abcdefghijklmnop")); 11 | } 12 | 13 | function test_returnsFalse_whenInvalid() public view { 14 | assertFalse(controller.valid("")); 15 | assertFalse(controller.valid("a")); 16 | assertFalse(controller.valid("ab")); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/EARegistrarController/WithdrawETH.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {EARegistrarControllerBase} from "./EARegistrarControllerBase.t.sol"; 5 | import {EARegistrarController} from "src/L2/EARegistrarController.sol"; 6 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 7 | 8 | contract WithdrawETH is EARegistrarControllerBase { 9 | function test_alwaysSendsTheBalanceToTheOwner(address caller) public { 10 | vm.deal(address(controller), 1 ether); 11 | assertEq(payments.balance, 0); 12 | vm.prank(caller); 13 | controller.withdrawETH(); 14 | assertEq(payments.balance, 1 ether); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/ExponentialPremiumPriceOracle/ExponentialPremiumOracleBase.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ~0.8.17; 3 | 4 | import {Test, console} from "forge-std/Test.sol"; 5 | import {StablePriceOracle} from "src/L2/StablePriceOracle.sol"; 6 | import {ExponentialPremiumPriceOracle} from "src/L2/ExponentialPremiumPriceOracle.sol"; 7 | 8 | contract ExponentialPremiumOracleBase is Test { 9 | ExponentialPremiumPriceOracle oracle; 10 | 11 | uint256 rent1; 12 | uint256 rent2; 13 | uint256 rent3; 14 | uint256 rent4; 15 | uint256 rent5; 16 | uint256 rent10; 17 | 18 | uint256 startPremium = 1e18; 19 | uint256 totalDays = 21; 20 | 21 | function setUp() public { 22 | uint256[] memory rentPrices = new uint256[](6); 23 | 24 | rent1 = 1e18; 25 | rent2 = 2e18; 26 | rent3 = 3e18; 27 | rent4 = 4e18; 28 | rent5 = 5e18; 29 | rent10 = 6e18; 30 | 31 | rentPrices[0] = rent1; 32 | rentPrices[1] = rent2; 33 | rentPrices[2] = rent3; 34 | rentPrices[3] = rent4; 35 | rentPrices[4] = rent5; 36 | rentPrices[5] = rent10; 37 | 38 | oracle = new ExponentialPremiumPriceOracle(rentPrices, startPremium, totalDays); 39 | } 40 | 41 | function test_constructor() public view { 42 | assertEq(oracle.startPremium(), startPremium); 43 | assertEq(oracle.endValue(), startPremium >> totalDays); 44 | assertEq(oracle.price1Letter(), rent1); 45 | assertEq(oracle.price2Letter(), rent2); 46 | assertEq(oracle.price3Letter(), rent3); 47 | assertEq(oracle.price4Letter(), rent4); 48 | assertEq(oracle.price5Letter(), rent5); 49 | assertEq(oracle.price10Letter(), rent10); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/ExponentialPremiumPriceOracle/decayedPremium.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ~0.8.17; 3 | 4 | import {Test, console} from "forge-std/Test.sol"; 5 | import {ExponentialPremiumOracleBase} from "./ExponentialPremiumOracleBase.t.sol"; 6 | import "solady/utils/FixedPointMathLib.sol"; 7 | 8 | contract DecayedPremium is ExponentialPremiumOracleBase { 9 | function test_decayedPremium_zeroElapsed() public view { 10 | uint256 elapsed = 0; 11 | uint256 expectedPremium = startPremium; 12 | uint256 actualPremium = oracle.decayedPremium(elapsed); 13 | assertEq(actualPremium, expectedPremium); 14 | } 15 | 16 | function test_decayedPremium_halfPeriod() public view { 17 | uint256 elapsed = 1 days / 2; 18 | uint256 expectedPremium = 707106781186547524; // Calculated expected value for premium price after 1/2 day 19 | uint256 actualPremium = oracle.decayedPremium(elapsed); 20 | assertEq(actualPremium, expectedPremium); 21 | } 22 | 23 | function test_decayedPremium_threePeriods() public view { 24 | uint256 elapsed = 3 days; 25 | uint256 expectedPremium = 124999999999999999; // Calculated expected value for premium price after 3 days 26 | uint256 actualPremium = oracle.decayedPremium(elapsed); 27 | assertEq(actualPremium, expectedPremium); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/Fork/BaseMainnetConfig.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {AbstractForkSuite} from "./AbstractForkSuite.t.sol"; 5 | import {BaseMainnet as C} from "./BaseMainnetConstants.sol"; 6 | 7 | abstract contract BaseMainnetConfig is AbstractForkSuite { 8 | function forkAlias() internal pure override returns (string memory) { 9 | return "base-mainnet"; 10 | } 11 | 12 | function registry() internal pure override returns (address) { 13 | return C.REGISTRY; 14 | } 15 | 16 | function baseRegistrar() internal pure override returns (address) { 17 | return C.BASE_REGISTRAR; 18 | } 19 | 20 | function legacyGaController() internal pure override returns (address) { 21 | return C.LEGACY_GA_CONTROLLER; 22 | } 23 | 24 | function legacyL2Resolver() internal pure override returns (address) { 25 | return C.LEGACY_L2_RESOLVER; 26 | } 27 | 28 | function legacyReverseRegistrar() internal pure override returns (address) { 29 | return C.LEGACY_REVERSE_REGISTRAR; 30 | } 31 | 32 | function upgradeableControllerProxy() internal pure override returns (address) { 33 | return C.UPGRADEABLE_CONTROLLER_PROXY; 34 | } 35 | 36 | function upgradeableL2ResolverProxy() internal pure override returns (address) { 37 | return C.UPGRADEABLE_L2_RESOLVER_PROXY; 38 | } 39 | 40 | function ensL2ReverseRegistrar() internal pure override returns (address) { 41 | return C.ENS_L2_REVERSE_REGISTRAR; 42 | } 43 | 44 | function l2Owner() internal pure override returns (address) { 45 | return C.L2_OWNER; 46 | } 47 | 48 | function migrationController() internal pure override returns (address) { 49 | return C.MIGRATION_CONTROLLER; 50 | } 51 | 52 | function baseCoinType() internal pure override returns (uint256) { 53 | return C.BASE_MAINNET_COINTYPE; 54 | } 55 | 56 | function baseReverseParentNode() internal pure override returns (bytes32) { 57 | return C.BASE_MAINNET_REVERSE_NODE; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/Fork/BaseMainnetConstants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | library BaseMainnet { 5 | // ENS / Basenames addresses on Base Mainnet 6 | address constant REGISTRY = 0xB94704422c2a1E396835A571837Aa5AE53285a95; 7 | address constant BASE_REGISTRAR = 0x03c4738Ee98aE44591e1A4A4F3CaB6641d95DD9a; 8 | address constant LEGACY_GA_CONTROLLER = 0x4cCb0BB02FCABA27e82a56646E81d8c5bC4119a5; 9 | address constant LEGACY_L2_RESOLVER = 0xC6d566A56A1aFf6508b41f6c90ff131615583BCD; 10 | // ReverseRegistrar with correct reverse node configured for Base Mainnet 11 | address constant LEGACY_REVERSE_REGISTRAR = 0x79EA96012eEa67A83431F1701B3dFf7e37F9E282; 12 | 13 | address constant UPGRADEABLE_CONTROLLER_PROXY = 0xa7d2607c6BD39Ae9521e514026CBB078405Ab322; 14 | address constant UPGRADEABLE_L2_RESOLVER_PROXY = 0x426fA03fB86E510d0Dd9F70335Cf102a98b10875; 15 | 16 | // ENS L2 Reverse Registrar (ENS-managed) on Base Mainnet 17 | address constant ENS_L2_REVERSE_REGISTRAR = 0x0000000000D8e504002cC26E3Ec46D81971C1664; 18 | 19 | // Ops / controllers 20 | address constant L2_OWNER = 0xf9BbA2F07a2c95fC4225f1CAeC76E6BF04B463e9; 21 | address constant MIGRATION_CONTROLLER = 0x8d5ef54f900c82da119B4a7F960A92F3Fa8daB43; 22 | 23 | // ENSIP-11 Base Mainnet cointype 24 | uint256 constant BASE_MAINNET_COINTYPE = 2147492101; // 0x80002105 25 | 26 | // ENSIP-19 Base Mainnet reverse parent node: namehash("80002105.reverse") 27 | bytes32 constant BASE_MAINNET_REVERSE_NODE = 0x08d9b0993eb8c4da57c37a4b84a6e384c2623114ff4e9370ed51c9b8935109ba; 28 | } 29 | -------------------------------------------------------------------------------- /test/Fork/BaseSepoliaConfig.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {AbstractForkSuite} from "./AbstractForkSuite.t.sol"; 5 | import {BaseSepolia as C} from "./BaseSepoliaConstants.sol"; 6 | 7 | abstract contract BaseSepoliaConfig is AbstractForkSuite { 8 | function forkAlias() internal pure override returns (string memory) { 9 | return "base-sepolia"; 10 | } 11 | 12 | function registry() internal pure override returns (address) { 13 | return C.REGISTRY; 14 | } 15 | 16 | function baseRegistrar() internal pure override returns (address) { 17 | return C.BASE_REGISTRAR; 18 | } 19 | 20 | function legacyGaController() internal pure override returns (address) { 21 | return C.LEGACY_GA_CONTROLLER; 22 | } 23 | 24 | function legacyL2Resolver() internal pure override returns (address) { 25 | return C.LEGACY_L2_RESOLVER; 26 | } 27 | 28 | function legacyReverseRegistrar() internal pure override returns (address) { 29 | return C.LEGACY_REVERSE_REGISTRAR; 30 | } 31 | 32 | function upgradeableControllerProxy() internal pure override returns (address) { 33 | return C.UPGRADEABLE_CONTROLLER_PROXY; 34 | } 35 | 36 | function upgradeableL2ResolverProxy() internal pure override returns (address) { 37 | return C.UPGRADEABLE_L2_RESOLVER_PROXY; 38 | } 39 | 40 | function ensL2ReverseRegistrar() internal pure override returns (address) { 41 | return C.ENS_L2_REVERSE_REGISTRAR; 42 | } 43 | 44 | function l2Owner() internal pure override returns (address) { 45 | return C.L2_OWNER; 46 | } 47 | 48 | function migrationController() internal pure override returns (address) { 49 | return C.MIGRATION_CONTROLLER; 50 | } 51 | 52 | function baseCoinType() internal pure override returns (uint256) { 53 | return C.BASE_SEPOLIA_COINTYPE; 54 | } 55 | 56 | function baseReverseParentNode() internal pure override returns (bytes32) { 57 | return C.BASE_SEPOLIA_REVERSE_NODE; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /test/Fork/BaseSepoliaConstants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | library BaseSepolia { 5 | // ENS / Basenames addresses on Base Sepolia 6 | address constant REGISTRY = 0x1493b2567056c2181630115660963E13A8E32735; 7 | address constant BASE_REGISTRAR = 0xA0c70ec36c010B55E3C434D6c6EbEEC50c705794; 8 | address constant LEGACY_GA_CONTROLLER = 0x49aE3cC2e3AA768B1e5654f5D3C6002144A59581; 9 | address constant LEGACY_L2_RESOLVER = 0x6533C94869D28fAA8dF77cc63f9e2b2D6Cf77eBA; 10 | // ReverseRegistrar with correct reverse node configured for Base Sepolia 11 | address constant LEGACY_REVERSE_REGISTRAR = 0x876eF94ce0773052a2f81921E70FF25a5e76841f; 12 | // Old reverse registrar with incorrect reverse node configured for Base Sepolia 13 | // address constant LEGACY_REVERSE_REGISTRAR = 0xa0A8401ECF248a9375a0a71C4dedc263dA18dCd7; 14 | 15 | address constant UPGRADEABLE_CONTROLLER_PROXY = 0x82c858CDF64b3D893Fe54962680edFDDC37e94C8; 16 | address constant UPGRADEABLE_L2_RESOLVER_PROXY = 0x85C87e548091f204C2d0350b39ce1874f02197c6; 17 | 18 | // ENS L2 Reverse Registrar (ENS-managed) on Base Sepolia 19 | address constant ENS_L2_REVERSE_REGISTRAR = 0x00000BeEF055f7934784D6d81b6BC86665630dbA; 20 | 21 | // Ops / controllers 22 | address constant L2_OWNER = 0xdEC57186e5dB11CcFbb4C932b8f11bD86171CB9D; 23 | address constant MIGRATION_CONTROLLER = 0xE8A87034a06425476F2bD6fD14EA038332Cc5e10; 24 | 25 | // ENSIP-11 Base Sepolia cointype 26 | uint256 constant BASE_SEPOLIA_COINTYPE = 2147568180; 27 | 28 | // ENSIP-19 Base Sepolia reverse parent node: namehash("80014a34.reverse") 29 | bytes32 constant BASE_SEPOLIA_REVERSE_NODE = 0x9831acb91a733dba6ffe6c6e872dd546b8c24e2dbd225f3616a8c670cbbd8b8a; 30 | } 31 | -------------------------------------------------------------------------------- /test/Fork/ENSIP19DataMigrationsMainnet.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BaseMainnetConfig} from "./BaseMainnetConfig.t.sol"; 5 | import {AbstractENSIP19DataMigrations} from "./AbstractENSIP19DataMigrations.t.sol"; 6 | 7 | contract ENSIP19DataMigrationsMainnet is BaseMainnetConfig, AbstractENSIP19DataMigrations {} 8 | -------------------------------------------------------------------------------- /test/Fork/ENSIP19DataMigrationsSepolia.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BaseSepoliaConfig} from "./BaseSepoliaConfig.t.sol"; 5 | import {AbstractENSIP19DataMigrations} from "./AbstractENSIP19DataMigrations.t.sol"; 6 | 7 | contract ENSIP19DataMigrationsSepolia is BaseSepoliaConfig, AbstractENSIP19DataMigrations {} 8 | -------------------------------------------------------------------------------- /test/Fork/ENSIP19LegacyFlowsMainnet.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BaseMainnetConfig} from "./BaseMainnetConfig.t.sol"; 5 | import {AbstractENSIP19LegacyFlows} from "./AbstractENSIP19LegacyFlows.t.sol"; 6 | 7 | contract ENSIP19LegacyFlowsMainnet is BaseMainnetConfig, AbstractENSIP19LegacyFlows {} 8 | -------------------------------------------------------------------------------- /test/Fork/ENSIP19LegacyFlowsSepolia.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BaseSepoliaConfig} from "./BaseSepoliaConfig.t.sol"; 5 | import {AbstractENSIP19LegacyFlows} from "./AbstractENSIP19LegacyFlows.t.sol"; 6 | 7 | contract ENSIP19LegacyFlowsSepolia is BaseSepoliaConfig, AbstractENSIP19LegacyFlows {} 8 | -------------------------------------------------------------------------------- /test/Fork/ENSIP19NewFlowsMainnet.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BaseMainnetConfig} from "./BaseMainnetConfig.t.sol"; 5 | import {AbstractENSIP19NewFlows} from "./AbstractENSIP19NewFlows.t.sol"; 6 | 7 | contract ENSIP19NewFlowsMainnet is BaseMainnetConfig, AbstractENSIP19NewFlows {} 8 | -------------------------------------------------------------------------------- /test/Fork/ENSIP19NewFlowsSepolia.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BaseSepoliaConfig} from "./BaseSepoliaConfig.t.sol"; 5 | import {AbstractENSIP19NewFlows} from "./AbstractENSIP19NewFlows.t.sol"; 6 | 7 | contract ENSIP19NewFlowsSepolia is BaseSepoliaConfig, AbstractENSIP19NewFlows {} 8 | -------------------------------------------------------------------------------- /test/Integration/ForwardResolutionMigration.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {IntegrationTestBase} from "./IntegrationTestBase.t.sol"; 5 | import {MigrationController} from "src/L2/MigrationController.sol"; 6 | 7 | contract ForwardResolutionMigration is IntegrationTestBase { 8 | MigrationController migrationController; 9 | uint256 BASE_COINTYPE = 0x80002105; 10 | bytes32 aliceNode; 11 | 12 | function setUp() public override { 13 | super.setUp(); 14 | migrationController = new MigrationController(registry, BASE_COINTYPE, address(defaultL2Resolver), owner); 15 | 16 | aliceNode = _registerAlice(); 17 | } 18 | 19 | function test_allowsTheOwnerToMigrateAUser() public { 20 | vm.startPrank(owner); 21 | defaultL2Resolver.setRegistrarController(address(migrationController)); 22 | 23 | bytes32[] memory nodes = new bytes32[](1); 24 | nodes[0] = aliceNode; 25 | 26 | migrationController.setBaseForwardAddr(nodes); 27 | 28 | bytes memory aliceAddr = defaultL2Resolver.addr(aliceNode, BASE_COINTYPE); 29 | 30 | assertEq(_bytesToAddress(aliceAddr), alice); 31 | } 32 | 33 | function _bytesToAddress(bytes memory b) internal pure returns (address payable a) { 34 | require(b.length == 20); 35 | assembly { 36 | a := div(mload(add(b, 32)), exp(256, 12)) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/L1Resolver/Fallback.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test, console} from "forge-std/Test.sol"; 5 | import {L1ResolverTestBase} from "./L1ResolverBase.t.sol"; 6 | 7 | import {L1Resolver} from "src/L1/L1Resolver.sol"; 8 | import {BASE_ETH_NODE} from "src/util/Constants.sol"; 9 | import {IAddrResolver} from "ens-contracts/resolvers/profiles/IAddrResolver.sol"; 10 | import {ITextResolver} from "ens-contracts/resolvers/profiles/ITextResolver.sol"; 11 | import {MockPublicResolver} from "test/mocks/MockPublicResolver.sol"; 12 | 13 | contract Fallback is L1ResolverTestBase { 14 | function test_forwardsAddrCall_whenResolvingRootName() public { 15 | bytes memory data = abi.encodeWithSelector(IAddrResolver.addr.selector, BASE_ETH_NODE); 16 | (, bytes memory response) = address(resolver).call{value: 0}(data); 17 | (address resolvedAddress) = abi.decode(response, (address)); 18 | assert(resolvedAddress == MockPublicResolver(rootResolver).ADDRESS()); 19 | } 20 | 21 | function test_forwardsTextCall_whenResolvingRootName() public { 22 | bytes memory data = abi.encodeWithSelector(ITextResolver.text.selector, BASE_ETH_NODE, "test"); 23 | (, bytes memory response) = address(resolver).call{value: 0}(data); 24 | (string memory resolvedText) = abi.decode(response, (string)); 25 | assert(keccak256(bytes(resolvedText)) == keccak256(bytes(MockPublicResolver(rootResolver).TEST_TEXT()))); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/L1Resolver/L1ResolverBase.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test, console} from "forge-std/Test.sol"; 5 | import {L1Resolver} from "src/L1/L1Resolver.sol"; 6 | import {MockPublicResolver} from "test/mocks/MockPublicResolver.sol"; 7 | 8 | contract L1ResolverTestBase is Test { 9 | L1Resolver public resolver; 10 | MockPublicResolver public rootResolver; 11 | string constant URL = "TEST_URL"; 12 | address signer; 13 | uint256 signerPk; 14 | address public owner = makeAddr("0x1"); 15 | 16 | function setUp() public { 17 | (signer, signerPk) = makeAddrAndKey("0xace"); 18 | address[] memory signers = new address[](1); 19 | signers[0] = signer; 20 | rootResolver = new MockPublicResolver(); 21 | resolver = new L1Resolver(URL, signers, owner, address(rootResolver)); 22 | } 23 | 24 | function test_constructor() public { 25 | (signer, signerPk) = makeAddrAndKey("0xace"); 26 | address[] memory signers_ = new address[](1); 27 | signers_[0] = signer; 28 | emit L1Resolver.AddedSigners(signers_); 29 | 30 | resolver = new L1Resolver(URL, signers_, owner, address(rootResolver)); 31 | assertTrue(keccak256(bytes(resolver.url())) == keccak256(bytes(URL))); 32 | assertTrue(resolver.signers(signer)); 33 | assertTrue(resolver.owner() == owner); 34 | assertTrue(resolver.rootResolver() == address(rootResolver)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/L1Resolver/L1ResolverMainnet.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test, console} from "forge-std/Test.sol"; 5 | import {L1Resolver} from "src/L1/L1Resolver.sol"; 6 | import {ENS} from "ens-contracts/registry/ENS.sol"; 7 | import {IExtendedResolver} from "ens-contracts/resolvers/profiles/IExtendedResolver.sol"; 8 | import "src/util/Constants.sol"; 9 | import "ens-contracts/resolvers/profiles/IAddrResolver.sol"; 10 | 11 | contract L1ResolverMainnet is Test { 12 | address signer = 0x14536667Cd30e52C0b458BaACcB9faDA7046E056; 13 | ENS public ens = ENS(0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e); 14 | address rootResolver = 0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41; 15 | address addrRoot; 16 | address l1resolver; 17 | 18 | string constant URL = "TEST_URL"; 19 | 20 | function setUp() public { 21 | uint256 forkId = vm.createFork("https://eth.llamarpc.com"); 22 | vm.selectFork(forkId); 23 | 24 | address[] memory signers = new address[](1); 25 | signers[0] = signer; 26 | l1resolver = address(new L1Resolver(URL, signers, signer, rootResolver)); 27 | 28 | vm.startPrank(signer); 29 | ens.setResolver(BASE_ETH_NODE, l1resolver); 30 | assertEq(ens.resolver(BASE_ETH_NODE), l1resolver); 31 | addrRoot = IAddrResolver(rootResolver).addr(BASE_ETH_NODE); 32 | } 33 | 34 | function test_resolves_addr() public view { 35 | address resolvedAddress = IAddrResolver(l1resolver).addr(BASE_ETH_NODE); 36 | assertEq(resolvedAddress, addrRoot); 37 | } 38 | 39 | function test_resolves_resolve() public view { 40 | bytes memory data = abi.encodeWithSelector(IAddrResolver.addr.selector, BASE_ETH_NODE); 41 | bytes memory response = IExtendedResolver(l1resolver).resolve(BASE_ETH_NAME, data); 42 | (address resolvedAddress) = abi.decode(response, (address)); 43 | assertEq(resolvedAddress, addrRoot); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/L1Resolver/MakeSignatureHash.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test, console} from "forge-std/Test.sol"; 5 | import {L1ResolverTestBase} from "./L1ResolverBase.t.sol"; 6 | 7 | import {L1Resolver} from "src/L1/L1Resolver.sol"; 8 | import {Ownable} from "solady/auth/Ownable.sol"; 9 | import {SignatureVerifier} from "src/lib/SignatureVerifier.sol"; 10 | 11 | contract MakeSignatureHash is L1ResolverTestBase { 12 | function test_makesValidSignatureHash(address target, uint64 expires, bytes memory request, bytes memory result) 13 | public 14 | view 15 | { 16 | bytes32 expectedHash = SignatureVerifier.makeSignatureHash(target, expires, request, result); 17 | bytes32 testHash = resolver.makeSignatureHash(target, expires, request, result); 18 | assertEq(expectedHash, testHash); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/L1Resolver/SupportsInterface.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test, console} from "forge-std/Test.sol"; 5 | import {L1ResolverTestBase} from "./L1ResolverBase.t.sol"; 6 | 7 | import {L1Resolver} from "src/L1/L1Resolver.sol"; 8 | import {Ownable} from "solady/auth/Ownable.sol"; 9 | import {IAddrResolver} from "ens-contracts/resolvers/profiles/IAddrResolver.sol"; 10 | import {ITextResolver} from "ens-contracts/resolvers/profiles/ITextResolver.sol"; 11 | import {IERC165} from "openzeppelin-contracts/contracts/interfaces/IERC165.sol"; 12 | 13 | contract SupportsInterface is L1ResolverTestBase { 14 | function test_supportsExtendedResolver() public view { 15 | assertTrue(resolver.supportsInterface(bytes4(0x9061b923))); // https://docs.ens.domains/ensip/10 16 | } 17 | 18 | function test_supportsERC165() public view { 19 | assertTrue(resolver.supportsInterface(type(IERC165).interfaceId)); 20 | } 21 | 22 | function test_supportsForwarding_toIAddrCompliantRootResolver() public view { 23 | assertTrue(resolver.supportsInterface(type(IAddrResolver).interfaceId)); 24 | } 25 | 26 | function test_supportsForwarding_toITextCompliantRootResolver() public view { 27 | assertTrue(resolver.supportsInterface(type(ITextResolver).interfaceId)); 28 | } 29 | 30 | function test_doesNotSupportArbitraryInterfaceId(bytes4 interfaceID) public view { 31 | vm.assume( 32 | interfaceID != bytes4(0x9061b923) && interfaceID != type(IERC165).interfaceId 33 | && interfaceID != type(IAddrResolver).interfaceId && interfaceID != type(ITextResolver).interfaceId 34 | ); 35 | assertFalse(resolver.supportsInterface(interfaceID)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/L2Resolver/Approve.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {L2ResolverBase} from "./L2ResolverBase.t.sol"; 5 | import {L2Resolver} from "src/L2/L2Resolver.sol"; 6 | 7 | contract Approve is L2ResolverBase { 8 | function test_revertsIfCalledForSelf() public { 9 | vm.expectRevert(L2Resolver.CantSetSelfAsDelegate.selector); 10 | vm.prank(user); 11 | resolver.approve(node, user, true); 12 | } 13 | 14 | function test_allowsSenderToSetDelegate(address operator) public { 15 | vm.assume(operator != user); 16 | vm.expectEmit(address(resolver)); 17 | emit L2Resolver.Approved(user, node, operator, true); 18 | vm.prank(user); 19 | resolver.approve(node, operator, true); 20 | assertTrue(resolver.isApprovedFor(user, node, operator)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/L2Resolver/SetApprovalForAll.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {L2ResolverBase} from "./L2ResolverBase.t.sol"; 5 | import {L2Resolver} from "src/L2/L2Resolver.sol"; 6 | 7 | contract SetApprovalForAll is L2ResolverBase { 8 | function test_revertsIfCalledForSelf() public { 9 | vm.expectRevert(L2Resolver.CantSetSelfAsOperator.selector); 10 | vm.prank(user); 11 | resolver.setApprovalForAll(user, true); 12 | } 13 | 14 | function test_allowsSenderToSetApproval(address operator) public { 15 | vm.assume(operator != user); 16 | vm.expectEmit(address(resolver)); 17 | emit L2Resolver.ApprovalForAll(user, operator, true); 18 | vm.prank(user); 19 | resolver.setApprovalForAll(operator, true); 20 | assertTrue(resolver.isApprovedForAll(user, operator)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/L2Resolver/SetRegistrarController.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {L2ResolverBase} from "./L2ResolverBase.t.sol"; 5 | import {L2Resolver} from "src/L2/L2Resolver.sol"; 6 | import {Ownable} from "solady/auth/Ownable.sol"; 7 | 8 | contract SetRegistrarController is L2ResolverBase { 9 | function test_reverts_ifCalledByNonOwner(address caller, address newController) public { 10 | vm.assume(caller != owner); 11 | vm.expectRevert(Ownable.Unauthorized.selector); 12 | vm.prank(caller); 13 | resolver.setRegistrarController(newController); 14 | } 15 | 16 | function test_setsTheRegistrarControllerAccordingly(address newController) public { 17 | vm.expectEmit(); 18 | emit L2Resolver.RegistrarControllerUpdated(newController); 19 | vm.prank(owner); 20 | resolver.setRegistrarController(newController); 21 | assertEq(resolver.registrarController(), newController); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/L2Resolver/SetReverseRegistrar.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {L2ResolverBase} from "./L2ResolverBase.t.sol"; 5 | import {L2Resolver} from "src/L2/L2Resolver.sol"; 6 | import {Ownable} from "solady/auth/Ownable.sol"; 7 | 8 | contract SetReverseRegistrar is L2ResolverBase { 9 | function test_reverts_ifCalledByNonOwner(address caller, address newReverse) public { 10 | vm.assume(caller != owner); 11 | vm.expectRevert(Ownable.Unauthorized.selector); 12 | vm.prank(caller); 13 | resolver.setReverseRegistrar(newReverse); 14 | } 15 | 16 | function test_setsTheReverseRegistrarAccordingly(address newReverse) public { 17 | vm.expectEmit(); 18 | emit L2Resolver.ReverseRegistrarUpdated(newReverse); 19 | vm.prank(owner); 20 | resolver.setReverseRegistrar(newReverse); 21 | assertEq(resolver.reverseRegistrar(), newReverse); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/LaunchAuctionPriceOracle/DecayedPremium.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ~0.8.17; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {LaunchAuctionPriceOracleBase} from "./LaunchAuctionPriceOracleBase.t.sol"; 6 | 7 | contract DecayedPremium is LaunchAuctionPriceOracleBase { 8 | function test_decayedPremium_zeroElapsed() public view { 9 | uint256 elapsed = 0; 10 | uint256 expectedPremium = startPremium; 11 | uint256 actualPremium = oracle.decayedPremium(elapsed); 12 | assertEq(actualPremium, expectedPremium); 13 | } 14 | 15 | function test_decayedPremium_auctionEnd() public view { 16 | uint256 auctionEndPremium = oracle.decayedPremium(_auctionDuration()); 17 | assertTrue(auctionEndPremium < oracle.endValue()); 18 | } 19 | 20 | function test_decayedPremium_halfPeriod() public view { 21 | uint256 elapsed = PRICE_PREMIUM_HALF_LIFE / 2; 22 | uint256 expectedPremium = _calculateDecayedPremium(elapsed); 23 | uint256 actualPremium = oracle.decayedPremium(elapsed); 24 | assertEq(actualPremium, expectedPremium); 25 | } 26 | 27 | function test_decayedPremium_threePeriods() public view { 28 | uint256 elapsed = 3 * PRICE_PREMIUM_HALF_LIFE; 29 | uint256 expectedPremium = _calculateDecayedPremium(elapsed); 30 | uint256 actualPremium = oracle.decayedPremium(elapsed); 31 | assertEq(actualPremium, expectedPremium); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/LaunchAuctionPriceOracle/ExponentialPremiumFuzzTest.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ~0.8.17; 3 | 4 | import {Test, console} from "forge-std/Test.sol"; 5 | import {LaunchAuctionPriceOracleBase} from "./LaunchAuctionPriceOracleBase.t.sol"; 6 | import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol"; 7 | 8 | contract ExponentialPremiumFuzzTest is LaunchAuctionPriceOracleBase { 9 | function test_decayedPremium_decreasingPrices(uint256 elapsed) public view { 10 | elapsed = bound(elapsed, 0, _auctionDuration()); 11 | uint256 actualPremium = oracle.decayedPremium(elapsed); 12 | assert(actualPremium <= startPremium); 13 | } 14 | 15 | function test_decayedPremium_alwaysDecreasing(uint256 elapsed1, uint256 elapsed2) public view { 16 | vm.assume(elapsed1 < elapsed2 && (elapsed1 < _auctionDuration())); 17 | elapsed1 = bound(elapsed1, 0, elapsed2); 18 | elapsed2 = bound(elapsed2, elapsed1, _auctionDuration()); 19 | 20 | uint256 premium1 = oracle.decayedPremium(elapsed1); 21 | uint256 premium2 = oracle.decayedPremium(elapsed2); 22 | 23 | assert(premium1 >= premium2); 24 | } 25 | 26 | function test_fuzzDecayedPremium_matchesExpectedValue(uint256 elapsed) public view { 27 | vm.assume(elapsed < ONE_HUNDRED_YEARS); 28 | uint256 returnedPremium = oracle.decayedPremium(elapsed); 29 | uint256 expectedPremium = _calculateDecayedPremium(elapsed); 30 | assertEq(returnedPremium, expectedPremium); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/MigrationController/SetBaseForwardAddr.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {MigrationControllerBase} from "./MigrationControllerBase.t.sol"; 5 | import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; 6 | 7 | contract SetBaseForwardAddr is MigrationControllerBase { 8 | function test_revertsWhen_calledByNonOwner(address caller) public { 9 | vm.assume(caller != owner); 10 | vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, caller)); 11 | vm.prank(caller); 12 | migrationController.setBaseForwardAddr(new bytes32[](1)); 13 | } 14 | 15 | function test_continuesWhenTheResolverIsNotTheDefaultResolver() public { 16 | _setupAliceNode(); 17 | vm.prank(alice); 18 | registry.setResolver(aliceNode, makeAddr("newResolver")); 19 | 20 | vm.prank(owner); 21 | migrationController.setBaseForwardAddr(_getNodesArray()); 22 | 23 | assertEq(resolver.addr(aliceNode, BASE_COINTYPE), ""); 24 | } 25 | 26 | function test_continuesWhenTheAddrRecordIsNotSet() public { 27 | _setupAliceNode(); 28 | 29 | vm.prank(owner); 30 | migrationController.setBaseForwardAddr(_getNodesArray()); 31 | 32 | assertEq(resolver.addr(aliceNode, BASE_COINTYPE), ""); 33 | } 34 | 35 | function test_continuesWhenAnEnsip11AddressIsAlreadySet() public { 36 | _setupAliceNode(); 37 | _createBaseAddrResolverRecord(); 38 | 39 | vm.prank(owner); 40 | migrationController.setBaseForwardAddr(_getNodesArray()); 41 | 42 | assertEq(bytesToAddress(resolver.addr(aliceNode, BASE_COINTYPE)), alice); 43 | } 44 | 45 | function test_setsTheEnsip11AddressCorrectly() public { 46 | _setupAliceNode(); 47 | _createAddrResolverRecord(); 48 | 49 | vm.prank(owner); 50 | migrationController.setBaseForwardAddr(_getNodesArray()); 51 | 52 | assertEq(bytesToAddress(resolver.addr(aliceNode, BASE_COINTYPE)), alice); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/RegistrarController/Available.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol"; 5 | 6 | contract Available is RegistrarControllerBase { 7 | function test_returnsFalse_whenNotAvailableOnBase() public { 8 | base.setAvailable(uint256(nameLabel), false); 9 | assertFalse(controller.available(name)); 10 | } 11 | 12 | function test_returnsFalse_whenInvalidLength() public { 13 | base.setAvailable(uint256(shortNameLabel), true); 14 | assertFalse(controller.available(shortName)); 15 | } 16 | 17 | function test_returnsTrue_whenValidAndAvailable() public { 18 | base.setAvailable(uint256(nameLabel), true); 19 | assertTrue(controller.available(name)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/RegistrarController/DiscountedRegisterPrice.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol"; 5 | import {RegistrarController} from "src/L2/RegistrarController.sol"; 6 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 7 | 8 | contract DiscountedRegisterPrice is RegistrarControllerBase { 9 | function test_returnsADiscountedPrice_whenThePriceIsGreaterThanTheDiscount(uint256 price) public { 10 | vm.assume(price > discountAmount); 11 | prices.setPrice(name, IPriceOracle.Price({base: price, premium: 0})); 12 | vm.prank(owner); 13 | controller.setDiscountDetails(_getDefaultDiscount()); 14 | 15 | uint256 expectedPrice = price - discountAmount; 16 | uint256 retPrice = controller.discountedRegisterPrice(name, duration, discountKey); 17 | assertEq(retPrice, expectedPrice); 18 | } 19 | 20 | function test_returnsZero_whenThePriceIsLessThanOrEqualToTheDiscount(uint256 price) public { 21 | vm.assume(price <= discountAmount); 22 | prices.setPrice(name, IPriceOracle.Price({base: price, premium: 0})); 23 | vm.prank(owner); 24 | controller.setDiscountDetails(_getDefaultDiscount()); 25 | 26 | uint256 retPrice = controller.discountedRegisterPrice(name, duration, discountKey); 27 | assertEq(retPrice, 0); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/RegistrarController/RecoverFunds.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol"; 5 | import {RegistrarController} from "src/L2/RegistrarController.sol"; 6 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 7 | import {Ownable} from "solady/auth/Ownable.sol"; 8 | import {MockUSDC} from "test/mocks/MockUSDC.sol"; 9 | 10 | contract RecoverFunds is RegistrarControllerBase { 11 | MockUSDC public usdc; 12 | 13 | function test_reverts_ifCalledByNonOwner(address caller, uint256 amount) public { 14 | vm.assume(caller != owner); 15 | vm.assume(amount > 0 && amount < type(uint128).max); 16 | vm.expectRevert(Ownable.Unauthorized.selector); 17 | vm.prank(caller); 18 | controller.recoverFunds(address(usdc), caller, amount); 19 | } 20 | 21 | function test_allowsTheOwnerToRecoverFunds(uint256 amount) public { 22 | vm.assume(amount > 0 && amount < type(uint128).max); 23 | _setupTokenAndAssignBalanceToController(amount); 24 | assertEq(usdc.balanceOf(owner), 0); 25 | 26 | vm.prank(owner); 27 | controller.recoverFunds(address(usdc), owner, amount); 28 | assertEq(usdc.balanceOf(owner), amount); 29 | } 30 | 31 | function _setupTokenAndAssignBalanceToController(uint256 balance) internal { 32 | usdc = new MockUSDC(); 33 | usdc.mint(address(controller), balance); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/RegistrarController/RegisterPrice.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol"; 5 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 6 | 7 | contract RegisterPrice is RegistrarControllerBase { 8 | function test_returnsRegisterPrice_fromPricingOracle() public view { 9 | uint256 retPrice = controller.registerPrice(name, 0); 10 | assertEq(retPrice, prices.DEFAULT_BASE_WEI() + prices.DEFAULT_PREMIUM_WEI()); 11 | } 12 | 13 | function test_fuzz_returnsRegisterPrice_fromPricingOracle(uint256 fuzzBase, uint256 fuzzPremium) public { 14 | vm.assume(fuzzBase != 0 && fuzzBase < type(uint128).max); 15 | vm.assume(fuzzPremium < type(uint128).max); 16 | IPriceOracle.Price memory expectedPrice = IPriceOracle.Price({base: fuzzBase, premium: fuzzPremium}); 17 | prices.setPrice(name, expectedPrice); 18 | uint256 retPrice = controller.registerPrice(name, 0); 19 | assertEq(retPrice, expectedPrice.base + expectedPrice.premium); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/RegistrarController/Renew.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol"; 5 | import {RegistrarController} from "src/L2/RegistrarController.sol"; 6 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 7 | 8 | contract Renew is RegistrarControllerBase { 9 | function test_allowsAUserToRenewTheirName() public { 10 | vm.deal(user, 1 ether); 11 | (uint256 expires,) = _register(); 12 | IPriceOracle.Price memory price = controller.rentPrice(name, duration); 13 | uint256 newExpiry = expires + duration; 14 | 15 | vm.expectEmit(address(controller)); 16 | emit RegistrarController.ETHPaymentProcessed(user, price.base); 17 | vm.expectEmit(address(controller)); 18 | emit RegistrarController.NameRenewed(name, nameLabel, newExpiry); 19 | 20 | vm.prank(user); 21 | controller.renew{value: price.base}(name, duration); 22 | } 23 | 24 | function test_refundsExcessETH_onOverpaidRenewal() public { 25 | vm.deal(user, 1 ether); 26 | (, uint256 registerPrice) = _register(); 27 | IPriceOracle.Price memory price = controller.rentPrice(name, duration); 28 | 29 | vm.prank(user); 30 | controller.renew{value: (price.base + 1)}(name, duration); 31 | 32 | uint256 expectedBalance = 1 ether - registerPrice - price.base; 33 | assertEq(user.balance, expectedBalance); 34 | } 35 | 36 | function _register() internal returns (uint256, uint256) { 37 | RegistrarController.RegisterRequest memory request = _getDefaultRegisterRequest(); 38 | uint256 price = controller.registerPrice(request.name, request.duration); 39 | base.setAvailable(uint256(nameLabel), true); 40 | uint256 expires = block.timestamp + request.duration; 41 | base.setNameExpires(uint256(nameLabel), expires); 42 | vm.prank(user); 43 | controller.register{value: price}(request); 44 | return (expires, price); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/RegistrarController/RentPrice.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol"; 5 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 6 | 7 | contract RentPrice is RegistrarControllerBase { 8 | function test_returnsPrice_fromPricingOracle() public view { 9 | IPriceOracle.Price memory retPrices = controller.rentPrice(name, 0); 10 | assertEq(retPrices.base, prices.DEFAULT_BASE_WEI()); 11 | assertEq(retPrices.premium, prices.DEFAULT_PREMIUM_WEI()); 12 | } 13 | 14 | function test_returnsPremium_ifTimeIsNearLaunchTime() public { 15 | vm.prank(owner); 16 | controller.setLaunchTime(launchTime); 17 | 18 | vm.warp(launchTime + 1); 19 | IPriceOracle.Price memory retPrices = controller.rentPrice(name, 0); 20 | assertEq(retPrices.base, prices.DEFAULT_BASE_WEI()); 21 | assertEq(retPrices.premium, prices.DEFAULT_INCLUDED_PREMIUM()); 22 | } 23 | 24 | function test_fuzz_returnsPrice_fromPricingOracle(uint256 fuzzBase, uint256 fuzzPremium) public { 25 | vm.assume(fuzzBase != 0 && fuzzBase < type(uint128).max); 26 | vm.assume(fuzzPremium < type(uint128).max); 27 | IPriceOracle.Price memory expectedPrice = IPriceOracle.Price({base: fuzzBase, premium: fuzzPremium}); 28 | prices.setPrice(name, expectedPrice); 29 | IPriceOracle.Price memory retPrices = controller.rentPrice(name, 0); 30 | assertEq(retPrices.base, expectedPrice.base); 31 | assertEq(retPrices.premium, expectedPrice.premium); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/RegistrarController/SetLaunchTime.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol"; 5 | import {Ownable} from "solady/auth/Ownable.sol"; 6 | 7 | contract SetLaunchTime is RegistrarControllerBase { 8 | function test_reverts_ifCalledByNonOwner(address caller) public { 9 | vm.assume(caller != owner); 10 | vm.expectRevert(Ownable.Unauthorized.selector); 11 | vm.prank(caller); 12 | controller.setLaunchTime(launchTime); 13 | } 14 | 15 | function test_allowsTheOwnerToSetTheLaunchTime() public { 16 | uint256 before_launchTime = controller.launchTime(); 17 | assertEq(before_launchTime, 0); 18 | 19 | vm.prank(owner); 20 | controller.setLaunchTime(launchTime); 21 | 22 | uint256 after_launchTime = controller.launchTime(); 23 | assertEq(after_launchTime, launchTime); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/RegistrarController/SetPaymentReceiver.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol"; 5 | import {RegistrarController} from "src/L2/RegistrarController.sol"; 6 | import {Ownable} from "solady/auth/Ownable.sol"; 7 | 8 | contract SetPaymentReceiver is RegistrarControllerBase { 9 | function test_reverts_ifCalledByNonOwner(address caller) public { 10 | vm.assume(caller != owner && caller != address(0)); 11 | vm.expectRevert(Ownable.Unauthorized.selector); 12 | vm.prank(caller); 13 | controller.setPaymentReceiver(caller); 14 | } 15 | 16 | function test_reverts_ifNewPaymentReceiver_isZeroAddress() public { 17 | vm.expectRevert(RegistrarController.InvalidPaymentReceiver.selector); 18 | vm.prank(owner); 19 | controller.setPaymentReceiver(address(0)); 20 | } 21 | 22 | function test_allowsTheOwner_toSetThePaymentReceiver(address newReceiver) public { 23 | vm.assume(newReceiver != address(0)); 24 | vm.expectEmit(address(controller)); 25 | emit RegistrarController.PaymentReceiverUpdated(newReceiver); 26 | vm.prank(owner); 27 | controller.setPaymentReceiver(newReceiver); 28 | assertEq(newReceiver, controller.paymentReceiver()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/RegistrarController/SetPriceOracle.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol"; 5 | import {RegistrarController} from "src/L2/RegistrarController.sol"; 6 | import {MockPriceOracle} from "test/mocks/MockPriceOracle.sol"; 7 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 8 | import {Ownable} from "solady/auth/Ownable.sol"; 9 | 10 | contract SetPriceOracle is RegistrarControllerBase { 11 | function test_reverts_ifCalledByNonOwner(address caller) public { 12 | vm.assume(caller != owner); 13 | MockPriceOracle newPrices = new MockPriceOracle(); 14 | vm.expectRevert(Ownable.Unauthorized.selector); 15 | vm.prank(caller); 16 | controller.setPriceOracle(IPriceOracle(address(newPrices))); 17 | } 18 | 19 | function test_setsThePriceOracleAccordingly() public { 20 | vm.expectEmit(); 21 | MockPriceOracle newPrices = new MockPriceOracle(); 22 | emit RegistrarController.PriceOracleUpdated(address(newPrices)); 23 | vm.prank(owner); 24 | controller.setPriceOracle(IPriceOracle(address(newPrices))); 25 | assertEq(address(controller.prices()), address(newPrices)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/RegistrarController/SetReverseRegistrar.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol"; 5 | import {RegistrarController} from "src/L2/RegistrarController.sol"; 6 | import {MockReverseRegistrar} from "test/mocks/MockReverseRegistrar.sol"; 7 | import {IReverseRegistrar} from "src/L2/interface/IReverseRegistrar.sol"; 8 | import {Ownable} from "solady/auth/Ownable.sol"; 9 | 10 | contract SetReverseRegistrar is RegistrarControllerBase { 11 | function test_reverts_ifCalledByNonOwner(address caller) public { 12 | vm.assume(caller != owner); 13 | MockReverseRegistrar newReverse = new MockReverseRegistrar(); 14 | vm.expectRevert(Ownable.Unauthorized.selector); 15 | vm.prank(caller); 16 | controller.setReverseRegistrar(IReverseRegistrar(address(newReverse))); 17 | } 18 | 19 | function test_setsTheReverseRegistrarAccordingly() public { 20 | vm.expectEmit(); 21 | MockReverseRegistrar newReverse = new MockReverseRegistrar(); 22 | emit RegistrarController.ReverseRegistrarUpdated(address(newReverse)); 23 | vm.prank(owner); 24 | controller.setReverseRegistrar(IReverseRegistrar(address(newReverse))); 25 | assertEq(address(controller.reverseRegistrar()), address(newReverse)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/RegistrarController/Valid.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol"; 5 | 6 | contract Valid is RegistrarControllerBase { 7 | function test_returnsTrue_whenValid() public view { 8 | assertTrue(controller.valid("abc")); 9 | assertTrue(controller.valid("abcdef")); 10 | assertTrue(controller.valid("abcdefghijklmnop")); 11 | } 12 | 13 | function test_returnsFalse_whenInvalid() public view { 14 | assertFalse(controller.valid("")); 15 | assertFalse(controller.valid("a")); 16 | assertFalse(controller.valid("ab")); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/RegistrarController/WithdrawETH.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {RegistrarControllerBase} from "./RegistrarControllerBase.t.sol"; 5 | import {RegistrarController} from "src/L2/RegistrarController.sol"; 6 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 7 | 8 | contract WithdrawETH is RegistrarControllerBase { 9 | function test_alwaysSendsTheBalanceToTheOwner(address caller) public { 10 | vm.deal(address(controller), 1 ether); 11 | assertEq(payments.balance, 0); 12 | vm.prank(caller); 13 | controller.withdrawETH(); 14 | assertEq(payments.balance, 1 ether); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/Registry/RecordExists.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Test.sol"; 5 | import {Registry} from "src/L2/Registry.sol"; 6 | import {ENS} from "ens-contracts/registry/ENS.sol"; 7 | import {ETH_NODE, BASE_ETH_NODE} from "src/util/Constants.sol"; 8 | import {NameEncoder} from "ens-contracts/utils/NameEncoder.sol"; 9 | import {RegistryBase} from "./RegistryBase.t.sol"; 10 | 11 | contract RecordExists is RegistryBase { 12 | function test_correctlyConfirmsARecordExists() public view { 13 | assertTrue(registry.recordExists(BASE_ETH_NODE)); 14 | } 15 | 16 | function text_correctlyConfirmsARecordDoesNotExist(bytes32 node) public view { 17 | vm.assume(node != BASE_ETH_NODE && node != ETH_NODE && node != bytes32(0)); 18 | assertFalse(registry.recordExists(node)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/Registry/RegistryBase.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {Registry} from "src/L2/Registry.sol"; 6 | import {ETH_NODE} from "src/util/Constants.sol"; 7 | 8 | import {MockPublicResolver} from "../mocks/MockPublicResolver.sol"; 9 | 10 | contract RegistryBase is Test { 11 | uint64 TTL = type(uint64).max; 12 | Registry public registry; 13 | MockPublicResolver public resolver; 14 | 15 | address public rootOwner = makeAddr("0x1"); 16 | address public ethOwner = makeAddr("eth"); 17 | address public baseEthOwner = makeAddr("base"); 18 | address public nodeOwner = makeAddr("test"); 19 | 20 | function setUp() public { 21 | registry = new Registry(rootOwner); 22 | resolver = new MockPublicResolver(); 23 | registry.owner(0x0); 24 | _ownershipSetup(); 25 | } 26 | 27 | function test_constructor_setsTheRootNodeOwner() public view { 28 | assertTrue(registry.owner(bytes32(0)) == rootOwner); 29 | } 30 | 31 | function _ownershipSetup() internal virtual { 32 | // establish the base.eth namespace 33 | bytes32 ethLabel = keccak256("eth"); 34 | bytes32 baseLabel = keccak256("base"); 35 | vm.prank(rootOwner); 36 | registry.setSubnodeOwner(0x0, ethLabel, ethOwner); 37 | vm.prank(ethOwner); 38 | registry.setSubnodeOwner(ETH_NODE, baseLabel, baseEthOwner); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/Registry/SetApprovalForAll.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Test.sol"; 5 | import {Registry} from "src/L2/Registry.sol"; 6 | import {ENS} from "ens-contracts/registry/ENS.sol"; 7 | import {ETH_NODE, BASE_ETH_NODE} from "src/util/Constants.sol"; 8 | import {NameEncoder} from "ens-contracts/utils/NameEncoder.sol"; 9 | import {RegistryBase} from "./RegistryBase.t.sol"; 10 | 11 | contract SetResolver is RegistryBase { 12 | function test_setsApprovalCorrectly() public { 13 | vm.expectEmit(); 14 | emit ENS.ApprovalForAll(ethOwner, nodeOwner, true); 15 | vm.prank(ethOwner); 16 | registry.setApprovalForAll(nodeOwner, true); 17 | 18 | vm.prank(nodeOwner); 19 | address newResolver = makeAddr("resolver"); 20 | registry.setResolver(ETH_NODE, newResolver); 21 | address storedResolver = registry.resolver(ETH_NODE); 22 | assertTrue(storedResolver == newResolver); 23 | assertTrue(registry.isApprovedForAll(ethOwner, nodeOwner)); 24 | } 25 | 26 | function test_revokesApprovalCorrectly() public { 27 | vm.prank(ethOwner); 28 | registry.setApprovalForAll(nodeOwner, true); 29 | assertTrue(registry.isApprovedForAll(ethOwner, nodeOwner)); 30 | vm.prank(nodeOwner); 31 | address newResolver = makeAddr("resolver"); 32 | registry.setResolver(ETH_NODE, newResolver); 33 | address storedResolver = registry.resolver(ETH_NODE); 34 | assertTrue(storedResolver == newResolver); 35 | 36 | vm.expectEmit(); 37 | emit ENS.ApprovalForAll(ethOwner, nodeOwner, false); 38 | vm.prank(ethOwner); 39 | registry.setApprovalForAll(nodeOwner, false); 40 | assertFalse(registry.isApprovedForAll(ethOwner, nodeOwner)); 41 | 42 | vm.prank(nodeOwner); 43 | vm.expectRevert(Registry.Unauthorized.selector); 44 | address newerResolver = makeAddr("resolver2"); 45 | registry.setResolver(ETH_NODE, newerResolver); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/Registry/SetOwner.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Test.sol"; 5 | import {Registry} from "src/L2/Registry.sol"; 6 | import {ENS} from "ens-contracts/registry/ENS.sol"; 7 | import {ETH_NODE, BASE_ETH_NODE} from "src/util/Constants.sol"; 8 | import {NameEncoder} from "ens-contracts/utils/NameEncoder.sol"; 9 | import {RegistryBase} from "./RegistryBase.t.sol"; 10 | 11 | contract SetOwner is RegistryBase { 12 | function test_setsOwnerCorrectly() public { 13 | vm.expectEmit(); 14 | emit ENS.Transfer(ETH_NODE, nodeOwner); 15 | vm.prank(ethOwner); 16 | registry.setOwner(ETH_NODE, nodeOwner); 17 | 18 | address storedOwner = registry.owner(ETH_NODE); 19 | assertTrue(storedOwner == nodeOwner); 20 | } 21 | 22 | function test_reverts_whenTheCallerIsNotAuthroized(address caller) public { 23 | vm.assume(caller != ethOwner); 24 | vm.expectRevert(Registry.Unauthorized.selector); 25 | vm.prank(caller); 26 | registry.setOwner(ETH_NODE, nodeOwner); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/Registry/SetRecord.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Test.sol"; 5 | import {Registry} from "src/L2/Registry.sol"; 6 | import {ENS} from "ens-contracts/registry/ENS.sol"; 7 | import {ETH_NODE, BASE_ETH_NODE} from "src/util/Constants.sol"; 8 | import {NameEncoder} from "ens-contracts/utils/NameEncoder.sol"; 9 | import {RegistryBase} from "./RegistryBase.t.sol"; 10 | 11 | contract SetRecord is RegistryBase { 12 | function test_setsTheRecordCorrectly() public { 13 | vm.expectEmit(); 14 | emit ENS.Transfer(ETH_NODE, nodeOwner); 15 | vm.expectEmit(); 16 | emit ENS.NewResolver(ETH_NODE, address(resolver)); 17 | vm.expectEmit(); 18 | emit ENS.NewTTL(ETH_NODE, TTL); 19 | vm.prank(ethOwner); 20 | registry.setRecord(ETH_NODE, nodeOwner, address(resolver), TTL); 21 | 22 | address storedOwner = registry.owner(ETH_NODE); 23 | address storedResolver = registry.resolver(ETH_NODE); 24 | uint64 storedTtl = registry.ttl(ETH_NODE); 25 | assertTrue(storedOwner == nodeOwner); 26 | assertTrue(storedResolver == address(resolver)); 27 | assertTrue(storedTtl == TTL); 28 | } 29 | 30 | function test_reverts_whenTheCallerIsNotAuthroized(address caller) public { 31 | vm.assume(caller != ethOwner); 32 | vm.expectRevert(Registry.Unauthorized.selector); 33 | vm.prank(caller); 34 | registry.setRecord(ETH_NODE, nodeOwner, address(resolver), TTL); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/Registry/SetResolver.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Test.sol"; 5 | import {Registry} from "src/L2/Registry.sol"; 6 | import {ENS} from "ens-contracts/registry/ENS.sol"; 7 | import {ETH_NODE, BASE_ETH_NODE} from "src/util/Constants.sol"; 8 | import {NameEncoder} from "ens-contracts/utils/NameEncoder.sol"; 9 | import {RegistryBase} from "./RegistryBase.t.sol"; 10 | 11 | contract SetResolver is RegistryBase { 12 | function test_setsTheResolverCorrectly() public { 13 | vm.expectEmit(); 14 | emit ENS.NewResolver(ETH_NODE, address(resolver)); 15 | vm.prank(ethOwner); 16 | registry.setResolver(ETH_NODE, address(resolver)); 17 | 18 | address storedResolver = registry.resolver(ETH_NODE); 19 | assertTrue(storedResolver == address(resolver)); 20 | } 21 | 22 | function test_reverts_whenTheCallerIsNotAuthroized(address caller) public { 23 | vm.assume(caller != ethOwner); 24 | vm.expectRevert(Registry.Unauthorized.selector); 25 | vm.prank(caller); 26 | registry.setResolver(ETH_NODE, address(resolver)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/Registry/SetSubnodeOwner.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Test.sol"; 5 | import {Registry} from "src/L2/Registry.sol"; 6 | import {ENS} from "ens-contracts/registry/ENS.sol"; 7 | import {ETH_NODE, BASE_ETH_NODE} from "src/util/Constants.sol"; 8 | import {NameEncoder} from "ens-contracts/utils/NameEncoder.sol"; 9 | import {RegistryBase} from "./RegistryBase.t.sol"; 10 | 11 | contract SetSubnodeOwner is RegistryBase { 12 | bytes32 label = keccak256("test"); 13 | 14 | function test_setsSubnodeOwnerCorrectly() public { 15 | bytes32 node = keccak256(abi.encodePacked(ETH_NODE, label)); // test.eth 16 | vm.expectEmit(); 17 | emit ENS.NewOwner(ETH_NODE, label, nodeOwner); 18 | vm.prank(ethOwner); 19 | registry.setSubnodeOwner(ETH_NODE, label, nodeOwner); 20 | 21 | address storedOwner = registry.owner(node); 22 | assertTrue(storedOwner == nodeOwner); 23 | } 24 | 25 | function test_reverts_whenTheCallerIsNotAuthroized(address caller) public { 26 | vm.assume(caller != ethOwner); 27 | vm.expectRevert(Registry.Unauthorized.selector); 28 | vm.prank(caller); 29 | registry.setSubnodeOwner(ETH_NODE, label, nodeOwner); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/Registry/SetSubnodeRecord.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Test.sol"; 5 | import {Registry} from "src/L2/Registry.sol"; 6 | import {ENS} from "ens-contracts/registry/ENS.sol"; 7 | import {ETH_NODE, BASE_ETH_NODE} from "src/util/Constants.sol"; 8 | import {NameEncoder} from "ens-contracts/utils/NameEncoder.sol"; 9 | import {RegistryBase} from "./RegistryBase.t.sol"; 10 | 11 | contract SetSubnodeRecord is RegistryBase { 12 | bytes32 label = keccak256("test"); 13 | 14 | function test_setsTheSubnodeRecordCorrectly() public { 15 | bytes32 node = keccak256(abi.encodePacked(ETH_NODE, label)); // test.eth 16 | vm.expectEmit(); 17 | emit ENS.NewOwner(ETH_NODE, label, nodeOwner); 18 | vm.expectEmit(); 19 | emit ENS.NewResolver(node, address(resolver)); 20 | vm.expectEmit(); 21 | emit ENS.NewTTL(node, TTL); 22 | vm.prank(ethOwner); 23 | registry.setSubnodeRecord(ETH_NODE, label, nodeOwner, address(resolver), TTL); 24 | 25 | address storedOwner = registry.owner(node); 26 | address storedResolver = registry.resolver(node); 27 | uint64 storedTtl = registry.ttl(node); 28 | assertTrue(storedOwner == nodeOwner); 29 | assertTrue(storedResolver == address(resolver)); 30 | assertTrue(storedTtl == TTL); 31 | } 32 | 33 | function test_reverts_whenTheCallerIsNotAuthroized(address caller) public { 34 | vm.assume(caller != ethOwner); 35 | vm.expectRevert(Registry.Unauthorized.selector); 36 | vm.prank(caller); 37 | registry.setSubnodeRecord(ETH_NODE, label, nodeOwner, address(resolver), TTL); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/Registry/SetTTL.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import "forge-std/Test.sol"; 5 | import {Registry} from "src/L2/Registry.sol"; 6 | import {ENS} from "ens-contracts/registry/ENS.sol"; 7 | import {ETH_NODE, BASE_ETH_NODE} from "src/util/Constants.sol"; 8 | import {NameEncoder} from "ens-contracts/utils/NameEncoder.sol"; 9 | import {RegistryBase} from "./RegistryBase.t.sol"; 10 | 11 | contract SetTTL is RegistryBase { 12 | function test_setsTheTTLCorrectly() public { 13 | vm.expectEmit(); 14 | emit ENS.NewTTL(ETH_NODE, TTL); 15 | vm.prank(ethOwner); 16 | registry.setTTL(ETH_NODE, TTL); 17 | 18 | uint64 storedTtl = registry.ttl(ETH_NODE); 19 | assertTrue(storedTtl == TTL); 20 | } 21 | 22 | function test_reverts_whenTheCallerIsNotAuthroized(address caller) public { 23 | vm.assume(caller != ethOwner); 24 | vm.expectRevert(Registry.Unauthorized.selector); 25 | vm.prank(caller); 26 | registry.setResolver(ETH_NODE, address(resolver)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/ReverseRegistrar/Claim.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ReverseRegistrarBase} from "./ReverseRegistrarBase.t.sol"; 5 | import {ReverseRegistrar} from "src/L2/ReverseRegistrar.sol"; 6 | import {Sha3} from "src/lib/Sha3.sol"; 7 | import {BASE_REVERSE_NODE} from "src/util/Constants.sol"; 8 | 9 | contract Claim is ReverseRegistrarBase { 10 | address resolver = makeAddr("resolver"); 11 | 12 | function test_allowsUser_toClaim() public { 13 | bytes32 labelHash = Sha3.hexAddress(user); 14 | bytes32 baseReverseNode = keccak256(abi.encodePacked(BASE_REVERSE_NODE, labelHash)); 15 | 16 | vm.prank(owner); 17 | reverse.setDefaultResolver(resolver); 18 | 19 | vm.expectEmit(address(reverse)); 20 | emit ReverseRegistrar.BaseReverseClaimed(user, baseReverseNode); 21 | 22 | vm.prank(user); 23 | bytes32 returnedReverseNode = reverse.claim(user); 24 | 25 | assertTrue(baseReverseNode == returnedReverseNode); 26 | address retBaseOwner = registry.owner(baseReverseNode); 27 | assertTrue(retBaseOwner == user); 28 | address retBaseResolver = registry.resolver(baseReverseNode); 29 | assertTrue(retBaseResolver == address(resolver)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/ReverseRegistrar/ClaimWithResolver.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ReverseRegistrarBase} from "./ReverseRegistrarBase.t.sol"; 5 | import {ReverseRegistrar} from "src/L2/ReverseRegistrar.sol"; 6 | import {Sha3} from "src/lib/Sha3.sol"; 7 | import {BASE_REVERSE_NODE} from "src/util/Constants.sol"; 8 | 9 | contract ClaimWithResolver is ReverseRegistrarBase { 10 | address resolver = makeAddr("resolver"); 11 | 12 | function test_allowsUser_toClaimWithResolver() public { 13 | bytes32 labelHash = Sha3.hexAddress(user); 14 | bytes32 reverseNode = keccak256(abi.encodePacked(BASE_REVERSE_NODE, labelHash)); 15 | 16 | vm.expectEmit(address(reverse)); 17 | emit ReverseRegistrar.BaseReverseClaimed(user, reverseNode); 18 | vm.prank(user); 19 | bytes32 returnedReverseNode = reverse.claimWithResolver(user, resolver); 20 | assertTrue(reverseNode == returnedReverseNode); 21 | address retOwner = registry.owner(reverseNode); 22 | assertTrue(retOwner == user); 23 | address retResolver = registry.resolver(reverseNode); 24 | assertTrue(retResolver == resolver); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/ReverseRegistrar/Node.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ReverseRegistrarBase} from "./ReverseRegistrarBase.t.sol"; 5 | import {Sha3} from "src/lib/Sha3.sol"; 6 | import {BASE_REVERSE_NODE} from "src/util/Constants.sol"; 7 | 8 | contract Node is ReverseRegistrarBase { 9 | function test_returnsExpectedNode(address addr) public view { 10 | bytes32 labelHash = Sha3.hexAddress(addr); 11 | bytes32 expectedNode = keccak256(abi.encodePacked(BASE_REVERSE_NODE, labelHash)); 12 | bytes32 retNode = reverse.node(addr); 13 | assertTrue(retNode == expectedNode); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/ReverseRegistrar/ReverseRegistrarBase.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {ReverseRegistrar} from "src/L2/ReverseRegistrar.sol"; 6 | import {Registry} from "src/L2/Registry.sol"; 7 | import {ENS} from "ens-contracts/registry/ENS.sol"; 8 | import {ETH_NODE, REVERSE_NODE, BASE_REVERSE_NODE} from "src/util/Constants.sol"; 9 | 10 | contract ReverseRegistrarBase is Test { 11 | address public owner = makeAddr("owner"); 12 | address public user = makeAddr("user"); 13 | address public controller = makeAddr("controller"); 14 | 15 | Registry public registry; 16 | ReverseRegistrar public reverse; 17 | 18 | function setUp() public { 19 | registry = new Registry(owner); 20 | reverse = new ReverseRegistrar(ENS(address(registry)), owner, BASE_REVERSE_NODE); 21 | vm.prank(owner); 22 | reverse.setControllerApproval(controller, true); 23 | _registrySetup(); 24 | } 25 | 26 | function _registrySetup() internal virtual { 27 | // establish the base.eth namespace 28 | bytes32 ethLabel = keccak256("eth"); 29 | bytes32 baseLabel = keccak256("base"); 30 | vm.prank(owner); 31 | registry.setSubnodeOwner(0x0, ethLabel, owner); 32 | vm.prank(owner); 33 | registry.setSubnodeOwner(ETH_NODE, baseLabel, owner); 34 | 35 | // establish the 80002105.reverse namespace 36 | vm.prank(owner); 37 | registry.setSubnodeOwner(0x0, keccak256("reverse"), owner); 38 | vm.prank(owner); 39 | registry.setSubnodeOwner(REVERSE_NODE, keccak256("80002105"), address(reverse)); 40 | } 41 | 42 | function test_constructor() public view { 43 | assertTrue(reverse.owner() == owner); 44 | assertTrue(address(reverse.registry()) == address(registry)); 45 | assertTrue(reverse.controllers(controller)); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/ReverseRegistrar/SetControllerApproval.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ReverseRegistrarBase} from "./ReverseRegistrarBase.t.sol"; 5 | import {ReverseRegistrar} from "src/L2/ReverseRegistrar.sol"; 6 | import {Ownable} from "solady/auth/Ownable.sol"; 7 | 8 | contract SetControllerApproval is ReverseRegistrarBase { 9 | function test_reverts_ifCalledByNonOwner(address caller) public { 10 | vm.assume(caller != owner && caller != address(0)); 11 | vm.expectRevert(Ownable.Unauthorized.selector); 12 | reverse.setControllerApproval(caller, true); 13 | } 14 | 15 | function test_allowsTheOwner_toUpdateControllerApproval(address newController) public { 16 | vm.assume(newController != address(0)); 17 | 18 | vm.expectEmit(address(reverse)); 19 | emit ReverseRegistrar.ControllerApprovalChanged(newController, true); 20 | vm.prank(owner); 21 | reverse.setControllerApproval(newController, true); 22 | assertTrue(reverse.controllers(newController)); 23 | 24 | vm.expectEmit(address(reverse)); 25 | emit ReverseRegistrar.ControllerApprovalChanged(newController, false); 26 | vm.prank(owner); 27 | reverse.setControllerApproval(newController, false); 28 | assertFalse(reverse.controllers(newController)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/ReverseRegistrar/SetDefaultResolver.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {NameResolver} from "ens-contracts/resolvers/profiles/NameResolver.sol"; 5 | import {Ownable} from "solady/auth/Ownable.sol"; 6 | import {ReverseRegistrarBase} from "./ReverseRegistrarBase.t.sol"; 7 | import {ReverseRegistrar} from "src/L2/ReverseRegistrar.sol"; 8 | 9 | contract SetDefaultResolver is ReverseRegistrarBase { 10 | function test_reverts_whenCalledByNonOwner(address caller) public { 11 | vm.assume(caller != owner); 12 | vm.expectRevert(Ownable.Unauthorized.selector); 13 | vm.prank(caller); 14 | reverse.setDefaultResolver(makeAddr("fake")); 15 | } 16 | 17 | function test_reverts_whenPassedZeroAddress() public { 18 | vm.expectRevert(ReverseRegistrar.NoZeroAddress.selector); 19 | vm.prank(owner); 20 | reverse.setDefaultResolver(address(0)); 21 | } 22 | 23 | function test_setsTheDefaultResolver() public { 24 | address resolverAddr = makeAddr("resolver"); 25 | vm.expectEmit(address(reverse)); 26 | emit ReverseRegistrar.DefaultResolverChanged(NameResolver(resolverAddr)); 27 | vm.prank(owner); 28 | reverse.setDefaultResolver(resolverAddr); 29 | assertTrue(reverse.defaultResolver() == NameResolver(resolverAddr)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/ReverseRegistrar/SetName.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ReverseRegistrarBase} from "./ReverseRegistrarBase.t.sol"; 5 | import {ReverseRegistrar} from "src/L2/ReverseRegistrar.sol"; 6 | import {Sha3} from "src/lib/Sha3.sol"; 7 | import {BASE_REVERSE_NODE} from "src/util/Constants.sol"; 8 | import {NameResolver, MockNameResolver} from "test/mocks/MockNameResolver.sol"; 9 | 10 | contract SetName is ReverseRegistrarBase { 11 | NameResolver resolver = new MockNameResolver(); 12 | 13 | function test_setsName() public { 14 | bytes32 labelHash = Sha3.hexAddress(user); 15 | bytes32 baseReverseNode = keccak256(abi.encodePacked(BASE_REVERSE_NODE, labelHash)); 16 | 17 | string memory name = "name"; 18 | vm.prank(owner); 19 | reverse.setDefaultResolver(address(resolver)); 20 | 21 | vm.expectEmit(address(reverse)); 22 | emit ReverseRegistrar.BaseReverseClaimed(user, baseReverseNode); 23 | vm.prank(user); 24 | bytes32 returnedReverseNode = reverse.setName(name); 25 | 26 | assertTrue(baseReverseNode == returnedReverseNode); 27 | address retBaseOwner = registry.owner(baseReverseNode); 28 | assertTrue(retBaseOwner == user); 29 | address retBaseResolver = registry.resolver(baseReverseNode); 30 | assertTrue(retBaseResolver == address(resolver)); 31 | assertTrue(keccak256(abi.encode(resolver.name(baseReverseNode))) == keccak256(abi.encode(name))); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/ReverseRegistrarV2/Claim.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ReverseRegistrarV2Base} from "./ReverseRegistrarV2Base.t.sol"; 5 | import {ReverseRegistrarV2} from "src/L2/ReverseRegistrarV2.sol"; 6 | import {Sha3} from "src/lib/Sha3.sol"; 7 | import {BASE_REVERSE_NODE} from "src/util/Constants.sol"; 8 | 9 | contract Claim is ReverseRegistrarV2Base { 10 | function test_allowsUser_toClaim() public { 11 | bytes32 labelHash = Sha3.hexAddress(user); 12 | bytes32 baseReverseNode = keccak256(abi.encodePacked(BASE_REVERSE_NODE, labelHash)); 13 | 14 | vm.prank(owner); 15 | reverse.setDefaultResolver(address(resolver)); 16 | 17 | vm.expectEmit(address(reverse)); 18 | emit ReverseRegistrarV2.BaseReverseClaimed(user, baseReverseNode); 19 | 20 | vm.prank(user); 21 | bytes32 returnedReverseNode = reverse.claim(user); 22 | 23 | assertTrue(baseReverseNode == returnedReverseNode); 24 | address retBaseOwner = registry.owner(baseReverseNode); 25 | assertTrue(retBaseOwner == user); 26 | address retBaseResolver = registry.resolver(baseReverseNode); 27 | assertTrue(retBaseResolver == address(resolver)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/ReverseRegistrarV2/ClaimWithResolver.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ReverseRegistrarV2Base} from "./ReverseRegistrarV2Base.t.sol"; 5 | import {ReverseRegistrarV2} from "src/L2/ReverseRegistrarV2.sol"; 6 | import {Sha3} from "src/lib/Sha3.sol"; 7 | import {BASE_REVERSE_NODE} from "src/util/Constants.sol"; 8 | 9 | contract ClaimWithResolver is ReverseRegistrarV2Base { 10 | function test_allowsUser_toClaimWithResolver() public { 11 | bytes32 labelHash = Sha3.hexAddress(user); 12 | bytes32 reverseNode = keccak256(abi.encodePacked(BASE_REVERSE_NODE, labelHash)); 13 | 14 | vm.expectEmit(address(reverse)); 15 | emit ReverseRegistrarV2.BaseReverseClaimed(user, reverseNode); 16 | vm.prank(user); 17 | bytes32 returnedReverseNode = reverse.claimWithResolver(user, address(resolver)); 18 | assertTrue(reverseNode == returnedReverseNode); 19 | address retOwner = registry.owner(reverseNode); 20 | assertTrue(retOwner == user); 21 | address retResolver = registry.resolver(reverseNode); 22 | assertTrue(retResolver == address(resolver)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/ReverseRegistrarV2/Node.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ReverseRegistrarV2Base} from "./ReverseRegistrarV2Base.t.sol"; 5 | import {Sha3} from "src/lib/Sha3.sol"; 6 | import {BASE_REVERSE_NODE} from "src/util/Constants.sol"; 7 | 8 | contract Node is ReverseRegistrarV2Base { 9 | function test_returnsExpectedNode(address addr) public view { 10 | bytes32 labelHash = Sha3.hexAddress(addr); 11 | bytes32 expectedNode = keccak256(abi.encodePacked(BASE_REVERSE_NODE, labelHash)); 12 | bytes32 retNode = reverse.node(addr); 13 | assertTrue(retNode == expectedNode); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/ReverseRegistrarV2/SetControllerApproval.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ReverseRegistrarV2Base} from "./ReverseRegistrarV2Base.t.sol"; 5 | import {ReverseRegistrar} from "src/L2/ReverseRegistrar.sol"; 6 | import {Ownable} from "solady/auth/Ownable.sol"; 7 | 8 | contract SetControllerApproval is ReverseRegistrarV2Base { 9 | function test_reverts_ifCalledByNonOwner(address caller) public { 10 | vm.assume(caller != owner && caller != address(0)); 11 | vm.expectRevert(Ownable.Unauthorized.selector); 12 | reverse.setControllerApproval(caller, true); 13 | } 14 | 15 | function test_allowsTheOwner_toUpdateControllerApproval(address newController) public { 16 | vm.assume(newController != address(0)); 17 | 18 | vm.expectEmit(address(reverse)); 19 | emit ReverseRegistrar.ControllerApprovalChanged(newController, true); 20 | vm.prank(owner); 21 | reverse.setControllerApproval(newController, true); 22 | assertTrue(reverse.controllers(newController)); 23 | 24 | vm.expectEmit(address(reverse)); 25 | emit ReverseRegistrar.ControllerApprovalChanged(newController, false); 26 | vm.prank(owner); 27 | reverse.setControllerApproval(newController, false); 28 | assertFalse(reverse.controllers(newController)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/ReverseRegistrarV2/SetDefaultResolver.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {NameResolver} from "ens-contracts/resolvers/profiles/NameResolver.sol"; 5 | import {Ownable} from "solady/auth/Ownable.sol"; 6 | import {ReverseRegistrarV2Base} from "./ReverseRegistrarV2Base.t.sol"; 7 | import {ReverseRegistrarV2} from "src/L2/ReverseRegistrarV2.sol"; 8 | 9 | contract SetDefaultResolver is ReverseRegistrarV2Base { 10 | function test_reverts_whenCalledByNonOwner(address caller) public { 11 | vm.assume(caller != owner); 12 | vm.expectRevert(Ownable.Unauthorized.selector); 13 | vm.prank(caller); 14 | reverse.setDefaultResolver(makeAddr("fake")); 15 | } 16 | 17 | function test_reverts_whenPassedZeroAddress() public { 18 | vm.expectRevert(ReverseRegistrarV2.NoZeroAddress.selector); 19 | vm.prank(owner); 20 | reverse.setDefaultResolver(address(0)); 21 | } 22 | 23 | function test_setsTheDefaultResolver() public { 24 | vm.expectEmit(address(reverse)); 25 | emit ReverseRegistrarV2.DefaultResolverChanged(address(resolver)); 26 | vm.prank(owner); 27 | reverse.setDefaultResolver(address(resolver)); 28 | assertTrue(reverse.defaultResolver() == address(resolver)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/ReverseRegistrarV2/SetName.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ReverseRegistrarV2Base} from "./ReverseRegistrarV2Base.t.sol"; 5 | import {ReverseRegistrarV2} from "src/L2/ReverseRegistrarV2.sol"; 6 | import {Sha3} from "src/lib/Sha3.sol"; 7 | import {BASE_REVERSE_NODE} from "src/util/Constants.sol"; 8 | 9 | contract SetName is ReverseRegistrarV2Base { 10 | function test_setsName() public { 11 | bytes32 labelHash = Sha3.hexAddress(user); 12 | bytes32 baseReverseNode = keccak256(abi.encodePacked(BASE_REVERSE_NODE, labelHash)); 13 | 14 | string memory name = "name"; 15 | vm.prank(owner); 16 | reverse.setDefaultResolver(address(resolver)); 17 | 18 | vm.expectEmit(address(reverse)); 19 | emit ReverseRegistrarV2.BaseReverseClaimed(user, baseReverseNode); 20 | vm.prank(user); 21 | bytes32 returnedReverseNode = reverse.setName(name); 22 | 23 | assertTrue(baseReverseNode == returnedReverseNode); 24 | address retBaseOwner = registry.owner(baseReverseNode); 25 | assertTrue(retBaseOwner == user); 26 | address retBaseResolver = registry.resolver(baseReverseNode); 27 | assertTrue(retBaseResolver == address(resolver)); 28 | assertTrue(keccak256(abi.encode(resolver.name(baseReverseNode))) == keccak256(abi.encode(name))); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/ReverseRegistrarV2/SetNameForAddrWithSignature.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ReverseRegistrarV2} from "src/L2/ReverseRegistrarV2.sol"; 5 | import {ReverseRegistrarV2Base} from "./ReverseRegistrarV2Base.t.sol"; 6 | import {MockL2ReverseRegistrar} from "test/mocks/MockL2ReverseRegistrar.sol"; 7 | import {MockReverseRegistrar} from "test/mocks/MockReverseRegistrar.sol"; 8 | import {BASE_REVERSE_NODE} from "src/util/Constants.sol"; 9 | import {Sha3} from "src/lib/Sha3.sol"; 10 | 11 | contract SetNameForAddrWithSignature is ReverseRegistrarV2Base { 12 | function test_setsNameForAddrWithSignature() public { 13 | uint256[] memory cointypes = new uint256[](1); 14 | cointypes[0] = BASE_COINTYPE; 15 | bytes memory signature = ""; 16 | uint256 expiry = block.timestamp + 5 minutes; 17 | 18 | vm.prank(owner); 19 | reverse.setDefaultResolver(address(resolver)); 20 | 21 | bytes32 labelHash = Sha3.hexAddress(user); 22 | bytes32 baseReverseNode = keccak256(abi.encodePacked(BASE_REVERSE_NODE, labelHash)); 23 | 24 | vm.expectCall( 25 | address(l2ReverseRegistrar), 26 | abi.encodeCall( 27 | MockL2ReverseRegistrar.setNameForAddrWithSignature, (user, expiry, name, cointypes, signature) 28 | ) 29 | ); 30 | vm.prank(user); 31 | 32 | bytes32 revNode = reverse.setNameForAddrWithSignature(user, expiry, name, cointypes, signature); 33 | assertEq(revNode, baseReverseNode); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/StablePriceOracle/Premium.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {StablePriceOracle} from "src/L2/StablePriceOracle.sol"; 6 | import {StablePriceOracleBase} from "./StablePriceOracleBase.t.sol"; 7 | 8 | contract Premium is StablePriceOracleBase { 9 | function test_premium() public view { 10 | uint256 premiumWei = stablePriceOracle.premium("abc", 0, 365 days); 11 | assertEq(premiumWei, 0); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/StablePriceOracle/StablePriceFuzzTest.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import {Test, console} from "forge-std/Test.sol"; 5 | import {StablePriceOracle} from "src/L2/StablePriceOracle.sol"; 6 | 7 | contract StablePriceFuzzTest is Test { 8 | StablePriceOracle stablePriceOracle; 9 | 10 | function setUp(uint256 fuzz) internal { 11 | uint256[] memory rentPrices = new uint256[](6); 12 | for (uint256 i = 0; i < 6; i++) { 13 | rentPrices[i] = uint256(keccak256(abi.encodePacked(fuzz, i))) % 1e14; 14 | } 15 | stablePriceOracle = new StablePriceOracle(rentPrices); 16 | } 17 | 18 | function test_price(string memory name, uint256 expires, uint256 duration, uint256 fuzz) public { 19 | setUp(fuzz); 20 | vm.assume(bytes(name).length > 0 && bytes(name).length <= 512); 21 | duration = duration % 1e18; 22 | stablePriceOracle.price(name, expires, duration); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/StablePriceOracle/StablePriceOracleBase.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ~0.8.17; 3 | 4 | import {Test, console} from "forge-std/Test.sol"; 5 | import {StablePriceOracle} from "src/L2/StablePriceOracle.sol"; 6 | 7 | contract StablePriceOracleBase is Test { 8 | StablePriceOracle stablePriceOracle; 9 | 10 | uint256 rent1; 11 | uint256 rent2; 12 | uint256 rent3; 13 | uint256 rent4; 14 | uint256 rent5; 15 | uint256 rent10; 16 | 17 | function setUp() public { 18 | uint256[] memory rentPrices = new uint256[](6); 19 | 20 | rent1 = 316_808_781_402; 21 | rent2 = 31_680_878_140; 22 | rent3 = 3_168_087_814; 23 | rent4 = 316_808_781; 24 | rent5 = 31_680_878; 25 | rent10 = 3_168_087; // 3,168,808.781402895 = 1e14 / (365.25 * 24 * 3600) 26 | 27 | rentPrices[0] = rent1; 28 | rentPrices[1] = rent2; 29 | rentPrices[2] = rent3; 30 | rentPrices[3] = rent4; 31 | rentPrices[4] = rent5; 32 | rentPrices[5] = rent10; 33 | 34 | stablePriceOracle = new StablePriceOracle(rentPrices); 35 | } 36 | 37 | function test_constructor() public view { 38 | assertEq(stablePriceOracle.price1Letter(), rent1); 39 | assertEq(stablePriceOracle.price2Letter(), rent2); 40 | assertEq(stablePriceOracle.price3Letter(), rent3); 41 | assertEq(stablePriceOracle.price4Letter(), rent4); 42 | assertEq(stablePriceOracle.price5Letter(), rent5); 43 | assertEq(stablePriceOracle.price10Letter(), rent10); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/UpgradeableL2Resolver/Approve.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableL2ResolverBase} from "./UpgradeableL2ResolverBase.t.sol"; 5 | import {UpgradeableL2Resolver} from "src/L2/UpgradeableL2Resolver.sol"; 6 | 7 | contract Approve is UpgradeableL2ResolverBase { 8 | function test_revertsIfCalledForSelf() public { 9 | vm.expectRevert(UpgradeableL2Resolver.CantSetSelfAsDelegate.selector); 10 | vm.prank(user); 11 | resolver.approve(node, user, true); 12 | } 13 | 14 | function test_allowsSenderToSetDelegate(address operator) public { 15 | vm.assume(operator != user); 16 | vm.expectEmit(address(resolver)); 17 | emit UpgradeableL2Resolver.Approved(user, node, operator, true); 18 | vm.prank(user); 19 | resolver.approve(node, operator, true); 20 | assertTrue(resolver.isApprovedFor(user, node, operator)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/UpgradeableL2Resolver/ClearRecords.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableL2ResolverBase} from "./UpgradeableL2ResolverBase.t.sol"; 5 | import {ResolverBase} from "src/L2/resolver/ResolverBase.sol"; 6 | 7 | import {IVersionableResolver} from "ens-contracts/resolvers/profiles/IVersionableResolver.sol"; 8 | 9 | contract ClearRecords is UpgradeableL2ResolverBase { 10 | function test_reverts_forUnauthorizedUser() public { 11 | vm.expectRevert(abi.encodeWithSelector(ResolverBase.NotAuthorized.selector, node, notUser)); 12 | vm.prank(notUser); 13 | resolver.clearRecords(node); 14 | } 15 | 16 | function test_clearRecords() public { 17 | uint64 currentRecordVersion = resolver.recordVersions(node); 18 | vm.prank(user); 19 | vm.expectEmit(address(resolver)); 20 | emit IVersionableResolver.VersionChanged(node, currentRecordVersion + 1); 21 | resolver.clearRecords(node); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/UpgradeableL2Resolver/SetApprovalForAll.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableL2ResolverBase} from "./UpgradeableL2ResolverBase.t.sol"; 5 | import {L2Resolver} from "src/L2/L2Resolver.sol"; 6 | 7 | contract SetApprovalForAll is UpgradeableL2ResolverBase { 8 | function test_revertsIfCalledForSelf() public { 9 | vm.expectRevert(L2Resolver.CantSetSelfAsOperator.selector); 10 | vm.prank(user); 11 | resolver.setApprovalForAll(user, true); 12 | } 13 | 14 | function test_allowsSenderToSetApproval(address operator, bool approve) public { 15 | vm.assume(operator != user); 16 | vm.expectEmit(address(resolver)); 17 | emit L2Resolver.ApprovalForAll(user, operator, approve); 18 | vm.prank(user); 19 | resolver.setApprovalForAll(operator, approve); 20 | assertEq(resolver.isApprovedForAll(user, operator), approve); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/UpgradeableL2Resolver/SetContentHash.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableL2ResolverBase} from "./UpgradeableL2ResolverBase.t.sol"; 5 | import {ResolverBase} from "src/L2/resolver/ResolverBase.sol"; 6 | import {ContentHashResolver} from "src/L2/resolver/ContentHashResolver.sol"; 7 | 8 | contract SetContenthash is UpgradeableL2ResolverBase { 9 | bytes IPFS_Data = hex"e3010170122029f2d17be6139079dc48696d1f582a8530eb9805b561eda517e22a892c7e3f1f"; 10 | 11 | function test_reverts_forUnauthorizedUser() public { 12 | vm.expectRevert(abi.encodeWithSelector(ResolverBase.NotAuthorized.selector, node, notUser)); 13 | vm.prank(notUser); 14 | resolver.setContenthash(node, IPFS_Data); 15 | } 16 | 17 | function test_setsAContenthash() public { 18 | vm.prank(user); 19 | resolver.setContenthash(node, IPFS_Data); 20 | assertEq(keccak256(resolver.contenthash(node)), keccak256(IPFS_Data)); 21 | } 22 | 23 | function test_canClearRecord() public { 24 | vm.startPrank(user); 25 | 26 | resolver.setContenthash(node, IPFS_Data); 27 | assertEq(resolver.contenthash(node), IPFS_Data); 28 | 29 | resolver.clearRecords(node); 30 | assertEq(resolver.contenthash(node), ""); 31 | 32 | vm.stopPrank(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/UpgradeableL2Resolver/SetControllerApproval.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableL2ResolverBase} from "./UpgradeableL2ResolverBase.t.sol"; 5 | import {UpgradeableL2Resolver} from "src/L2/UpgradeableL2Resolver.sol"; 6 | import {OwnableUpgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol"; 7 | 8 | contract SetControllerApproval is UpgradeableL2ResolverBase { 9 | function test_reverts_ifCalledByNonOwner(address caller, address newController) public notProxyAdmin(caller) { 10 | vm.assume(caller != owner); 11 | vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, caller)); 12 | vm.prank(caller); 13 | resolver.setControllerApproval(newController, true); 14 | } 15 | 16 | function test_reverts_ifSettingNewControllerToZeroAddress() public { 17 | vm.expectRevert(UpgradeableL2Resolver.NoZeroAddress.selector); 18 | vm.prank(owner); 19 | resolver.setControllerApproval(address(0), true); 20 | } 21 | 22 | function test_setsTheRegistrarControllerAccordingly(address newController) public { 23 | vm.assume(newController != address(0)); 24 | vm.expectEmit(); 25 | emit UpgradeableL2Resolver.ControllerApprovalChanged(newController, true); 26 | vm.prank(owner); 27 | resolver.setControllerApproval(newController, true); 28 | assertTrue(resolver.getControllerApproval(newController)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/UpgradeableL2Resolver/SetName.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableL2ResolverBase} from "./UpgradeableL2ResolverBase.t.sol"; 5 | import {ResolverBase} from "src/L2/resolver/ResolverBase.sol"; 6 | import {NameResolver} from "src/L2/resolver/NameResolver.sol"; 7 | 8 | contract SetName is UpgradeableL2ResolverBase { 9 | function test_reverts_forUnauthorizedUser() public { 10 | vm.expectRevert(abi.encodeWithSelector(ResolverBase.NotAuthorized.selector, node, notUser)); 11 | vm.prank(notUser); 12 | resolver.setName(node, name); 13 | } 14 | 15 | function test_setsTheName() public { 16 | vm.prank(user); 17 | resolver.setName(node, name); 18 | string memory retName = resolver.name(node); 19 | assertEq(keccak256(bytes(name)), keccak256(bytes(retName))); 20 | } 21 | 22 | function test_canClearRecord() public { 23 | vm.startPrank(user); 24 | 25 | resolver.setName(node, name); 26 | assertEq(resolver.name(node), name); 27 | 28 | resolver.clearRecords(node); 29 | assertEq(resolver.name(node), ""); 30 | 31 | vm.stopPrank(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/UpgradeableL2Resolver/SetPubkey.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableL2ResolverBase} from "./UpgradeableL2ResolverBase.t.sol"; 5 | import {ResolverBase} from "src/L2/resolver/ResolverBase.sol"; 6 | import {PubkeyResolver} from "src/L2/resolver/PubkeyResolver.sol"; 7 | 8 | contract SetPubkey is UpgradeableL2ResolverBase { 9 | bytes32 x = 0x65a2fa44daad46eab0278703edb6c4dcf5e30b8a9aec09fdc71a56f52aa392e4; 10 | bytes32 y = 0x4a7a9e4604aa36898209997288e902ac544a555e4b5e0a9efef2b59233f3f437; 11 | 12 | function test_reverts_forUnauthorizedUser() public { 13 | vm.expectRevert(abi.encodeWithSelector(ResolverBase.NotAuthorized.selector, node, notUser)); 14 | vm.prank(notUser); 15 | resolver.setPubkey(node, x, y); 16 | } 17 | 18 | function test_setsThePubkey() public { 19 | vm.prank(user); 20 | resolver.setPubkey(node, x, y); 21 | (bytes32 retX, bytes32 retY) = resolver.pubkey(node); 22 | assertEq(retX, x); 23 | assertEq(retY, y); 24 | } 25 | 26 | function test_canClearRecord() public { 27 | vm.startPrank(user); 28 | 29 | resolver.setPubkey(node, x, y); 30 | (bytes32 retX, bytes32 retY) = resolver.pubkey(node); 31 | assertEq(retX, x); 32 | assertEq(retY, y); 33 | 34 | resolver.clearRecords(node); 35 | (retX, retY) = resolver.pubkey(node); 36 | assertEq(retX, 0); 37 | assertEq(retY, 0); 38 | 39 | vm.stopPrank(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/UpgradeableL2Resolver/SetReverseRegistrar.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableL2ResolverBase} from "./UpgradeableL2ResolverBase.t.sol"; 5 | import {UpgradeableL2Resolver} from "src/L2/UpgradeableL2Resolver.sol"; 6 | import {OwnableUpgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol"; 7 | 8 | contract SetReverseRegistrar is UpgradeableL2ResolverBase { 9 | function test_reverts_ifCalledByNonOwner(address caller, address newReverse) public notProxyAdmin(caller) { 10 | vm.assume(caller != owner); 11 | vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, caller)); 12 | vm.prank(caller); 13 | resolver.setReverseRegistrar(newReverse); 14 | } 15 | 16 | function test_reverts_ifSettingNewReverseRegistrarToZeroAddress() public { 17 | vm.expectRevert(UpgradeableL2Resolver.NoZeroAddress.selector); 18 | vm.prank(owner); 19 | resolver.setReverseRegistrar(address(0)); 20 | } 21 | 22 | function test_setsTheReverseRegistrarAccordingly(address newReverse) public { 23 | vm.assume(newReverse != address(0)); 24 | vm.expectEmit(); 25 | emit UpgradeableL2Resolver.ReverseRegistrarUpdated(newReverse); 26 | vm.prank(owner); 27 | resolver.setReverseRegistrar(newReverse); 28 | assertEq(resolver.reverseRegistrar(), newReverse); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/UpgradeableL2Resolver/SetText.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableL2ResolverBase} from "./UpgradeableL2ResolverBase.t.sol"; 5 | import {ResolverBase} from "src/L2/resolver/ResolverBase.sol"; 6 | import {TextResolver} from "src/L2/resolver/TextResolver.sol"; 7 | 8 | contract SetText is UpgradeableL2ResolverBase { 9 | string key = "key"; 10 | string value = "value"; 11 | 12 | function test_reverts_forUnauthorizedUser() public { 13 | vm.expectRevert(abi.encodeWithSelector(ResolverBase.NotAuthorized.selector, node, notUser)); 14 | vm.prank(notUser); 15 | resolver.setText(node, key, value); 16 | } 17 | 18 | function test_setsTheTextValue_forTheSpecifiedKey() public { 19 | vm.prank(user); 20 | resolver.setText(node, key, value); 21 | string memory retValue = resolver.text(node, key); 22 | assertEq(keccak256(bytes(retValue)), keccak256(bytes(value))); 23 | } 24 | 25 | function test_canClearRecord() public { 26 | vm.startPrank(user); 27 | 28 | resolver.setText(node, key, value); 29 | assertEq(resolver.text(node, key), value); 30 | 31 | resolver.clearRecords(node); 32 | assertEq(resolver.text(node, key), ""); 33 | 34 | vm.stopPrank(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/UpgradeableL2Resolver/SetZonehash.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableL2ResolverBase} from "./UpgradeableL2ResolverBase.t.sol"; 5 | import {ResolverBase} from "src/L2/resolver/ResolverBase.sol"; 6 | import {DNSResolver} from "src/L2/resolver/DNSResolver.sol"; 7 | 8 | contract SetZonehash is UpgradeableL2ResolverBase { 9 | bytes zonehash = bytes("zonehash"); 10 | 11 | function test_reverts_forUnauthorizedUser() public { 12 | vm.expectRevert(abi.encodeWithSelector(ResolverBase.NotAuthorized.selector, node, notUser)); 13 | vm.prank(notUser); 14 | resolver.setZonehash(node, zonehash); 15 | } 16 | 17 | function test_setsZonehash() public { 18 | vm.prank(user); 19 | resolver.setZonehash(node, zonehash); 20 | assertEq(keccak256(resolver.zonehash(node)), keccak256(zonehash)); 21 | } 22 | 23 | function test_canClearRecord() public { 24 | vm.startPrank(user); 25 | 26 | resolver.setZonehash(node, zonehash); 27 | assertEq(resolver.zonehash(node), zonehash); 28 | 29 | resolver.clearRecords(node); 30 | assertEq(resolver.zonehash(node), ""); 31 | 32 | vm.stopPrank(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/UpgradeableRegistrarController/Available.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol"; 5 | 6 | contract Available is UpgradeableRegistrarControllerBase { 7 | function test_returnsFalse_whenNotAvailableOnBase() public { 8 | base.setAvailable(uint256(nameLabel), false); 9 | assertFalse(controller.available(name)); 10 | } 11 | 12 | function test_returnsFalse_whenInvalidLength() public { 13 | base.setAvailable(uint256(shortNameLabel), true); 14 | assertFalse(controller.available(shortName)); 15 | } 16 | 17 | function test_returnsTrue_whenValidAndAvailable() public { 18 | base.setAvailable(uint256(nameLabel), true); 19 | assertTrue(controller.available(name)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/UpgradeableRegistrarController/DiscountedRegisterPrice.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol"; 5 | import {RegistrarController} from "src/L2/RegistrarController.sol"; 6 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 7 | 8 | contract DiscountedRegisterPrice is UpgradeableRegistrarControllerBase { 9 | function test_returnsADiscountedPrice_whenThePriceIsGreaterThanTheDiscount(uint256 price) public { 10 | vm.assume(price > discountAmount); 11 | prices.setPrice(name, IPriceOracle.Price({base: price, premium: 0})); 12 | vm.prank(owner); 13 | controller.setDiscountDetails(_getDefaultDiscount()); 14 | 15 | uint256 expectedPrice = price - discountAmount; 16 | uint256 retPrice = controller.discountedRegisterPrice(name, duration, discountKey); 17 | assertEq(retPrice, expectedPrice); 18 | } 19 | 20 | function test_returnsZero_whenThePriceIsLessThanOrEqualToTheDiscount(uint256 price) public { 21 | vm.assume(price > 0 && price <= discountAmount); 22 | prices.setPrice(name, IPriceOracle.Price({base: price, premium: 0})); 23 | vm.prank(owner); 24 | controller.setDiscountDetails(_getDefaultDiscount()); 25 | 26 | uint256 retPrice = controller.discountedRegisterPrice(name, duration, discountKey); 27 | assertEq(retPrice, 0); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/UpgradeableRegistrarController/RegisterPrice.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol"; 5 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 6 | 7 | contract RegisterPrice is UpgradeableRegistrarControllerBase { 8 | function test_returnsRegisterPrice_fromPricingOracle() public view { 9 | uint256 retPrice = controller.registerPrice(name, duration); 10 | assertEq(retPrice, prices.DEFAULT_BASE_WEI() + prices.DEFAULT_PREMIUM_WEI()); 11 | } 12 | 13 | function test_fuzz_returnsRegisterPrice_fromPricingOracle(uint256 fuzzBase, uint256 fuzzPremium) public { 14 | vm.assume(fuzzBase != 0 && fuzzBase < type(uint128).max); 15 | vm.assume(fuzzPremium < type(uint128).max); 16 | IPriceOracle.Price memory expectedPrice = IPriceOracle.Price({base: fuzzBase, premium: fuzzPremium}); 17 | prices.setPrice(name, expectedPrice); 18 | uint256 retPrice = controller.registerPrice(name, 0); 19 | assertEq(retPrice, expectedPrice.base + expectedPrice.premium); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/UpgradeableRegistrarController/RentPrice.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol"; 5 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 6 | 7 | contract RentPrice is UpgradeableRegistrarControllerBase { 8 | function test_returnsPrice_fromPricingOracle() public view { 9 | IPriceOracle.Price memory retPrices = controller.rentPrice(name, 0); 10 | assertEq(retPrices.base, prices.DEFAULT_BASE_WEI()); 11 | assertEq(retPrices.premium, prices.DEFAULT_PREMIUM_WEI()); 12 | } 13 | 14 | function test_fuzz_returnsPrice_fromPricingOracle(uint256 fuzzBase, uint256 fuzzPremium) public { 15 | vm.assume(fuzzBase != 0 && fuzzBase < type(uint128).max); 16 | vm.assume(fuzzPremium < type(uint128).max); 17 | IPriceOracle.Price memory expectedPrice = IPriceOracle.Price({base: fuzzBase, premium: fuzzPremium}); 18 | prices.setPrice(name, expectedPrice); 19 | IPriceOracle.Price memory retPrices = controller.rentPrice(name, 0); 20 | assertEq(retPrices.base, expectedPrice.base); 21 | assertEq(retPrices.premium, expectedPrice.premium); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/UpgradeableRegistrarController/SetPaymentReceiver.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol"; 5 | import {UpgradeableRegistrarController} from "src/L2/UpgradeableRegistrarController.sol"; 6 | import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/access/OwnableUpgradeable.sol"; 7 | 8 | contract SetPaymentReceiver is UpgradeableRegistrarControllerBase { 9 | function test_reverts_ifCalledByNonOwner(address caller) public whenNotProxyAdmin(caller, address(controller)) { 10 | vm.assume(caller != owner && caller != address(0)); 11 | vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, caller)); 12 | vm.prank(caller); 13 | controller.setPaymentReceiver(caller); 14 | } 15 | 16 | function test_reverts_ifNewPaymentReceiver_isZeroAddress() public { 17 | vm.expectRevert(UpgradeableRegistrarController.InvalidPaymentReceiver.selector); 18 | vm.prank(owner); 19 | controller.setPaymentReceiver(address(0)); 20 | } 21 | 22 | function test_allowsTheOwner_toSetThePaymentReceiver(address newReceiver) public { 23 | vm.assume(newReceiver != address(0)); 24 | vm.expectEmit(address(controller)); 25 | emit UpgradeableRegistrarController.PaymentReceiverUpdated(newReceiver); 26 | vm.prank(owner); 27 | controller.setPaymentReceiver(newReceiver); 28 | assertEq(newReceiver, controller.paymentReceiver()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/UpgradeableRegistrarController/SetPriceOracle.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol"; 5 | import {UpgradeableRegistrarController} from "src/L2/UpgradeableRegistrarController.sol"; 6 | import {MockPriceOracle} from "test/mocks/MockPriceOracle.sol"; 7 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 8 | import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/access/OwnableUpgradeable.sol"; 9 | 10 | contract SetPriceOracle is UpgradeableRegistrarControllerBase { 11 | function test_reverts_ifCalledByNonOwner(address caller) public whenNotProxyAdmin(caller, address(controller)) { 12 | vm.assume(caller != owner); 13 | MockPriceOracle newPrices = new MockPriceOracle(); 14 | vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, caller)); 15 | vm.prank(caller); 16 | controller.setPriceOracle(IPriceOracle(address(newPrices))); 17 | } 18 | 19 | function test_setsThePriceOracleAccordingly() public { 20 | vm.expectEmit(); 21 | MockPriceOracle newPrices = new MockPriceOracle(); 22 | emit UpgradeableRegistrarController.PriceOracleUpdated(address(newPrices)); 23 | vm.prank(owner); 24 | controller.setPriceOracle(IPriceOracle(address(newPrices))); 25 | assertEq(address(controller.prices()), address(newPrices)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/UpgradeableRegistrarController/SetReverseRegistrar.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol"; 5 | import {UpgradeableRegistrarController} from "src/L2/UpgradeableRegistrarController.sol"; 6 | import {MockReverseRegistrar} from "test/mocks/MockReverseRegistrar.sol"; 7 | import {IReverseRegistrar} from "src/L2/interface/IReverseRegistrar.sol"; 8 | import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/access/OwnableUpgradeable.sol"; 9 | 10 | contract SetReverseRegistrar is UpgradeableRegistrarControllerBase { 11 | function test_reverts_ifCalledByNonOwner(address caller) public whenNotProxyAdmin(caller, address(controller)) { 12 | vm.assume(caller != owner); 13 | MockReverseRegistrar newReverse = new MockReverseRegistrar(); 14 | vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, caller)); 15 | vm.prank(caller); 16 | controller.setReverseRegistrar(IReverseRegistrar(address(newReverse))); 17 | } 18 | 19 | function test_setsTheReverseRegistrarAccordingly() public { 20 | vm.expectEmit(); 21 | MockReverseRegistrar newReverse = new MockReverseRegistrar(); 22 | emit UpgradeableRegistrarController.ReverseRegistrarUpdated(address(newReverse)); 23 | vm.prank(owner); 24 | controller.setReverseRegistrar(IReverseRegistrar(address(newReverse))); 25 | assertEq(address(controller.reverseRegistrar()), address(newReverse)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/UpgradeableRegistrarController/Valid.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol"; 5 | 6 | contract Valid is UpgradeableRegistrarControllerBase { 7 | function test_returnsTrue_whenValid() public view { 8 | assertTrue(controller.valid("abc")); 9 | assertTrue(controller.valid("abcdef")); 10 | assertTrue(controller.valid("abcdefghijklmnop")); 11 | } 12 | 13 | function test_returnsFalse_whenInvalid() public view { 14 | assertFalse(controller.valid("")); 15 | assertFalse(controller.valid("a")); 16 | assertFalse(controller.valid("ab")); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/UpgradeableRegistrarController/WithdrawETH.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {UpgradeableRegistrarControllerBase} from "./UpgradeableRegistrarControllerBase.t.sol"; 5 | import {UpgradeableRegistrarController} from "src/L2/UpgradeableRegistrarController.sol"; 6 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 7 | 8 | contract WithdrawETH is UpgradeableRegistrarControllerBase { 9 | function test_alwaysSendsTheBalanceToTheOwner(address caller) 10 | public 11 | whenNotProxyAdmin(caller, address(controller)) 12 | { 13 | vm.deal(address(controller), 1 ether); 14 | assertEq(payments.balance, 0); 15 | vm.prank(caller); 16 | controller.withdrawETH(); 17 | assertEq(payments.balance, 1 ether); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/discounts/AttestationValidator/SetSigner.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {AttestationValidatorBase} from "./AttestationValidatorBase.t.sol"; 5 | import {Ownable} from "solady/auth/Ownable.sol"; 6 | 7 | contract SetSigner is AttestationValidatorBase { 8 | function test_reverts_whenCalledByNonOwner(address caller) public { 9 | vm.assume(caller != owner && caller != address(0)); 10 | vm.expectRevert(Ownable.Unauthorized.selector); 11 | vm.prank(caller); 12 | validator.setSigner(caller); 13 | } 14 | 15 | function test_allowsTheOwner_toUpdateTheSigner() public { 16 | vm.prank(owner); 17 | address newSigner = makeAddr("new"); 18 | validator.setSigner(newSigner); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/discounts/CBIdDiscountValidator/CBIdDiscountValidatorBase.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {CBIdDiscountValidator} from "src/L2/discounts/CBIdDiscountValidator.sol"; 6 | 7 | contract CBIdDiscountValidatorBase is Test { 8 | CBIdDiscountValidator public validator; 9 | 10 | address public owner = makeAddr("owner"); 11 | address ace = address(0xace); 12 | address bob = address(0xb0b); 13 | address codie = address(0xc0d1e); 14 | 15 | bytes32 public root = 0x17b5b2e0a6979fb8c0e55a25fd3f2dbf6d147e5ad04d79d9e272c3dd1706219a; // tree contains bob and codie 16 | 17 | bytes32[] public bobProof = [bytes32(0x789d8ab94963f94f7fbef2c39fc1c79a810770640e2d061d775eea4b24b255c0)]; 18 | bytes32[] public codieProof = [bytes32(0x3034df95d8f0ea7db7ab950e22fc977fa82ae80174df73ee1c75c24246b96df3)]; 19 | 20 | function setUp() public { 21 | validator = new CBIdDiscountValidator(owner, root); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/discounts/CBIdDiscountValidator/IsValidDiscountRegistration.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {CBIdDiscountValidatorBase} from "./CBIdDiscountValidatorBase.t.sol"; 5 | 6 | contract IsValidDiscountRegistration is CBIdDiscountValidatorBase { 7 | function test_returnsFalse_forInvalidProof(address claimer) public view { 8 | vm.assume(claimer != bob); 9 | bytes memory data = abi.encode(bobProof); 10 | assertFalse(validator.isValidDiscountRegistration(claimer, data)); 11 | } 12 | 13 | function test_returnsTrue_forValidProof() public view { 14 | bytes memory bobData = abi.encode(bobProof); 15 | assertTrue(validator.isValidDiscountRegistration(bob, bobData)); 16 | bytes memory codieData = abi.encode(codieProof); 17 | assertTrue(validator.isValidDiscountRegistration(codie, codieData)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/discounts/CBIdDiscountValidator/SetRoot.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Ownable} from "solady/auth/Ownable.sol"; 5 | import {CBIdDiscountValidatorBase} from "./CBIdDiscountValidatorBase.t.sol"; 6 | 7 | contract SetRoot is CBIdDiscountValidatorBase { 8 | function test_reverts_ifCalledByNonowner(address caller) public { 9 | vm.assume(caller != address(0) && caller != owner); 10 | vm.expectRevert(Ownable.Unauthorized.selector); 11 | vm.prank(caller); 12 | validator.setRoot(bytes32(0)); 13 | } 14 | 15 | function test_allowsTheOwnerToSetTheRoot(bytes32 newRoot) public { 16 | vm.assume(newRoot != root); 17 | assertEq(validator.root(), root); 18 | vm.prank(owner); 19 | validator.setRoot(bytes32(0)); 20 | assertEq(validator.root(), bytes32(0)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/discounts/CouponDiscountValidator/CouponDiscountValidatorBase.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {CouponDiscountValidator} from "src/L2/discounts/CouponDiscountValidator.sol"; 6 | 7 | contract CouponDiscountValidatorBase is Test { 8 | CouponDiscountValidator validator; 9 | 10 | address public owner = makeAddr("owner"); 11 | address public user = makeAddr("user"); 12 | address public signer; 13 | uint256 public signerPk; 14 | bytes32 uuid; 15 | 16 | uint64 time = 1717200000; 17 | uint64 expires = 1893456000; 18 | 19 | function setUp() public { 20 | vm.warp(time); 21 | (signer, signerPk) = makeAddrAndKey("signer"); 22 | uuid = keccak256("test_coupon"); 23 | validator = new CouponDiscountValidator(owner, signer); 24 | } 25 | 26 | function _getDefaultValidationData() internal virtual returns (bytes memory) { 27 | bytes32 digest = _makeSignatureHash(user, uuid, expires); 28 | (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, digest); 29 | bytes memory sig = abi.encodePacked(r, s, v); 30 | return abi.encode(expires, uuid, sig); 31 | } 32 | 33 | function _makeSignatureHash(address claimer, bytes32 couponUuid, uint64 _expires) internal view returns (bytes32) { 34 | return keccak256(abi.encodePacked(hex"1900", address(validator), signer, claimer, couponUuid, _expires)); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/discounts/CouponDiscountValidator/IsValidDiscountRegistration.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {CouponDiscountValidator} from "src/L2/discounts/CouponDiscountValidator.sol"; 5 | import {CouponDiscountValidatorBase} from "./CouponDiscountValidatorBase.t.sol"; 6 | 7 | contract IsValidDiscountRegistration is CouponDiscountValidatorBase { 8 | function test_reverts_whenTheSignatureIsExpired() public { 9 | bytes memory validationData = _getDefaultValidationData(); 10 | (, bytes32 _uuid, bytes memory sig) = abi.decode(validationData, (uint64, bytes32, bytes)); 11 | bytes memory expiredSignatureData = abi.encode((block.timestamp - 1), _uuid, sig); 12 | 13 | vm.expectRevert(abi.encodeWithSelector(CouponDiscountValidator.SignatureExpired.selector)); 14 | validator.isValidDiscountRegistration(user, expiredSignatureData); 15 | } 16 | 17 | function test_returnsFalse_whenTheExpectedSignerMismatches(uint256 pk) public view { 18 | vm.assume(pk != signerPk && pk != 0 && pk < type(uint128).max); 19 | bytes32 digest = _makeSignatureHash(user, uuid, expires); 20 | (uint8 v, bytes32 r, bytes32 s) = vm.sign(pk, digest); 21 | bytes memory sig = abi.encodePacked(r, s, v); 22 | bytes memory badSignerValidationData = abi.encode(expires, uuid, sig); 23 | 24 | assertFalse(validator.isValidDiscountRegistration(user, badSignerValidationData)); 25 | } 26 | 27 | function test_returnsTrue_whenEverythingIsHappy() public { 28 | assertTrue(validator.isValidDiscountRegistration(user, _getDefaultValidationData())); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/discounts/CouponDiscountValidator/SetSigner.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {CouponDiscountValidatorBase} from "./CouponDiscountValidatorBase.t.sol"; 5 | import {Ownable} from "solady/auth/Ownable.sol"; 6 | 7 | contract SetSigner is CouponDiscountValidatorBase { 8 | function test_reverts_whenCalledByNonOwner(address caller) public { 9 | vm.assume(caller != owner && caller != address(0)); 10 | vm.expectRevert(Ownable.Unauthorized.selector); 11 | vm.prank(caller); 12 | validator.setSigner(caller); 13 | } 14 | 15 | function test_allowsTheOwner_toUpdateTheSigner() public { 16 | vm.prank(owner); 17 | address newSigner = makeAddr("new"); 18 | validator.setSigner(newSigner); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/discounts/ERC1155DiscountValidator/ERC1155DiscountValidatorBase.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {ERC1155DiscountValidator} from "src/L2/discounts/ERC1155DiscountValidator.sol"; 6 | import {MockERC1155} from "test/mocks/MockERC1155.sol"; 7 | 8 | contract ERC1155DiscountValidatorBase is Test { 9 | ERC1155DiscountValidator validator; 10 | MockERC1155 token; 11 | uint256 tokenId = 1; 12 | address userA = makeAddr("userA"); 13 | address userB = makeAddr("userB"); 14 | 15 | function setUp() public { 16 | token = new MockERC1155(); 17 | validator = new ERC1155DiscountValidator(address(token), tokenId); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/discounts/ERC1155DiscountValidator/IsValidDiscountRegistration.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ERC1155DiscountValidatorBase} from "./ERC1155DiscountValidatorBase.t.sol"; 5 | 6 | contract IsValidDiscountRegistration is ERC1155DiscountValidatorBase { 7 | function test_returnsFalse_whenTheClaimerDoesNotHaveTheToken() public view { 8 | assertFalse(validator.isValidDiscountRegistration(userA, "")); 9 | } 10 | 11 | function test_returnsFalse_whenAnotherUserHasTheToken() public { 12 | token.mint(userA, tokenId, 1); 13 | assertFalse(validator.isValidDiscountRegistration(userB, "")); 14 | } 15 | 16 | function test_returnsTrue_whenTheUserHasTheToken() public { 17 | token.mint(userA, tokenId, 1); 18 | assertTrue(validator.isValidDiscountRegistration(userA, "")); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/discounts/ERC1155DiscountValidatorV2/ERC1155DiscountValidatorV2Base.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {ERC1155DiscountValidatorV2} from "src/L2/discounts/ERC1155DiscountValidatorV2.sol"; 6 | import {MockERC1155} from "test/mocks/MockERC1155.sol"; 7 | 8 | contract ERC1155DiscountValidatorV2Base is Test { 9 | ERC1155DiscountValidatorV2 validator; 10 | MockERC1155 token; 11 | uint256 firstValidTokenId = 1; 12 | uint256 secondValidTokenId = 2; 13 | uint256 invalidTokenId = type(uint256).max; 14 | address userA = makeAddr("userA"); 15 | address userB = makeAddr("userB"); 16 | 17 | function setUp() public { 18 | token = new MockERC1155(); 19 | uint256[] memory validTokens = new uint256[](2); 20 | validTokens[0] = firstValidTokenId; 21 | validTokens[1] = secondValidTokenId; 22 | validator = new ERC1155DiscountValidatorV2(address(token), validTokens); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/discounts/ERC721DiscountValidator/ERC721DiscountValidatorBase.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {ERC721DiscountValidator} from "src/L2/discounts/ERC721DiscountValidator.sol"; 6 | import {MockERC721} from "test/mocks/MockERC721.sol"; 7 | 8 | contract ERC721DiscountValidatorBase is Test { 9 | ERC721DiscountValidator validator; 10 | MockERC721 token; 11 | address userA = makeAddr("userA"); 12 | address userB = makeAddr("userB"); 13 | 14 | function setUp() public { 15 | token = new MockERC721(); 16 | validator = new ERC721DiscountValidator(address(token)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/discounts/ERC721DiscountValidator/IsValidDiscountRegistration.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ERC721DiscountValidatorBase} from "./ERC721DiscountValidatorBase.t.sol"; 5 | 6 | contract IsValidDiscountRegistration is ERC721DiscountValidatorBase { 7 | function test_returnsFalse_whenTheClaimerDoesNotHaveTheToken() public view { 8 | assertFalse(validator.isValidDiscountRegistration(userA, "")); 9 | } 10 | 11 | function test_returnsFalse_whenAnotherUserHasTheToken() public { 12 | token.mint(userA, 1); 13 | assertFalse(validator.isValidDiscountRegistration(userB, "")); 14 | } 15 | 16 | function test_returnsTrue_whenTheUserHasTheToken() public { 17 | token.mint(userA, 1); 18 | assertTrue(validator.isValidDiscountRegistration(userA, "")); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/discounts/TalenProtocolDiscountValidator/IsValidDiscountRegistration.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {TalentProtocolDiscountValidatorBase} from "./TalentProtocolDiscountValidatorBase.t.sol"; 5 | 6 | contract IsValidDiscountRegistration is TalentProtocolDiscountValidatorBase { 7 | function test_returnsTrue_whenTheScoreMeetsTheThreshold() public { 8 | talent.setScore(threshold); 9 | bool ret = validator.isValidDiscountRegistration(userA, ""); 10 | assertTrue(ret); 11 | } 12 | 13 | function test_returnsTrue_whenTheScoreExceedsTheThreshold() public { 14 | talent.setScore(threshold + 1); 15 | bool ret = validator.isValidDiscountRegistration(userA, ""); 16 | assertTrue(ret); 17 | } 18 | 19 | function test_returnsFalse_whenTheScoreIsBelowTheThreshold() public { 20 | talent.setScore(threshold - 1); 21 | bool ret = validator.isValidDiscountRegistration(userA, ""); 22 | assertFalse(ret); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/discounts/TalenProtocolDiscountValidator/SetThreshold.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Ownable} from "solady/auth/Ownable.sol"; 5 | import {TalentProtocolDiscountValidatorBase} from "./TalentProtocolDiscountValidatorBase.t.sol"; 6 | 7 | contract SetThreshold is TalentProtocolDiscountValidatorBase { 8 | function test_reverts_whenCalledByNonOwner() public { 9 | vm.prank(userA); 10 | vm.expectRevert(abi.encodeWithSelector(Ownable.Unauthorized.selector)); 11 | validator.setThreshold(0); 12 | } 13 | 14 | function test_allowsOwnerToSetThreshold() public { 15 | vm.prank(owner); 16 | validator.setThreshold(1); 17 | assertEq(1, validator.threshold()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/discounts/TalenProtocolDiscountValidator/TalentProtocolDiscountValidatorBase.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {TalentProtocolDiscountValidator} from "src/L2/discounts/TalentProtocolDiscountValidator.sol"; 6 | import {MockBuilderScorePassport} from "test/mocks/MockBuilderScorePassport.sol"; 7 | 8 | contract TalentProtocolDiscountValidatorBase is Test { 9 | MockBuilderScorePassport talent; 10 | TalentProtocolDiscountValidator validator; 11 | address owner = makeAddr("owner"); 12 | address userA = makeAddr("userA"); 13 | address userB = makeAddr("userB"); 14 | 15 | uint256 threshold = 50; 16 | 17 | function setUp() public { 18 | talent = new MockBuilderScorePassport(threshold); 19 | validator = new TalentProtocolDiscountValidator(owner, address(talent), threshold); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/mocks/MockAddrResolver.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {AddrResolver} from "ens-contracts/resolvers/profiles/AddrResolver.sol"; 5 | 6 | contract MockAddrResolver is AddrResolver { 7 | function isAuthorised(bytes32) internal pure override returns (bool) { 8 | return true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/mocks/MockAttestationIndexer.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | contract MockAttestationIndexer { 5 | bytes32 uid; 6 | 7 | constructor(bytes32 uid_) { 8 | uid = uid_; 9 | } 10 | 11 | function getAttestationUid(address, bytes32) external view returns (bytes32) { 12 | return uid; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/mocks/MockBaseRegistrar.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | contract MockBaseRegistrar { 5 | mapping(uint256 => bool) availability; 6 | mapping(uint256 => uint256) expiries; 7 | 8 | function setAvailable(uint256 label, bool available_) external { 9 | availability[label] = available_; 10 | } 11 | 12 | function isAvailable(uint256 label) external view returns (bool) { 13 | return availability[label]; 14 | } 15 | 16 | function setNameExpires(uint256 label, uint256 expiry) external { 17 | expiries[label] = expiry; 18 | } 19 | 20 | function nameExpires(uint256 label) external view returns (uint256) { 21 | return expiries[label]; 22 | } 23 | 24 | function registerWithRecord(uint256 label, address, uint256, address, uint64) external view returns (uint256) { 25 | return expiries[label]; 26 | } 27 | 28 | function renew(uint256 label, uint256 duration) external returns (uint256) { 29 | return expiries[label] += duration; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/mocks/MockBuilderScorePassport.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {TalentProtocol} from "src/L2/discounts/TalentProtocolDiscountValidator.sol"; 5 | 6 | contract MockBuilderScorePassport is TalentProtocol { 7 | uint256 score; 8 | 9 | constructor(uint256 score_) { 10 | score = score_; 11 | } 12 | 13 | function getScoreByAddress(address) external view returns (uint256) { 14 | return score; 15 | } 16 | 17 | function setScore(uint256 score_) external { 18 | score = score_; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/mocks/MockDiscountValidator.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import "src/L2/interface/IDiscountValidator.sol"; 5 | 6 | contract MockDiscountValidator is IDiscountValidator { 7 | bool returnValue = true; 8 | 9 | function isValidDiscountRegistration(address, bytes calldata) external view returns (bool) { 10 | return returnValue; 11 | } 12 | 13 | function setReturnValue(bool value) external { 14 | returnValue = value; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/mocks/MockEAS.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Attestation} from "eas-contracts/EAS.sol"; 5 | 6 | contract MockEAS { 7 | bytes32 schema; 8 | Attestation att; 9 | 10 | function setAttestattion(Attestation memory att_) public { 11 | att = att_; 12 | } 13 | 14 | function getAttestation(bytes32) external view returns (Attestation memory) { 15 | return att; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/mocks/MockERC1155.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; 5 | 6 | contract MockERC1155 is ERC1155 { 7 | constructor() ERC1155("") {} 8 | 9 | function mint(address to, uint256 id, uint256 value) public { 10 | _mint(to, id, value, ""); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/mocks/MockERC721.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 5 | 6 | contract MockERC721 is ERC721 { 7 | constructor() ERC721("", "") {} 8 | 9 | function mint(address to, uint256 id) public { 10 | _mint(to, id); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/mocks/MockL2ReverseRegistrar.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | contract MockL2ReverseRegistrar { 5 | bool public hasClaimed; 6 | 7 | function setNameForAddrWithSignature(address, uint256, string memory, uint256[] memory, bytes memory) external { 8 | hasClaimed = true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/mocks/MockNameResolver.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {NameResolver} from "ens-contracts/resolvers/profiles/NameResolver.sol"; 5 | 6 | contract MockNameResolver is NameResolver { 7 | function isAuthorised(bytes32) internal pure override returns (bool) { 8 | return true; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/mocks/MockNameWrapper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | contract MockNameWrapper { 5 | mapping(uint256 => uint256) expiries; 6 | mapping(string => bool) public hasRegistered; 7 | 8 | function setExpiry(uint256 id, uint256 expiry) external { 9 | expiries[id] = expiry; 10 | } 11 | 12 | function renew(uint256 id, uint256 duration) external returns (uint256) { 13 | expiries[id] += duration; 14 | return expiries[id]; 15 | } 16 | 17 | function registerAndWrapETH2LD(string memory name, address, uint256 duration, address, uint16) 18 | external 19 | returns (uint256) 20 | { 21 | hasRegistered[name] = true; 22 | return block.timestamp + duration; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/mocks/MockOwnedContract.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Ownable} from "solady/auth/Ownable.sol"; 5 | 6 | contract MockOwnedContract is Ownable { 7 | constructor(address owner) { 8 | _initializeOwner(owner); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/mocks/MockPriceOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {IPriceOracle} from "src/L2/interface/IPriceOracle.sol"; 5 | import {GRACE_PERIOD} from "src/util/Constants.sol"; 6 | 7 | contract MockPriceOracle is IPriceOracle { 8 | uint256 public constant DEFAULT_BASE_WEI = 0.1 ether; 9 | uint256 public constant DEFAULT_PREMIUM_WEI = 0; 10 | uint256 public constant DEFAULT_INCLUDED_PREMIUM = 0.2 ether; 11 | 12 | IPriceOracle.Price public defaultPrice = IPriceOracle.Price({base: DEFAULT_BASE_WEI, premium: DEFAULT_PREMIUM_WEI}); 13 | 14 | mapping(string => IPriceOracle.Price) prices; 15 | 16 | function setPrice(string calldata name, IPriceOracle.Price calldata priceData) external { 17 | prices[name] = priceData; 18 | } 19 | 20 | function price(string calldata name, uint256 expires, uint256 duration) 21 | external 22 | view 23 | returns (IPriceOracle.Price memory) 24 | { 25 | if (prices[name].base > 0) return prices[name]; 26 | if ( 27 | (expires == block.timestamp + duration + GRACE_PERIOD) || (expires == block.timestamp + duration) 28 | || expires == 0 29 | ) { 30 | return defaultPrice; 31 | } 32 | return IPriceOracle.Price({base: DEFAULT_BASE_WEI, premium: DEFAULT_INCLUDED_PREMIUM}); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/mocks/MockPublicResolver.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {BASE_ETH_NODE} from "src/util/Constants.sol"; 5 | import {ExtendedResolver} from "ens-contracts/resolvers/profiles/ExtendedResolver.sol"; 6 | import {IAddrResolver} from "ens-contracts/resolvers/profiles/IAddrResolver.sol"; 7 | import {ITextResolver} from "ens-contracts/resolvers/profiles/ITextResolver.sol"; 8 | 9 | import "forge-std/console.sol"; 10 | 11 | contract MockPublicResolver is ExtendedResolver { 12 | mapping(bytes32 => address) addrs; 13 | mapping(bytes32 => mapping(string => string)) texts; 14 | address public constant ADDRESS = 0x000000000000000000000000000000000000dEaD; 15 | string public constant TEST_TEXT = "pass"; 16 | bytes public firstBytes; 17 | 18 | constructor() { 19 | addrs[BASE_ETH_NODE] = ADDRESS; 20 | texts[BASE_ETH_NODE]["test"] = TEST_TEXT; 21 | } 22 | 23 | function addr(bytes32 node) external view returns (address) { 24 | return addrs[node]; 25 | } 26 | 27 | function text(bytes32 node, string calldata key) external view returns (string memory) { 28 | return texts[node][key]; 29 | } 30 | 31 | function multicallWithNodeCheck(bytes32, bytes[] memory data) external returns (bytes[] memory results) { 32 | firstBytes = data[0]; 33 | return data; 34 | } 35 | 36 | function supportsInterface(bytes4 interfaceID) public pure returns (bool) { 37 | return interfaceID == type(IAddrResolver).interfaceId || interfaceID == type(ITextResolver).interfaceId; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/mocks/MockRegistrarController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | contract MockRegistrarController { 5 | mapping(address => bool) hasRegistered; 6 | uint256 public launchTime; 7 | 8 | mapping(address => bool) public discountedRegistrants; 9 | 10 | constructor(uint256 launchTime_) { 11 | launchTime = launchTime_; 12 | } 13 | 14 | function hasRegisteredWithDiscount(address[] memory addresses) external view returns (bool) { 15 | for (uint256 i; i < addresses.length; i++) { 16 | if (hasRegistered[addresses[i]]) { 17 | return true; 18 | } 19 | } 20 | return false; 21 | } 22 | 23 | function setHasRegisteredWithDiscount(address addr, bool status) external { 24 | hasRegistered[addr] = status; 25 | discountedRegistrants[addr] = status; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/mocks/MockReverseRegistrar.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | contract MockReverseRegistrar { 5 | struct MockReverseRecord { 6 | address addr; 7 | address owner; 8 | address resolver; 9 | string name; 10 | } 11 | 12 | mapping(address => bool) public hasClaimed; 13 | MockReverseRecord public record; 14 | 15 | function claim(address claimant) external { 16 | hasClaimed[claimant] = true; 17 | } 18 | 19 | function setNameForAddr(address addr, address owner, address resolver, string memory name) 20 | external 21 | returns (bytes32) 22 | { 23 | record = MockReverseRecord({addr: addr, owner: owner, resolver: resolver, name: name}); 24 | hasClaimed[owner] = true; 25 | return bytes32(0); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/mocks/MockReverseRegistrarV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | contract MockReverseRegistrarV2 { 5 | struct MockReverseRecord { 6 | address addr; 7 | string name; 8 | } 9 | 10 | mapping(address => bool) public hasClaimed; 11 | MockReverseRecord public record; 12 | 13 | function claim(address claimant) external { 14 | hasClaimed[claimant] = true; 15 | } 16 | 17 | function setNameForAddrWithSignature(address addr, uint256, string calldata name, uint256[] memory, bytes memory) 18 | external 19 | returns (bytes32) 20 | { 21 | record = MockReverseRecord({addr: addr, name: name}); 22 | hasClaimed[addr] = true; 23 | return bytes32(0); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/mocks/MockUSDC.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {ERC20} from "solady/tokens/ERC20.sol"; 5 | 6 | contract MockUSDC is ERC20 { 7 | /// @dev Returns the name of the token. 8 | function name() public pure override returns (string memory) { 9 | return "USD Coin"; 10 | } 11 | 12 | /// @dev Returns the symbol of the token. 13 | function symbol() public pure override returns (string memory) { 14 | return "USDC"; 15 | } 16 | 17 | /// @dev Returns the decimals places of the token. 18 | function decimals() public pure override returns (uint8) { 19 | return 6; 20 | } 21 | 22 | function mint(address to, uint256 amount) public { 23 | _mint(to, amount); 24 | } 25 | 26 | function burn(address from, uint256 amount) public { 27 | _burn(from, amount); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/mocks/TestnetDiscountValidator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.23; 3 | 4 | import {IDiscountValidator} from "src/L2/interface/IDiscountValidator.sol"; 5 | 6 | contract TestnetDiscountValidator is IDiscountValidator { 7 | function isValidDiscountRegistration(address, bytes calldata) external pure returns (bool) { 8 | return true; 9 | } 10 | } 11 | --------------------------------------------------------------------------------