├── .env.template ├── .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-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 ├── foundry.toml ├── py ├── BNS.py ├── Price.py ├── README.md ├── compute_premium.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 │ ├── 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 │ ├── RegistrarController.sol │ ├── Registry.sol │ ├── ReverseRegistrar.sol │ ├── ReverseRegistrarV2.sol │ ├── StablePriceOracle.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 ├── 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 ├── Integration ├── 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 ├── 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 ├── 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 /.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 | -------------------------------------------------------------------------------- /.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 | 11 | jobs: 12 | forge-test: 13 | name: Run Forge Tests and Checks 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | submodules: recursive 19 | 20 | - name: Set up Python 21 | uses: actions/setup-python@v5 22 | with: 23 | python-version: '3.8' 24 | 25 | - name: Install eth-abi 26 | run: | 27 | python3 -m pip install --upgrade pip 28 | python3 -m pip install eth-abi 29 | 30 | - name: Install Foundry 31 | uses: foundry-rs/foundry-toolchain@v1 32 | with: 33 | version: stable 34 | 35 | - name: Run Forge build 36 | run: | 37 | forge --version 38 | id: build 39 | 40 | - name: Run Forge tests 41 | run: | 42 | forge test -vvv --ffi 43 | id: test 44 | 45 | - name: Check formatting 46 | run: | 47 | forge fmt --check 48 | id: fmt 49 | -------------------------------------------------------------------------------- /.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* -------------------------------------------------------------------------------- /.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/e6bd1419038496b1c28107493584d935a9b5c17f/audit/final-report-cantinacode-coinbase-sbr-usernames-review.pdf -------------------------------------------------------------------------------- /audit/report-cantinacode-coinbase-sbr-usernames-review-2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/base/basenames/e6bd1419038496b1c28107493584d935a9b5c17f/audit/report-cantinacode-coinbase-sbr-usernames-review-2.pdf -------------------------------------------------------------------------------- /broadcast/DeployL1Resolver.s.sol/11155111/run-1715636990.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactions": [ 3 | { 4 | "hash": "0x5385b5f8b2376ca4d7888e7b6d7f429ca4e88640292e38ebddfd3bfd9959063e", 5 | "transactionType": "CALL", 6 | "contractName": null, 7 | "contractAddress": "0x5F15c3B5949F5767F5Ca9013a8E4Ca4D97a053eD", 8 | "function": "setUrl(string)", 9 | "arguments": [ 10 | "\"http://localhost:8000/api/v1/domain/resolver/resolveDomain/{sender}/{data}\"" 11 | ], 12 | "transaction": { 13 | "type": "0x02", 14 | "from": "0x869140c91edf43214a3ecedeaf777cc7107ad71a", 15 | "to": "0x5f15c3b5949f5767f5ca9013a8e4ca4d97a053ed", 16 | "gas": "0x10736", 17 | "value": "0x0", 18 | "data": "0x252498a20000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004a687474703a2f2f6c6f63616c686f73743a383030302f6170692f76312f646f6d61696e2f7265736f6c7665722f7265736f6c7665446f6d61696e2f7b73656e6465727d2f7b646174617d00000000000000000000000000000000000000000000", 19 | "nonce": "0x2", 20 | "accessList": [] 21 | }, 22 | "additionalContracts": [], 23 | "isFixedGasLimit": false 24 | } 25 | ], 26 | "receipts": [ 27 | { 28 | "transactionHash": "0x5385b5f8b2376ca4d7888e7b6d7f429ca4e88640292e38ebddfd3bfd9959063e", 29 | "transactionIndex": "0x3", 30 | "blockHash": "0x5403573a93adc83ab9b82706d77d82c504b7e8b38921132f0827ee2de56fe02f", 31 | "blockNumber": "0x59fb42", 32 | "from": "0x869140c91eDF43214a3EcEdEAF777cc7107aD71a", 33 | "to": "0x5F15c3B5949F5767F5Ca9013a8E4Ca4D97a053eD", 34 | "cumulativeGasUsed": "0x635df", 35 | "gasUsed": "0xb3fa", 36 | "contractAddress": null, 37 | "logs": [], 38 | "status": "0x1", 39 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 40 | "type": "0x2", 41 | "effectiveGasPrice": "0x433be5a18" 42 | } 43 | ], 44 | "libraries": [], 45 | "pending": [], 46 | "returns": {}, 47 | "timestamp": 1715636990, 48 | "chain": 11155111, 49 | "commit": "5f0dc54" 50 | } -------------------------------------------------------------------------------- /broadcast/SetCBIdTreeRoot.s.sol/84532/run-1719442265.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactions": [ 3 | { 4 | "hash": "0x8a876691e2eb6a7f57f283d025d91818dbe708731462b5e074d73762e7fe7fd1", 5 | "transactionType": "CALL", 6 | "contractName": null, 7 | "contractAddress": "0x1079ef978d3c2a6cd4db142118d3c904e0ac4fc7", 8 | "function": "setRoot(bytes32)", 9 | "arguments": [ 10 | "0x8cbc28e840d1cd2accddec6592a4bffce2ca38c6c559c252504f342dd46acbb6" 11 | ], 12 | "transaction": { 13 | "from": "0x63e216601b3588a5b54d9f961cffc4af916a63c7", 14 | "to": "0x1079ef978d3c2a6cd4db142118d3c904e0ac4fc7", 15 | "gas": "0x9cab", 16 | "value": "0x0", 17 | "input": "0xdab5f3408cbc28e840d1cd2accddec6592a4bffce2ca38c6c559c252504f342dd46acbb6", 18 | "nonce": "0x9", 19 | "chainId": "0x14a34" 20 | }, 21 | "additionalContracts": [], 22 | "isFixedGasLimit": false 23 | } 24 | ], 25 | "receipts": [ 26 | { 27 | "status": "0x1", 28 | "cumulativeGasUsed": "0x18839b", 29 | "logs": [], 30 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 31 | "type": "0x2", 32 | "transactionHash": "0x8a876691e2eb6a7f57f283d025d91818dbe708731462b5e074d73762e7fe7fd1", 33 | "transactionIndex": "0x8", 34 | "blockHash": "0x4e341cd72456eff751f71943c6466a796e04fef5acc71b889bc5f896c0d29081", 35 | "blockNumber": "0xb49e3a", 36 | "gasUsed": "0x716e", 37 | "effectiveGasPrice": "0x1692c2", 38 | "from": "0x63e216601b3588a5b54d9f961cffc4af916a63c7", 39 | "to": "0x1079ef978d3c2a6cd4db142118d3c904e0ac4fc7", 40 | "contractAddress": null, 41 | "l1BaseFeeScalar": "0x44d", 42 | "l1BlobBaseFee": "0x1", 43 | "l1BlobBaseFeeScalar": "0xa118b", 44 | "l1Fee": "0x6e70bac6", 45 | "l1GasPrice": "0x3eb175bd", 46 | "l1GasUsed": "0x640" 47 | } 48 | ], 49 | "libraries": [], 50 | "pending": [], 51 | "returns": {}, 52 | "timestamp": 1719442265, 53 | "chain": 84532, 54 | "commit": "fbad20c" 55 | } -------------------------------------------------------------------------------- /broadcast/SetCBIdTreeRoot.s.sol/84532/run-latest.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactions": [ 3 | { 4 | "hash": "0x8a876691e2eb6a7f57f283d025d91818dbe708731462b5e074d73762e7fe7fd1", 5 | "transactionType": "CALL", 6 | "contractName": null, 7 | "contractAddress": "0x1079ef978d3c2a6cd4db142118d3c904e0ac4fc7", 8 | "function": "setRoot(bytes32)", 9 | "arguments": [ 10 | "0x8cbc28e840d1cd2accddec6592a4bffce2ca38c6c559c252504f342dd46acbb6" 11 | ], 12 | "transaction": { 13 | "from": "0x63e216601b3588a5b54d9f961cffc4af916a63c7", 14 | "to": "0x1079ef978d3c2a6cd4db142118d3c904e0ac4fc7", 15 | "gas": "0x9cab", 16 | "value": "0x0", 17 | "input": "0xdab5f3408cbc28e840d1cd2accddec6592a4bffce2ca38c6c559c252504f342dd46acbb6", 18 | "nonce": "0x9", 19 | "chainId": "0x14a34" 20 | }, 21 | "additionalContracts": [], 22 | "isFixedGasLimit": false 23 | } 24 | ], 25 | "receipts": [ 26 | { 27 | "status": "0x1", 28 | "cumulativeGasUsed": "0x18839b", 29 | "logs": [], 30 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 31 | "type": "0x2", 32 | "transactionHash": "0x8a876691e2eb6a7f57f283d025d91818dbe708731462b5e074d73762e7fe7fd1", 33 | "transactionIndex": "0x8", 34 | "blockHash": "0x4e341cd72456eff751f71943c6466a796e04fef5acc71b889bc5f896c0d29081", 35 | "blockNumber": "0xb49e3a", 36 | "gasUsed": "0x716e", 37 | "effectiveGasPrice": "0x1692c2", 38 | "from": "0x63e216601b3588a5b54d9f961cffc4af916a63c7", 39 | "to": "0x1079ef978d3c2a6cd4db142118d3c904e0ac4fc7", 40 | "contractAddress": null, 41 | "l1BaseFeeScalar": "0x44d", 42 | "l1BlobBaseFee": "0x1", 43 | "l1BlobBaseFeeScalar": "0xa118b", 44 | "l1Fee": "0x6e70bac6", 45 | "l1GasPrice": "0x3eb175bd", 46 | "l1GasUsed": "0x640" 47 | } 48 | ], 49 | "libraries": [], 50 | "pending": [], 51 | "returns": {}, 52 | "timestamp": 1719442265, 53 | "chain": 84532, 54 | "commit": "fbad20c" 55 | } -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = "src" 3 | out = "out" 4 | libs = ["lib"] 5 | remappings = [ 6 | "@ensdomains/buffer/=lib/buffer", 7 | "solady/=lib/solady/src/", 8 | "forge-std/=lib/forge-std/src/", 9 | "ens-contracts/=lib/ens-contracts/contracts/", 10 | "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", 11 | "openzeppelin-contracts/=lib/openzeppelin-contracts", 12 | "eas-contracts/=lib/eas-contracts/contracts/", 13 | "verifications/=lib/verifications/src", 14 | "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/" 15 | ] 16 | fs_permissions = [{access = "read", path = "./script/premint/"}] 17 | auto_detect_remappings = false 18 | 19 | [rpc_endpoints] 20 | sepolia="${SEPOLIA_RPC_URL}" 21 | base-sepolia="${BASE_SEPOLIA_RPC_URL}" 22 | 23 | [etherscan] 24 | sepolia={url = "https://api-sepolia.etherscan.io/api", key = "${ETHERSCAN_API_KEY}"} 25 | base-sepolia={url = "https://api-sepolia.basescan.org/api", key = "${BASE_ETHERSCAN_API_KEY}"} 26 | -------------------------------------------------------------------------------- /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/RegisterNewName.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 | import {L2Resolver} from "src/L2/L2Resolver.sol"; 7 | import {TextResolver} from "src/L2/L2Resolver.sol"; 8 | import "src/util/Constants.sol"; 9 | import "ens-contracts/utils/NameEncoder.sol"; 10 | import "solady/utils/LibString.sol"; 11 | 12 | interface AddrResolver { 13 | function setAddr(bytes32 node, address addr) external; 14 | } 15 | 16 | contract RegisterNewName is Script { 17 | // NAME AND RECORD DEFS ///////////////////////////// 18 | string NAME = "steve"; 19 | uint256 duration = 365 days; 20 | address RESOLVED_ADDR = 0xB18e4C959bccc8EF86D78DC297fb5efA99550d85; 21 | bytes32 discountKey = keccak256("testnet.discount.validator"); 22 | string textKey = "amicool"; 23 | string textValue = "yes"; 24 | ///////////////////////////////////////////////////// 25 | 26 | function run() external { 27 | uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); 28 | vm.startBroadcast(deployerPrivateKey); 29 | 30 | address controllerAddr = vm.envAddress("REGISTRAR_CONTROLLER_ADDR"); 31 | RegistrarController controller = RegistrarController(controllerAddr); 32 | address resolverAddr = vm.envAddress("L2_RESOLVER_ADDR"); // l2 resolver 33 | 34 | RegistrarController.RegisterRequest memory request = RegistrarController.RegisterRequest({ 35 | name: NAME, 36 | owner: RESOLVED_ADDR, 37 | duration: duration, 38 | resolver: resolverAddr, 39 | data: _packResolverData(), 40 | reverseRecord: false 41 | }); 42 | 43 | controller.discountedRegister(request, discountKey, ""); 44 | 45 | vm.stopBroadcast(); 46 | } 47 | 48 | function _packResolverData() internal view returns (bytes[] memory) { 49 | (, bytes32 rootNode) = NameEncoder.dnsEncodeName("basetest.eth"); 50 | bytes32 label = keccak256(bytes(NAME)); 51 | bytes32 nodehash = keccak256(abi.encodePacked(rootNode, label)); 52 | 53 | bytes memory addrData = abi.encodeWithSelector(AddrResolver.setAddr.selector, nodehash, RESOLVED_ADDR); 54 | bytes memory textData = abi.encodeWithSelector(TextResolver.setText.selector, nodehash, textKey, textValue); 55 | bytes[] memory data = new bytes[](2); 56 | data[0] = addrData; 57 | data[1] = textData; 58 | return data; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /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/IBaseRegistrar.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | interface IBaseRegistrar { 5 | event ControllerAdded(address indexed controller); 6 | event ControllerRemoved(address indexed controller); 7 | event NameMigrated(uint256 indexed id, address indexed owner, uint256 expires); 8 | event NameRegistered(uint256 indexed id, address indexed owner, uint256 expires); 9 | event NameRenewed(uint256 indexed id, uint256 expires); 10 | 11 | // Authorises a controller, who can register and renew domains. 12 | function addController(address controller) external; 13 | 14 | /// @notice Returns true if the specified name is available for registration. 15 | /// 16 | /// @param id The id of the name to check availability of. 17 | /// 18 | /// @return `true` if the name is available, else `false`. 19 | function isAvailable(uint256 id) external view returns (bool); 20 | 21 | // Revoke controller permission for an address. 22 | function removeController(address controller) external; 23 | 24 | // Set the resolver for the TLD this registrar manages. 25 | function setResolver(address resolver) external; 26 | 27 | // Returns the expiration timestamp of the specified label hash. 28 | function nameExpires(uint256 id) external view returns (uint256); 29 | 30 | // Returns true if the specified name is available for registration. 31 | function available(uint256 id) external view returns (bool); 32 | 33 | /** 34 | * @dev Register a name. 35 | */ 36 | function register(uint256 id, address owner, uint256 duration) external returns (uint256); 37 | 38 | /// @notice Register a name and add details to the record in the Registry. 39 | /// 40 | /// @param id The token id determined by keccak256(label). 41 | /// @param owner The address that should own the registration. 42 | /// @param duration Duration in seconds for the registration. 43 | /// @param resolver Address of the resolver for the name. 44 | /// @param ttl Time-to-live for the name. 45 | function registerWithRecord(uint256 id, address owner, uint256 duration, address resolver, uint64 ttl) 46 | external 47 | returns (uint256); 48 | 49 | function renew(uint256 id, uint256 duration) external returns (uint256); 50 | 51 | /** 52 | * @dev Reclaim ownership of a name in ENS, if you own it in the registrar. 53 | */ 54 | function reclaim(uint256 id, address owner) external; 55 | } 56 | -------------------------------------------------------------------------------- /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/IL2ReverseRegistrar.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | /// @notice Interface for the L2 Reverse Registrar. 5 | /// https://github.com/ensdomains/ens-contracts/tree/feature/simplify-reverse-resolver 6 | interface IL2ReverseRegistrar { 7 | /// @notice Sets the `nameForAddr()` record for the calling account. 8 | /// 9 | /// @param name The name to set. 10 | function setName(string memory name) external; 11 | 12 | /// @notice Sets the `nameForAddr()` record for the addr provided account. 13 | /// 14 | /// @param addr The address to set the name for. 15 | /// @param name The name to set. 16 | function setNameForAddr(address addr, string memory name) external; 17 | 18 | /// @notice Sets the `nameForAddr()` record for the addr provided account using a signature. 19 | /// 20 | /// @param addr The address to set the name for. 21 | /// @param signatureExpiry Date when the signature expires. 22 | /// @param name The name to set. 23 | /// @param coinTypes The coin types to set. Must be inclusive of the coin type for the contract. 24 | /// @param signature The signature from the addr. 25 | function setNameForAddrWithSignature( 26 | address addr, 27 | uint256 signatureExpiry, 28 | string memory name, 29 | uint256[] memory coinTypes, 30 | bytes memory signature 31 | ) external; 32 | 33 | /// @notice Sets the `nameForAddr()` record for the contract provided that is owned with `Ownable`. 34 | /// 35 | /// @param contractAddr The address of the contract to set the name for (implementing Ownable). 36 | /// @param owner The owner of the contract (via Ownable). 37 | /// @param signatureExpiry The expiry of the signature. 38 | /// @param name The name to set. 39 | /// @param coinTypes The coin types to set. Must be inclusive of the coin type for the contract. 40 | /// @param signature The signature of an address that will return true on isValidSignature for the owner. 41 | function setNameForOwnableWithSignature( 42 | address contractAddr, 43 | address owner, 44 | uint256 signatureExpiry, 45 | string memory name, 46 | uint256[] memory coinTypes, 47 | bytes memory signature 48 | ) external; 49 | } 50 | -------------------------------------------------------------------------------- /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/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 {ExponentialPremiumOracleBase} from "./ExponentialPremiumOracleBase.t.sol"; 6 | import "solady/utils/FixedPointMathLib.sol"; 7 | 8 | contract ExponentialPremiumFuzzTest is ExponentialPremiumOracleBase { 9 | function test_decayedPremium_decreasingPrices(uint256 elapsed) public view { 10 | vm.assume(elapsed <= 365 days); 11 | uint256 actualPremium = oracle.decayedPremium(elapsed); 12 | assert(actualPremium <= startPremium); 13 | } 14 | 15 | function test_decayedPremium_boundaryValues(uint256 elapsed) public view { 16 | uint256[] memory boundaryValues = new uint256[](3); 17 | boundaryValues[0] = 0; 18 | boundaryValues[1] = 1 days; 19 | boundaryValues[2] = 365 days; 20 | 21 | for (uint256 i = 0; i < boundaryValues.length; i++) { 22 | elapsed = boundaryValues[i]; 23 | uint256 actualPremium = oracle.decayedPremium(elapsed); 24 | assert(actualPremium <= startPremium); 25 | } 26 | } 27 | 28 | function test_decayedPremium_alwaysDecreasing(uint256 elapsed1, uint256 elapsed2) public view { 29 | vm.assume(elapsed1 <= elapsed2); 30 | vm.assume(elapsed2 <= 365 days); 31 | 32 | uint256 premium1 = oracle.decayedPremium(elapsed1); 33 | uint256 premium2 = oracle.decayedPremium(elapsed2); 34 | 35 | assert(premium1 >= premium2); 36 | } 37 | 38 | function test_decayedPremium_accuracy(uint256 elapsed) public { 39 | uint256 bound = 400 * 1 days; 40 | vm.assume(elapsed <= bound); 41 | string[] memory input = new string[](4); 42 | input[0] = "python3"; 43 | input[1] = "py/compute_premium.py"; 44 | input[2] = vm.toString(startPremium); 45 | input[3] = vm.toString(elapsed); 46 | uint256 result = oracle.decayedPremium(elapsed); 47 | bytes memory res = vm.ffi(input); 48 | uint256 expected = abi.decode(res, (uint256)); 49 | uint256 leftBound = (expected * (999)) / 1000; 50 | uint256 rightBound = (expected * (1001)) / 1000; 51 | bool withinBounds = (leftBound <= result && result <= rightBound); // Checking accuracy within 0.1 percent of the expected result 52 | assertTrue(withinBounds); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /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/L1Resolver/AdminMethods.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 | 10 | contract AdminMethods is L1ResolverTestBase { 11 | function test_setUrl(string memory newUrl) public { 12 | vm.prank(makeAddr("0x2")); 13 | vm.expectRevert(abi.encodeWithSelector(Ownable.Unauthorized.selector)); 14 | resolver.setUrl(newUrl); 15 | 16 | vm.prank(owner); 17 | vm.expectEmit(); 18 | emit L1Resolver.UrlChanged(newUrl); 19 | resolver.setUrl(newUrl); 20 | } 21 | 22 | function test_addSigners(address[] calldata _signers) public { 23 | vm.assume(_signers.length < 10); 24 | for (uint256 i; i < _signers.length; i++) { 25 | vm.assume(_signers[i] != address(0)); 26 | } 27 | 28 | vm.prank(makeAddr("0x2")); 29 | vm.expectRevert(abi.encodeWithSelector(Ownable.Unauthorized.selector)); 30 | resolver.addSigners(_signers); 31 | 32 | vm.prank(owner); 33 | vm.expectEmit(); 34 | emit L1Resolver.AddedSigners(_signers); 35 | resolver.addSigners(_signers); 36 | for (uint256 i; i < _signers.length; i++) { 37 | assertTrue(resolver.signers(_signers[i])); 38 | } 39 | } 40 | 41 | function test_removeSigner() public { 42 | vm.prank(makeAddr("0x2")); 43 | vm.expectRevert(abi.encodeWithSelector(Ownable.Unauthorized.selector)); 44 | resolver.removeSigner(signer); 45 | 46 | assertTrue(resolver.signers(signer)); 47 | vm.prank(owner); 48 | vm.expectEmit(); 49 | emit L1Resolver.RemovedSigner(signer); 50 | resolver.removeSigner(signer); 51 | assertFalse(resolver.signers(signer)); 52 | } 53 | 54 | function test_setRootResolver(address newResolver) public { 55 | vm.prank(makeAddr("0x2")); 56 | vm.expectRevert(abi.encodeWithSelector(Ownable.Unauthorized.selector)); 57 | resolver.setRootResolver(newResolver); 58 | 59 | vm.prank(owner); 60 | vm.expectEmit(); 61 | emit L1Resolver.RootResolverChanged(newResolver); 62 | resolver.setRootResolver(newResolver); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /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/IsAuthorised.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 {BASE_ETH_NODE} from "src/util/Constants.sol"; 7 | 8 | // Because isAuthorized() is an internal method, we test it indirectly here by using `setAddr()` which 9 | // checks the authorization status via `isAuthorized()`. 10 | contract IsAuthorised is L2ResolverBase { 11 | function test_returnsTrue_ifSenderIsController() public { 12 | vm.prank(controller); 13 | resolver.setAddr(node, user); 14 | assertEq(resolver.addr(node), user); 15 | } 16 | 17 | function test_returnsTrue_ifSenderIsReverse() public { 18 | vm.prank(reverse); 19 | resolver.setAddr(node, user); 20 | assertEq(resolver.addr(node), user); 21 | } 22 | 23 | function test_returnsTrue_ifSenderIOwnerOfNode() public { 24 | vm.prank(owner); 25 | registry.setSubnodeOwner(BASE_ETH_NODE, label, user); 26 | vm.prank(user); 27 | resolver.setAddr(node, user); 28 | assertEq(resolver.addr(node), user); 29 | } 30 | 31 | function test_returnsTrue_ifSenderIOperatorOfNode(address operator) public { 32 | vm.assume(operator != owner && operator != user && operator != address(0)); 33 | vm.prank(owner); 34 | registry.setSubnodeOwner(BASE_ETH_NODE, label, user); 35 | vm.prank(user); 36 | resolver.setApprovalForAll(operator, true); 37 | vm.prank(operator); 38 | resolver.setAddr(node, user); 39 | assertEq(resolver.addr(node), user); 40 | } 41 | 42 | function test_returnsTrue_ifSenderIDelegateOfNode(address operator) public { 43 | vm.assume(operator != owner && operator != user && operator != address(0)); 44 | vm.prank(owner); 45 | registry.setSubnodeOwner(BASE_ETH_NODE, label, user); 46 | vm.prank(user); 47 | resolver.approve(node, operator, true); 48 | vm.prank(operator); 49 | resolver.setAddr(node, user); 50 | assertEq(resolver.addr(node), user); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /test/L2Resolver/L2ResolverBase.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {L2Resolver} from "src/L2/L2Resolver.sol"; 6 | import {Registry} from "src/L2/Registry.sol"; 7 | import {ENS} from "ens-contracts/registry/ENS.sol"; 8 | import {ETH_NODE, REVERSE_NODE} from "src/util/Constants.sol"; 9 | import {NameEncoder} from "ens-contracts/utils/NameEncoder.sol"; 10 | import {MockReverseRegistrar} from "test/mocks/MockReverseRegistrar.sol"; 11 | 12 | contract L2ResolverBase is Test { 13 | L2Resolver public resolver; 14 | Registry public registry; 15 | address reverse; 16 | address controller = makeAddr("controller"); 17 | address owner = makeAddr("owner"); 18 | address user = makeAddr("user"); 19 | string name = "test.base.eth"; 20 | bytes32 label = keccak256("test"); 21 | bytes32 node; 22 | 23 | function setUp() public { 24 | registry = new Registry(owner); 25 | reverse = address(new MockReverseRegistrar()); 26 | resolver = new L2Resolver(ENS(address(registry)), controller, reverse, owner); 27 | (, node) = NameEncoder.dnsEncodeName(name); 28 | _establishNamespace(); 29 | } 30 | 31 | function test_constructor() public view { 32 | assertEq(address(resolver.ens()), address(registry)); 33 | assertEq(resolver.registrarController(), controller); 34 | assertEq(resolver.reverseRegistrar(), reverse); 35 | assertEq(resolver.owner(), owner); 36 | } 37 | 38 | function _establishNamespace() internal virtual { 39 | // establish the base.eth namespace 40 | bytes32 ethLabel = keccak256("eth"); 41 | bytes32 baseLabel = keccak256("base"); 42 | vm.prank(owner); 43 | registry.setSubnodeOwner(0x0, ethLabel, owner); 44 | vm.prank(owner); 45 | registry.setSubnodeOwner(ETH_NODE, baseLabel, owner); 46 | 47 | // establish the addr.reverse namespace 48 | vm.prank(owner); 49 | registry.setSubnodeOwner(0x0, keccak256("reverse"), owner); 50 | vm.prank(owner); 51 | registry.setSubnodeOwner(REVERSE_NODE, keccak256("addr"), address(reverse)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /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/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/ReverseRegistrarV2Base.t.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.23; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {ReverseRegistrarV2} from "src/L2/ReverseRegistrarV2.sol"; 6 | import {Registry} from "src/L2/Registry.sol"; 7 | import {ENS} from "ens-contracts/registry/ENS.sol"; 8 | import {MockL2ReverseRegistrar} from "test/mocks/MockL2ReverseRegistrar.sol"; 9 | import {MockNameResolver} from "test/mocks/MockNameResolver.sol"; 10 | import {ETH_NODE, REVERSE_NODE, BASE_REVERSE_NODE} from "src/util/Constants.sol"; 11 | 12 | contract ReverseRegistrarV2Base is Test { 13 | address public owner = makeAddr("owner"); 14 | address public user = makeAddr("user"); 15 | address public controller = makeAddr("controller"); 16 | 17 | Registry public registry; 18 | ReverseRegistrarV2 public reverse; 19 | MockL2ReverseRegistrar public l2ReverseRegistrar; 20 | MockNameResolver public resolver; 21 | 22 | uint256 constant BASE_COINTYPE = 0x80000000 | 0x00002105; 23 | string name = "name"; 24 | 25 | function setUp() public virtual { 26 | registry = new Registry(owner); 27 | l2ReverseRegistrar = new MockL2ReverseRegistrar(); 28 | resolver = new MockNameResolver(); 29 | reverse = new ReverseRegistrarV2( 30 | ENS(address(registry)), owner, BASE_REVERSE_NODE, address(l2ReverseRegistrar), BASE_COINTYPE 31 | ); 32 | vm.prank(owner); 33 | reverse.setControllerApproval(controller, true); 34 | _registrySetup(); 35 | } 36 | 37 | function _registrySetup() internal virtual { 38 | // establish the base.eth namespace 39 | bytes32 ethLabel = keccak256("eth"); 40 | bytes32 baseLabel = keccak256("base"); 41 | vm.prank(owner); 42 | registry.setSubnodeOwner(0x0, ethLabel, owner); 43 | vm.prank(owner); 44 | registry.setSubnodeOwner(ETH_NODE, baseLabel, owner); 45 | 46 | // establish the 80002105.reverse namespace 47 | vm.prank(owner); 48 | registry.setSubnodeOwner(0x0, keccak256("reverse"), owner); 49 | vm.prank(owner); 50 | registry.setSubnodeOwner(REVERSE_NODE, keccak256("80002105"), address(reverse)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /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/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/Renew.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 Renew is UpgradeableRegistrarControllerBase { 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 UpgradeableRegistrarController.ETHPaymentProcessed(user, price.base); 17 | vm.expectEmit(address(controller)); 18 | emit UpgradeableRegistrarController.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 | UpgradeableRegistrarController.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/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 {MockReverseRegistrarV2} from "test/mocks/MockReverseRegistrarV2.sol"; 7 | import {IReverseRegistrarV2} from "src/L2/interface/IReverseRegistrarV2.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 | MockReverseRegistrarV2 newReverse = new MockReverseRegistrarV2(); 14 | vm.expectRevert(abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, caller)); 15 | vm.prank(caller); 16 | controller.setReverseRegistrar(IReverseRegistrarV2(address(newReverse))); 17 | } 18 | 19 | function test_setsTheReverseRegistrarAccordingly() public { 20 | vm.expectEmit(); 21 | MockReverseRegistrarV2 newReverse = new MockReverseRegistrarV2(); 22 | emit UpgradeableRegistrarController.ReverseRegistrarUpdated(address(newReverse)); 23 | vm.prank(owner); 24 | controller.setReverseRegistrar(IReverseRegistrarV2(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 | /// @notice Sets the `nameForAddr()` record for the addr provided account using a signature. 6 | /// 7 | /// @param addr The address to set the name for. 8 | /// @param name The name to set. 9 | /// @param coinTypes The coin types to set. Must be inclusive of the coin type for the contract. 10 | /// @param signatureExpiry Date when the signature expires. 11 | /// @param signature The signature from the addr. 12 | function setNameForAddrWithSignature( 13 | address addr, 14 | uint256 signatureExpiry, 15 | string memory name, 16 | uint256[] memory coinTypes, 17 | bytes memory signature 18 | ) external {} 19 | } 20 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------