From 358550ea80b125ed2fabf09aec8e1036896eb462 Mon Sep 17 00:00:00 2001 From: Tim Pechersky Date: Thu, 25 Jan 2024 15:57:33 +0000 Subject: [PATCH 1/3] multipass migrated in to repo, tests pass. --- .../MultipassDiamond.json | 1268 +++++++++++++++++ abi/src/facets/DNSFacet.sol/DNSFacet.json | 1077 ++++++++++++++ .../MultipassInit.sol/MultipassInit.json | 20 + .../interfaces/IMultipass.sol/IMultipass.json | 1011 +++++++++++++ deploy/06_deoployMultipass.ts | 30 + hardhat.config.ts | 26 +- scripts/libraries/multipass.ts | 0 src/facets/DNSFacet.sol | 304 ++++ src/initializers/MultipassInit.sol | 65 + src/interfaces/IMultipass.sol | 240 ++++ src/libraries/LibMultipass.sol | 276 ++++ test/DNSFacet.ts | 872 ++++++++++++ test/RankifyInstance.ts | 10 +- test/utils.ts | 127 +- types/index.ts | 8 + 15 files changed, 5303 insertions(+), 31 deletions(-) create mode 100644 abi/hardhat-diamond-abi/HardhatDiamondABI.sol/MultipassDiamond.json create mode 100644 abi/src/facets/DNSFacet.sol/DNSFacet.json create mode 100644 abi/src/initializers/MultipassInit.sol/MultipassInit.json create mode 100644 abi/src/interfaces/IMultipass.sol/IMultipass.json create mode 100644 deploy/06_deoployMultipass.ts create mode 100644 scripts/libraries/multipass.ts create mode 100644 src/facets/DNSFacet.sol create mode 100644 src/initializers/MultipassInit.sol create mode 100644 src/interfaces/IMultipass.sol create mode 100644 src/libraries/LibMultipass.sol create mode 100644 test/DNSFacet.ts diff --git a/abi/hardhat-diamond-abi/HardhatDiamondABI.sol/MultipassDiamond.json b/abi/hardhat-diamond-abi/HardhatDiamondABI.sol/MultipassDiamond.json new file mode 100644 index 0000000..3f0daa9 --- /dev/null +++ b/abi/hardhat-diamond-abi/HardhatDiamondABI.sol/MultipassDiamond.json @@ -0,0 +1,1268 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "DomainActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32[]", + "name": "changes", + "type": "bytes32[]" + } + ], + "name": "DomainChangesAreLive", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "DomainDeactivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "newFee", + "type": "uint256" + } + ], + "name": "DomainFeeChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "domainIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "NewDomainName", + "type": "bytes32" + } + ], + "name": "DomainNameChangeRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DomainTTLChangeRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "domainIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "newAmount", + "type": "uint256" + } + ], + "name": "FreeRegistrationsChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registrar", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "freeRegistrationsNumber", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + } + ], + "name": "InitializedDomain", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "discount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "freeNumber", + "type": "uint256" + } + ], + "name": "ReferralProgramChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "indexed": false, + "internalType": "struct LibMultipass.Record", + "name": "refferrer", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "indexed": false, + "internalType": "struct LibMultipass.Record", + "name": "newRecord", + "type": "tuple" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "Referred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "indexed": false, + "internalType": "struct LibMultipass.Record", + "name": "NewRecord", + "type": "tuple" + } + ], + "name": "Registered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "name": "RegistrarChangeRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "indexed": true, + "internalType": "struct LibMultipass.Record", + "name": "newRecord", + "type": "tuple" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "oldName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "UserRecordModified", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32[]", + "name": "changes", + "type": "bytes32[]" + } + ], + "name": "changesQeueCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "fundsWithdawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + } + ], + "name": "nameDeleted", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "activateDomain", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "changeFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "freeRegistrations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "changeReferralProgram", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "newRegistrar", + "type": "address" + } + ], + "name": "changeRegistrar", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "deactivateDomain", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "query", + "type": "tuple" + } + ], + "name": "deleteName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getContractState", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "getDomainState", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "freeRegistrationsNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + }, + { + "internalType": "uint24", + "name": "ttl", + "type": "uint24" + }, + { + "internalType": "uint256", + "name": "registerSize", + "type": "uint256" + } + ], + "internalType": "struct LibMultipass.Domain", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getDomainStateByIdx", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "freeRegistrationsNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + }, + { + "internalType": "uint24", + "name": "ttl", + "type": "uint24" + }, + { + "internalType": "uint256", + "name": "registerSize", + "type": "uint256" + } + ], + "internalType": "struct LibMultipass.Domain", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "query", + "type": "tuple" + } + ], + "name": "getModifyPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "registrar", + "type": "address" + }, + { + "internalType": "uint256", + "name": "freeRegistrationsNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + } + ], + "name": "initializeDomain", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "query", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "newName", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "registrarSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "signatureDeadline", + "type": "uint256" + } + ], + "name": "modifyUserName", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.Record", + "name": "newRecord", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "registrarSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "signatureDeadline", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "referrer", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "referralCode", + "type": "bytes" + } + ], + "name": "register", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "query", + "type": "tuple" + } + ], + "name": "resolveRecord", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.Record", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "currentChainId", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "inspectEIP712Hashes", + "outputs": [ + { + "internalType": "bytes32", + "name": "_CACHED_DOMAIN_SEPARATOR", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "_CACHED_CHAIN_ID", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_CACHED_THIS", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_HASHED_NAME", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_HASHED_VERSION", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_TYPE_HASH", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_functionSelector", + "type": "bytes4" + } + ], + "name": "facetAddress", + "outputs": [ + { + "internalType": "address", + "name": "facetAddress_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "facetAddresses", + "outputs": [ + { + "internalType": "address[]", + "name": "facetAddresses_", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_facet", + "type": "address" + } + ], + "name": "facetFunctionSelectors", + "outputs": [ + { + "internalType": "bytes4[]", + "name": "facetFunctionSelectors_", + "type": "bytes4[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "facets", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "facetAddress", + "type": "address" + }, + { + "internalType": "bytes4[]", + "name": "functionSelectors", + "type": "bytes4[]" + } + ], + "internalType": "struct IDiamondLoupe.Facet[]", + "name": "facets_", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "owner_", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/abi/src/facets/DNSFacet.sol/DNSFacet.json b/abi/src/facets/DNSFacet.sol/DNSFacet.json new file mode 100644 index 0000000..deaf48f --- /dev/null +++ b/abi/src/facets/DNSFacet.sol/DNSFacet.json @@ -0,0 +1,1077 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "DomainActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32[]", + "name": "changes", + "type": "bytes32[]" + } + ], + "name": "DomainChangesAreLive", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "DomainDeactivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "newFee", + "type": "uint256" + } + ], + "name": "DomainFeeChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "domainIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "NewDomainName", + "type": "bytes32" + } + ], + "name": "DomainNameChangeRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DomainTTLChangeRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "domainIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "newAmount", + "type": "uint256" + } + ], + "name": "FreeRegistrationsChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registrar", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "freeRegistrationsNumber", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + } + ], + "name": "InitializedDomain", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "discount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "freeNumber", + "type": "uint256" + } + ], + "name": "ReferralProgramChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "indexed": false, + "internalType": "struct LibMultipass.Record", + "name": "refferrer", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "indexed": false, + "internalType": "struct LibMultipass.Record", + "name": "newRecord", + "type": "tuple" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "Referred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "indexed": false, + "internalType": "struct LibMultipass.Record", + "name": "NewRecord", + "type": "tuple" + } + ], + "name": "Registered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "name": "RegistrarChangeRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "indexed": true, + "internalType": "struct LibMultipass.Record", + "name": "newRecord", + "type": "tuple" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "oldName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "UserRecordModified", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32[]", + "name": "changes", + "type": "bytes32[]" + } + ], + "name": "changesQeueCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "fundsWithdawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + } + ], + "name": "nameDeleted", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "activateDomain", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "changeFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "freeRegistrations", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "changeReferralProgram", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "newRegistrar", + "type": "address" + } + ], + "name": "changeRegistrar", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "deactivateDomain", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "query", + "type": "tuple" + } + ], + "name": "deleteName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getContractState", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "getDomainState", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "freeRegistrationsNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + }, + { + "internalType": "uint24", + "name": "ttl", + "type": "uint24" + }, + { + "internalType": "uint256", + "name": "registerSize", + "type": "uint256" + } + ], + "internalType": "struct LibMultipass.Domain", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getDomainStateByIdx", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "freeRegistrationsNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + }, + { + "internalType": "uint24", + "name": "ttl", + "type": "uint24" + }, + { + "internalType": "uint256", + "name": "registerSize", + "type": "uint256" + } + ], + "internalType": "struct LibMultipass.Domain", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "query", + "type": "tuple" + } + ], + "name": "getModifyPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "registrar", + "type": "address" + }, + { + "internalType": "uint256", + "name": "freeRegistrationsNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + } + ], + "name": "initializeDomain", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "query", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "newName", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "registrarSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "signatureDeadline", + "type": "uint256" + } + ], + "name": "modifyUserName", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.Record", + "name": "newRecord", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "registrarSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "signatureDeadline", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "referrer", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "referralCode", + "type": "bytes" + } + ], + "name": "register", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "query", + "type": "tuple" + } + ], + "name": "resolveRecord", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.Record", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/abi/src/initializers/MultipassInit.sol/MultipassInit.json b/abi/src/initializers/MultipassInit.sol/MultipassInit.json new file mode 100644 index 0000000..26dfdb2 --- /dev/null +++ b/abi/src/initializers/MultipassInit.sol/MultipassInit.json @@ -0,0 +1,20 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + } + ], + "name": "init", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/abi/src/interfaces/IMultipass.sol/IMultipass.json b/abi/src/interfaces/IMultipass.sol/IMultipass.json new file mode 100644 index 0000000..eb566f0 --- /dev/null +++ b/abi/src/interfaces/IMultipass.sol/IMultipass.json @@ -0,0 +1,1011 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "DomainActivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32[]", + "name": "changes", + "type": "bytes32[]" + } + ], + "name": "DomainChangesAreLive", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "DomainDeactivated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "newFee", + "type": "uint256" + } + ], + "name": "DomainFeeChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "domainIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "NewDomainName", + "type": "bytes32" + } + ], + "name": "DomainNameChangeRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DomainTTLChangeRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "domainIndex", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "newAmount", + "type": "uint256" + } + ], + "name": "FreeRegistrationsChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "registrar", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "freeRegistrationsNumber", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + } + ], + "name": "InitializedDomain", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "reward", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "discount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "freeNumber", + "type": "uint256" + } + ], + "name": "ReferralProgramChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "indexed": false, + "internalType": "struct LibMultipass.Record", + "name": "refferrer", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "indexed": false, + "internalType": "struct LibMultipass.Record", + "name": "newRecord", + "type": "tuple" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "Referred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "indexed": false, + "internalType": "struct LibMultipass.Record", + "name": "NewRecord", + "type": "tuple" + } + ], + "name": "Registered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "registrar", + "type": "address" + } + ], + "name": "RegistrarChangeRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "indexed": true, + "internalType": "struct LibMultipass.Record", + "name": "newRecord", + "type": "tuple" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "oldName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "UserRecordModified", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32[]", + "name": "changes", + "type": "bytes32[]" + } + ], + "name": "changesQeueCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "fundsWithdawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + } + ], + "name": "nameDeleted", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "activateDomain", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "changeFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "referrerFeeShare", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "freeRegistrations", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "changeReferralProgram", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "newRegistrar", + "type": "address" + } + ], + "name": "changeRegistrar", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "deactivateDomain", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "query", + "type": "tuple" + } + ], + "name": "deleteName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getContractState", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "name": "getDomainState", + "outputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "freeRegistrationsNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "address", + "name": "registrar", + "type": "address" + }, + { + "internalType": "uint24", + "name": "ttl", + "type": "uint24" + }, + { + "internalType": "uint256", + "name": "registerSize", + "type": "uint256" + } + ], + "internalType": "struct LibMultipass.Domain", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "query", + "type": "tuple" + } + ], + "name": "getModifyPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "registrar", + "type": "address" + }, + { + "internalType": "uint256", + "name": "freeRegistrationsNumber", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "referrerReward", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "referralDiscount", + "type": "uint256" + } + ], + "name": "initializeDomain", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "query", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "newName", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "registrarSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "signatureDeadline", + "type": "uint256" + } + ], + "name": "modifyUserName", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.Record", + "name": "newRecord", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "registrarSignature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "signatureDeadline", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "referrer", + "type": "tuple" + }, + { + "internalType": "bytes", + "name": "referralCode", + "type": "bytes" + } + ], + "name": "register", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "targetDomain", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.NameQuery", + "name": "query", + "type": "tuple" + } + ], + "name": "resolveRecord", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + }, + { + "components": [ + { + "internalType": "address", + "name": "wallet", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "name", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "internalType": "bytes32", + "name": "domainName", + "type": "bytes32" + } + ], + "internalType": "struct LibMultipass.Record", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "withrawFunds", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/deploy/06_deoployMultipass.ts b/deploy/06_deoployMultipass.ts new file mode 100644 index 0000000..16c33ea --- /dev/null +++ b/deploy/06_deoployMultipass.ts @@ -0,0 +1,30 @@ +import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { DeployFunction } from 'hardhat-deploy/types'; +import { MULTIPASS_CONTRACT_VERSION, MULTIPASS_CONTRACT_NAME } from '../test/utils'; +import { getProcessEnv } from '../scripts/libraries/utils'; +import { ethers } from 'hardhat'; +import { MultipassDiamond } from '../types'; +const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => { + const { deployments, getNamedAccounts } = hre; + const { deploy, diamond } = deployments; + const { deployer, owner } = await getNamedAccounts(); + + const deployment = await diamond.deploy('Multipass', { + from: deployer, + owner: deployer, + log: true, + facets: ['DNSFacet', 'EIP712InspectorFacet', 'MultipassInit'], + execute: { + methodName: 'init', + args: + process.env.NODE_ENV === 'TEST' + ? [MULTIPASS_CONTRACT_NAME, MULTIPASS_CONTRACT_VERSION] + : [getProcessEnv(false, 'MULTIPASS_CONTRACT_NAME'), getProcessEnv(false, 'MULTIPASS_CONTRACT_VERSION')], + }, + }); + const multipass = (await ethers.getContractAt(deployment.abi, deployment.address)) as MultipassDiamond; + await multipass.connect(await hre.ethers.getSigner(deployer)).transferOwnership(owner); +}; + +export default func; +func.tags = ['multipass']; diff --git a/hardhat.config.ts b/hardhat.config.ts index 71df6bc..2c5fe5a 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -143,19 +143,19 @@ export default { ], }, diamondAbi: [ - // { - // // (required) The name of your Diamond ABI - // name: 'MultipassDiamond', - // include: ['DNSFacet', 'OwnershipFacet', 'DiamondLoupeFacet', 'EIP712InspectorFacet'], - // // We explicitly set `strict` to `true` because we want to validate our facets don't accidentally provide overlapping functions - // strict: true, - // // We use our diamond utils to filter some functions we ignore from the combined ABI - // filter(abiElement: unknown, index: number, abi: unknown[], fullyQualifiedName: string) { - // // const changes = new diamondUtils.DiamondChanges(); - // const signature = toSignature(abiElement); - // return isIncluded(fullyQualifiedName, signature); - // }, - // }, + { + // (required) The name of your Diamond ABI + name: 'MultipassDiamond', + include: ['DNSFacet', 'OwnershipFacet', 'DiamondLoupeFacet', 'EIP712InspectorFacet'], + // We explicitly set `strict` to `true` because we want to validate our facets don't accidentally provide overlapping functions + strict: true, + // We use our diamond utils to filter some functions we ignore from the combined ABI + filter(abiElement: unknown, index: number, abi: unknown[], fullyQualifiedName: string) { + // const changes = new diamondUtils.DiamondChanges(); + const signature = toSignature(abiElement); + return isIncluded(fullyQualifiedName, signature); + }, + }, { name: 'RankifyDiamondInstance', include: [ diff --git a/scripts/libraries/multipass.ts b/scripts/libraries/multipass.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/facets/DNSFacet.sol b/src/facets/DNSFacet.sol new file mode 100644 index 0000000..55669af --- /dev/null +++ b/src/facets/DNSFacet.sol @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; +// import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import "../abstracts/draft-EIP712Diamond.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import "../interfaces/IMultipass.sol"; +import "../libraries/LibMultipass.sol"; +import "../modifiers/OnlyOwnerDiamond.sol"; +import "hardhat/console.sol"; +import "../vendor/facets/OwnershipFacet.sol"; + +// Consider upgrade for https://eips.ethereum.org/EIPS/eip-4834 + +contract DNSFacet is EIP712, IMultipass { + using ECDSA for bytes32; + using LibMultipass for bytes32; + + // using LibMultipass for LibMultipass.Record; + using LibMultipass for LibMultipass.Record; + using LibMultipass for bytes; + + function _isValidSignature( + bytes memory message, + bytes memory signature, + address account + ) internal view returns (bool) { + bytes32 typedHash = _hashTypedDataV4(keccak256(message)); + return SignatureChecker.isValidSignatureNow(account, typedHash, signature); + } + + function _validateRegistration( + LibMultipass.Record memory newRecord, + bytes32 domainName, + bytes memory registrarSignature, + uint256 signatureDeadline + ) private view { + LibMultipass.NameQuery memory query = LibMultipass.queryFromRecord(newRecord, domainName); + //Check name query is legit + require(LibMultipass._checkNotEmpty(query.id), "_validateNameQuery-> new record id cannot be empty"); + require( + LibMultipass._checkNotEmpty(query.domainName), + "_validateNameQuery-> new record domain cannot be empty" + ); + require(query.wallet != address(0), "_validateNameQuery-> new ecord address cannot be empty"); + + //Check query does not resolves (name already exists) + (bool nameExists, ) = LibMultipass.resolveRecord(query); + require(nameExists == false, "User already registered, use modify instead"); + //Check LibMultipass.Domain is legit + LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(query.domainName); + require(_domain.properties.isActive, "Multipass->register: domain is not active"); + + //check signatures and time + require(signatureDeadline > block.number, "Multipass->register: Deadline is less than current block number"); + + { + bytes memory registrarMessage = abi.encode( + LibMultipass._TYPEHASH, + query.name, + query.id, + query.domainName, + signatureDeadline, + 0 + ); + + require( + _isValidSignature(registrarMessage, registrarSignature, _domain.properties.registrar), + "Multipass->register: Registrar signature is not valid" + ); + } + { + (bool status, ) = LibMultipass.resolveRecord(query); + require(status == false, "Multipass->register: applicant is already registered, use modify instread"); + } + } + + function initializeDomain( + address registrar, + uint256 freeRegistrationsNumber, + uint256 fee, + bytes32 domainName, + uint256 referrerReward, + uint256 referralDiscount + ) public override onlyOwner { + require(registrar != address(0), "Multipass->initializeDomain: You must provide a registrar address"); + require(LibMultipass._checkNotEmpty(domainName), "Multipass->initializeDomain: Domain name cannot be empty"); + require( + LibMultipass.resolveDomainIndex(domainName) == 0, + "Multipass->initializeDomain: Domain name already exists" + ); + (bool status, uint256 result) = SafeMath.tryAdd(referrerReward, referralDiscount); + require(status == true, "Multipass->initializeDomain: referrerReward + referralDiscount overflow"); + require(result <= fee, "Multipass->initializeDomain: referral values are higher then fee itself"); + + LibMultipass._initializeDomain( + registrar, + freeRegistrationsNumber, + fee, + domainName, + referrerReward, + referralDiscount + ); + emit InitializedDomain(registrar, freeRegistrationsNumber, fee, domainName, referrerReward, referralDiscount); + } + + function _enforseDomainNameIsValid(bytes32 domainName) private view { + require(domainName._checkNotEmpty(), "activateDomain->Please specify LibMultipass.Domain name"); + require(domainName.resolveDomainIndex() != 0, "Domain does not exist"); + } + + function activateDomain(bytes32 domainName) public override onlyOwner { + _enforseDomainNameIsValid(domainName); + LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); + _domain.properties.isActive = true; + emit DomainActivated(domainName); + } + + function deactivateDomain(bytes32 domainName) public override onlyOwner { + _enforseDomainNameIsValid(domainName); + LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); + _domain.properties.isActive = false; + } + + function changeFee(bytes32 domainName, uint256 fee) public override onlyOwner { + _enforseDomainNameIsValid(domainName); + LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); + uint256 _referrerReward = _domain.properties.referrerReward; + uint256 _referralDiscount = _domain.properties.referralDiscount; + require( + _referralDiscount + _referrerReward <= fee, + "Multipass->changeFee: referral rewards would become too high" + ); + _domain.properties.fee = fee; + emit DomainFeeChanged(domainName, fee); + } + + function changeRegistrar(bytes32 domainName, address newRegistrar) public override onlyOwner { + _enforseDomainNameIsValid(domainName); + LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); + require(newRegistrar != address(0), "new registrar cannot be zero"); + _domain.properties.registrar = newRegistrar; + } + + function deleteName( + LibMultipass.NameQuery memory query // bytes32 domainName, // address wallet, // bytes32 username, // bytes32 id + ) public override onlyOwner { + _enforseDomainNameIsValid(query.domainName); + LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(query.domainName); + query.targetDomain = ""; + (bool status, LibMultipass.Record memory r) = resolveRecord(query); + require(status == true, "Multipass->deleteName: name not resolved"); + _domain.addressToId[r.wallet] = bytes32(0); + _domain.idToAddress[r.id] = address(0); + _domain.idToName[r.id] = bytes32(0); + _domain.nameToId[r.name] = bytes32(0); + _domain.nonce[r.id] += 1; + _domain.properties.registerSize--; + + emit nameDeleted(_domain.properties.name, r.wallet, r.id, r.name); + } + + function changeReferralProgram( + uint256 referrerReward, + uint256 freeRegistrations, + uint256 referralDiscount, + bytes32 domainName + ) public override onlyOwner { + _enforseDomainNameIsValid(domainName); + LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); + (bool status, uint256 result) = SafeMath.tryAdd(referrerReward, referralDiscount); + require(status == true, "Multipass->changeReferralProgram: referrerReward + referralDiscount overflow"); + require( + result <= _domain.properties.fee, + "Multipass->changeReferralProgram: referral values are higher then the fee itself" + ); + _domain.properties.referrerReward = referrerReward; + _domain.properties.referralDiscount = referralDiscount; + _domain.properties.freeRegistrationsNumber = freeRegistrations; + emit ReferralProgramChanged(domainName, referrerReward, referralDiscount, freeRegistrations); + } + + /** + @dev resolves LibMultipass.Record of name query in to status and identity */ + function resolveRecord( + LibMultipass.NameQuery memory query + ) public view override returns (bool, LibMultipass.Record memory) { + return LibMultipass.resolveRecord(query); + } + + function register( + LibMultipass.Record memory newRecord, + bytes32 domainName, + bytes memory registrarSignature, + uint256 signatureDeadline, + LibMultipass.NameQuery memory referrer, + bytes memory referralCode + ) public payable override { + _enforseDomainNameIsValid(domainName); + _validateRegistration(newRecord, domainName, registrarSignature, signatureDeadline); + LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); + (bool hasValidReferrer, LibMultipass.Record memory referrerRecord) = LibMultipass.resolveRecord(referrer); + uint256 referrersShare = 0; + if (!LibMultipass.shouldRegisterForFree(_domain)) { + referrersShare = hasValidReferrer ? _domain.properties.referrerReward : 0; + uint256 valueToPay = SafeMath.sub( + _domain.properties.fee, + hasValidReferrer ? _domain.properties.referralDiscount : 0 + ); + require(msg.value >= valueToPay, "Multipass->register: Payment value is not enough"); + } + LibMultipass._registerNew(newRecord, _domain); + emit Registered(_domain.properties.name, newRecord); + if (hasValidReferrer) { + bytes memory refferalMessage = abi.encode(LibMultipass._TYPEHASH_REFERRAL, referrerRecord.wallet); + require( + _isValidSignature(refferalMessage, referralCode, referrerRecord.wallet), + "Multipass->register: Referral code is not valid" + ); + require( + payable(referrerRecord.wallet).send(referrersShare), + "Multipass->register: Failed to send referral reward" + ); + require(referrerRecord.wallet != newRecord.wallet, "Cannot refer yourself"); + emit Referred(referrerRecord, newRecord, domainName); + } + } + + function getModifyPrice(LibMultipass.NameQuery memory query) public view override returns (uint256) { + (bool userExists, LibMultipass.Record memory record) = LibMultipass.resolveRecord(query); + require(userExists == true, "getModifyPrice->user not found "); + return LibMultipass._getModifyPrice(record); + } + + function modifyUserName( + bytes32 domainName, + LibMultipass.NameQuery memory query, + bytes32 newName, + bytes memory registrarSignature, + uint256 signatureDeadline + ) public payable override { + _enforseDomainNameIsValid(domainName); + query.targetDomain = domainName; + LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); + require(_domain.properties.isActive, "Multipass->modifyUserName: LibMultipass.Domain is not active"); + require(newName != bytes32(0), "Multipass->modifyUserName: Name cannot be empty"); + require( + signatureDeadline >= block.number, + "Multipass->modifyUserName: Signature deadline must be greater than current block number" + ); + + (bool userExists, LibMultipass.Record memory userRecord) = LibMultipass.resolveRecord(query); + LibMultipass.Record memory newRecord = userRecord; + bytes32 oldName = newRecord.name; + newRecord.name = newName; + require(userExists == true, "user does not exist, use register() instead"); + bytes memory registrarMessage = abi.encode( + LibMultipass._TYPEHASH, + newRecord.name, + newRecord.id, + newRecord.domainName, + signatureDeadline, + userRecord.nonce + ); + require( + _isValidSignature(registrarMessage, registrarSignature, _domain.properties.registrar), + "Multipass->modifyUserName: Not a valid signature" + ); + + uint256 _fee = LibMultipass._getModifyPrice(newRecord); + + require(msg.value >= _fee, "Multipass->modifyUserName: Not enough payment"); + require(_domain.nonce[userRecord.id] == userRecord.nonce, "Multipass->modifyUserName: invalid nonce"); + require(_domain.nameToId[newName] == bytes32(0), "OveMultipass->modifyUserName: new name already exists"); + + LibMultipass._setRecord(_domain, newRecord); + _domain.nameToId[_domain.idToName[newRecord.id]] = bytes32(0); + + emit UserRecordModified(newRecord, oldName, domainName); + } + + function getBalance() external view override returns (uint256) { + return address(this).balance; + } + + function getDomainState(bytes32 domainName) external view override returns (LibMultipass.Domain memory) { + LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(domainName); + return _domain.properties; + } + + function getDomainStateByIdx(uint256 index) external view returns (LibMultipass.Domain memory) { + LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorageByIdx(index); + return _domain.properties; + } + + function getContractState() external view override returns (uint256) { + return LibMultipass._getContractState(); + } + + function withrawFunds(address to) public override onlyOwner { + payable(to).transfer(address(this).balance); + } +} \ No newline at end of file diff --git a/src/initializers/MultipassInit.sol b/src/initializers/MultipassInit.sol new file mode 100644 index 0000000..dd28722 --- /dev/null +++ b/src/initializers/MultipassInit.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/******************************************************************************\ +* Author: Nick Mudge (https://twitter.com/mudgen) +* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535 +* +* Implementation of a diamond. +/******************************************************************************/ + +import {LibDiamond} from "../vendor/libraries/LibDiamond.sol"; +import {IDiamondLoupe} from "../vendor/interfaces/IDiamondLoupe.sol"; +import {IDiamondCut} from "../vendor/interfaces/IDiamondCut.sol"; +import {IERC173} from "../vendor/interfaces/IERC173.sol"; +import {IERC165} from "../vendor/interfaces/IERC165.sol"; +import {IMultipass} from "../interfaces/IMultipass.sol"; +import {LibEIP712WithStorage} from "../libraries/LibEIP712Storage.sol"; +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import "../modifiers/OnlyOwnerDiamond.sol"; + +// It is expected that this contract is customized if you want to deploy your diamond +// with data from a deployment script. Use the init function to initialize state variables +// of your diamond. Add parameters to the init funciton if you need to. + +contract MultipassInit is OnlyOwnerDiamond { + + function _buildDomainSeparator( + bytes32 typeHash, + bytes32 nameHash, + bytes32 versionHash + ) private view returns (bytes32) { + return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this))); + } + + function init(string memory name, string memory version) external onlyOwner { + // adding ERC165 data + LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); + ds.supportedInterfaces[type(IERC165).interfaceId] = true; + ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true; + ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true; + ds.supportedInterfaces[type(IERC173).interfaceId] = true; + + bytes32 hashedName = keccak256(bytes(name)); + bytes32 hashedVersion = keccak256(bytes(version)); + bytes32 typeHash = keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ); + LibEIP712WithStorage.LibEIP712WithStorageStorage storage ss = LibEIP712WithStorage.EIP712WithStorage(); + ss._HASHED_NAME = hashedName; + ss._HASHED_VERSION = hashedVersion; + ss._CACHED_CHAIN_ID = block.chainid; + ss._CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); + ss._CACHED_THIS = address(this); + ss._TYPE_HASH = typeHash; + + ds.supportedInterfaces[type(IMultipass).interfaceId] = true; + + // add your own state variables + // EIP-2535 specifies that the `diamondCut` function takes two optional + // arguments: address _init and bytes calldata _calldata + // These arguments are used to execute an arbitrary function using delegatecall + // in order to set state variables in the diamond during deployment or an upgrade + // More info here: https://eips.ethereum.org/EIPS/eip-2535#diamond-interface + } +} \ No newline at end of file diff --git a/src/interfaces/IMultipass.sol b/src/interfaces/IMultipass.sol new file mode 100644 index 0000000..e859cd7 --- /dev/null +++ b/src/interfaces/IMultipass.sol @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +import "../libraries/LibMultipass.sol"; + +interface IMultipass { + function resolveRecord(LibMultipass.NameQuery memory query) + external + view + returns (bool, LibMultipass.Record memory); + + /** @dev same as resolveRecord but returns username, id and LibMultipass.Domain as string */ + // function resolveRecordToString(LibMultipass.NameQuery memory query) + // external + // view + // returns ( + // bool, + // LibMultipass.Record memory + // ); + + /** + * @dev Initializes new LibMultipass.Domain and configures it's parameters + * + * Requirements: + * registrar is not zero + * domainName is not empty + * domainIndex is either zero(auto assign) or can be one of preoccupied LibMultipass.Domain names + * domainName does not exist yet + * onlyOwner + * referrerReward+referralDiscount cannot be larger than fee + * @param registrar address of registrar + * @param freeRegistrationsNumber number of registrations free of fee + * @param fee fee in base currency of network + * @param domainName name of LibMultipass.Domain + * @param referrerReward referral fee share in base currency of network + * @param referralDiscount referral discount in base currency of network + * + * Emits an {InitializedDomain} event. + */ + function initializeDomain( + address registrar, + uint256 freeRegistrationsNumber, + uint256 fee, + bytes32 domainName, + uint256 referrerReward, + uint256 referralDiscount + ) external; + + /** + * @dev Activates LibMultipass.Domain name + * + * Requirements: + * msg.sender is Owner + * + * + * Emits an {DomainActivated} event. + */ + function activateDomain(bytes32 domainName) external; + + /** + * @dev Deactivates LibMultipass.Domain name + * + * Deactivated LibMultipass.Domain cannot mutate names and will return zeros + * + * Requirements: + * msg.sender is Owner OR registrar + * + * + * Emits an {DomainDeactivated} event. + */ + + function deactivateDomain(bytes32 domainName) external; + + /** + * @dev Changes registrar address + * + * Requirements: + * msg.sender is Owner + * + * Emits an {DomainFeeChanged} event. + */ + function changeFee(bytes32 domainName, uint256 fee) external; + + /** + * @dev Changes registrar address + * + * Requirements: + * msg.sender is Owner + * + * Emits an {RegistrarChangeRequested} event. + */ + function changeRegistrar(bytes32 domainName, address newRegistrar) external; + + /** + * @dev deletes name + * + * Requirements: + * msg.sender is Owner + * + * Emits an {DomainTTLChangeRequested} event. + */ + function deleteName(LibMultipass.NameQuery memory query) external; + + /** + * @dev executes all pending changes to LibMultipass.Domain that fulfill TTL + * + * Requirements: + * domainName must be set + * referrerFeeShare+referralDiscount cannot be larger than 2^32 + * + * + * Emits an {ReferralProgramChangeRequested} event. + */ + function changeReferralProgram( + uint256 referrerFeeShare, + uint256 referralDiscount, + uint256 freeRegistrations, + bytes32 domainName + ) external; + + /** + * @dev registers new name under LibMultipass.Domain + * + * Requirements: + * all arguments must be set + * domainName must be active + * resolveRecord for given arguments should return no LibMultipass.Record + * + * + * Emits an {registered} event. + */ + function register( + LibMultipass.Record memory newRecord, + bytes32 domainName, + bytes memory registrarSignature, + uint256 signatureDeadline, + LibMultipass.NameQuery memory referrer, + bytes memory referralCode + ) external payable; + + /** + * @dev modifies exsisting LibMultipass.Record + * + * Requirements: + * resolveRecord for given arguments should return valid LibMultipass.Record + * LibMultipass.Domain must be active + * newAddress and newName should be set and be unique in current LibMultipass.Domain + * + * @param domainName LibMultipass.Domain + * @param newName new name + * + * Emits an {Modified} event. + */ + function modifyUserName( + bytes32 domainName, + LibMultipass.NameQuery memory query, + bytes32 newName, + bytes memory registrarSignature, + uint256 signatureDeadline + ) external payable; + + /** + * @dev returns balance of this contract + */ + function getBalance() external view returns (uint256); + + /** + * @dev returns LibMultipass.Domain state variables + * @param domainName name of the LibMultipass.Domain + * @return (name, + fee, + freeRegistrationsNumber, + referrerReward, + referralDiscount, + isActive, + registrar, + ttl, + registerSize) + */ + function getDomainState(bytes32 domainName) external view returns (LibMultipass.Domain memory); + + /** + * @dev returns contract state variables + + * @return (s_numDomains) + */ + function getContractState() external view returns (uint256); + + /** + * @dev Withraws funds stored in smart contract + * + * Requirements: + * onlyOwner + * + * Emits an {fundsWithdawn} event. + */ + function withrawFunds(address to) external; + + function getModifyPrice(LibMultipass.NameQuery memory query) external view returns (uint256); + + event fundsWithdawn(uint256 indexed amount, address indexed account); + + // event InitializedDomain(uint256 indexed index, bytes32 indexed domainName); + event InitializedDomain( + address indexed registrar, + uint256 freeRegistrationsNumber, + uint256 indexed fee, + bytes32 indexed domainName, + uint256 referrerReward, + uint256 referralDiscount + ); + event DomainActivated(bytes32 indexed domainName); + event DomainDeactivated(bytes32 indexed domainName); + + event DomainFeeChanged(bytes32 indexed domainName, uint256 indexed newFee); + event FreeRegistrationsChanged(uint256 indexed domainIndex, uint256 indexed newAmount); + + event RegistrarChangeRequested(bytes32 indexed domainName, address indexed registrar); + event DomainNameChangeRequested(uint256 indexed domainIndex, bytes32 indexed NewDomainName); + event nameDeleted(bytes32 indexed domainName, address indexed wallet, bytes32 indexed id, bytes32 name); + event DomainTTLChangeRequested(bytes32 indexed domainName, uint256 amount); + event ReferralProgramChanged( + bytes32 indexed domainName, + uint256 reward, + uint256 discount, + uint256 indexed freeNumber + ); + event DomainChangesAreLive(bytes32 indexed domainName, bytes32[] indexed changes); + event changesQeueCanceled(bytes32 indexed domainName, bytes32[] indexed changes); + + event Registered(bytes32 indexed domainName, LibMultipass.Record NewRecord); + + event Referred(LibMultipass.Record refferrer, LibMultipass.Record newRecord, bytes32 indexed domainName); + + event UserRecordModified( + LibMultipass.Record indexed newRecord, + bytes32 indexed oldName, + bytes32 indexed domainName + ); +} \ No newline at end of file diff --git a/src/libraries/LibMultipass.sol b/src/libraries/LibMultipass.sol new file mode 100644 index 0000000..5606357 --- /dev/null +++ b/src/libraries/LibMultipass.sol @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +// import "./LibDiamondOwner.sol"; +// import { IMultipass } from "../interfaces/sol"; +import "hardhat/console.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; + +library LibMultipass { + /** + * @dev resolves user from any given argument + * Requirements: + * domainName must be given and must be initialized + * id OR username OR address must be given + * This method first tries to resolve by address, then by user id and finally by username + * @param domainName domain name + * @param wallet adress of user + * @param id user id + * @param username username + * @param targetDomain if this is set to valid domain name, then after sucessfull resolving account at domainName, + * this method will rerun with resolving user properties in targetDomain + */ + struct NameQuery { + bytes32 domainName; + address wallet; + bytes32 name; + bytes32 id; + bytes32 targetDomain; + } + + /** + * @dev The domain name of the registrar. + * @param registrar is the address private key of which is owned by signing server (e.g. Discord bot server) + * @param name is unique string that is used to find this domain within domains. + * @param freeRegistrationsNumber is the number of free registrations for this domain + + * @param fee amount of payment requried to register name in the domain + * @param ttl time to live for changes in the domain properties + * @param isActive when is false domain name will not respond to any changes and will not return any address + **/ + struct Domain { + bytes32 name; //32bytes + uint256 fee; //32bytes + uint256 freeRegistrationsNumber; //32bytes + uint256 referrerReward; //32bytes + uint256 referralDiscount; //32bytes + bool isActive; //1byte + address registrar; //20 bytes + uint24 ttl; //3 bytes (not being used for now) + uint256 registerSize; //32bytes + } + + // struct NameQueryBytes32 { + // string domainName; + // address wallet; + // bytes32 name; + // bytes32 id; + // string targetDomain; + // } + struct Record { + address wallet; + bytes32 name; + bytes32 id; + uint96 nonce; + bytes32 domainName; + } + + // struct RecordBytes32 { + // address wallet; + // bytes32 name; + // bytes32 id; + // uint96 nonce; + // } + + bytes32 constant MULTIPASS_STORAGE_POSITION = keccak256("multipass.diamond.storage.position"); + + /** + * @dev The domain name of the registrar. + * @param properties - domain configuration + * @param idToAddress is mapping from unique identificator to an address + * @param registerSize is number of registered users for this domain + * @param nonce is incremented each time Record changes in addressToId map + * @param nameToId is mapping from names to unique identificator. While each name required to be unique, + names might change on the domain, so we keep records to user identificators as immutable property of user + * @param addressToId is mapping from an address to unique identificator + * @param idToName is mapping from identificator to a name + **/ + struct DomainNameService { + Domain properties; //128 bytes + mapping(bytes32 => address) idToAddress; //N*20bytes + mapping(bytes32 => uint96) nonce; //N*12bytes + mapping(address => bytes32) addressToId; //N*32 bytes + mapping(bytes32 => bytes32) nameToId; //N*32 bytes + mapping(bytes32 => bytes32) idToName; //N*32 bytes + //Total: 128+N*160 Bytes + } + + struct MultipassStorageStruct { + mapping(uint256 => DomainNameService) domains; + mapping(bytes32 => uint256) domainNameToIndex; //helper to get domain index by name + uint256 numDomains; + } + + function MultipassStorage() internal pure returns (MultipassStorageStruct storage es) { + bytes32 position = MULTIPASS_STORAGE_POSITION; + assembly { + es.slot := position + } + } + + bytes32 internal constant _TYPEHASH = + keccak256("registerName(bytes32 name,bytes32 id,bytes32 domainName,uint256 deadline,uint96 nonce)"); + bytes32 internal constant _TYPEHASH_REFERRAL = keccak256("proofOfReferrer(address referrerAddress)"); + + // function _stringToBytes32(string memory source) internal pure returns (bytes32 result) { + // uint256 length = bytes(source).length; + // require(length <= 32, "_stringToBytes32->String longer than 32 bytes"); + // bytes memory tempEmptyStringTest = abi.encodePacked(source); + // if (tempEmptyStringTest.length == 0) { + // return 0x0; + // } + // assembly { + // result := mload(add(source,32)) + // } + // } + + function _checkStringFits32b(string memory value) internal pure returns (bool) { + if (bytes(value).length <= 32) { + return true; + } else { + return false; + } + } + + function _checkNotEmpty(bytes32 value) internal pure returns (bool) { + if (value == "") { + return false; + } else { + return true; + } + } + + function resolveDomainIndex(bytes32 domainName) internal view returns (uint256) { + MultipassStorageStruct storage s = MultipassStorage(); + return s.domainNameToIndex[domainName]; + } + + function _getDomainStorage(bytes32 domainName) internal view returns (DomainNameService storage) { + MultipassStorageStruct storage s = MultipassStorage(); + + return s.domains[resolveDomainIndex(domainName)]; + } + + function _initializeDomain( + address registrar, + uint256 freeRegistrationsNumber, + uint256 fee, + bytes32 domainName, + uint256 referrerReward, + uint256 referralDiscount + ) internal { + LibMultipass.MultipassStorageStruct storage ms = LibMultipass.MultipassStorage(); + + uint256 domainIndex = ms.numDomains + 1; + LibMultipass.DomainNameService storage _domain = ms.domains[domainIndex]; + _domain.properties.registrar = registrar; + _domain.properties.freeRegistrationsNumber = freeRegistrationsNumber; + _domain.properties.fee = fee; + _domain.properties.name = domainName; + _domain.properties.referrerReward = referrerReward; + _domain.properties.referralDiscount = referralDiscount; + ms.numDomains++; + ms.domainNameToIndex[domainName] = domainIndex; + } + + function _getModifyPrice(LibMultipass.Record memory userRecord) internal view returns (uint256) { + LibMultipass.DomainNameService storage _domain = LibMultipass._getDomainStorage(userRecord.domainName); + uint256 feeCoefficient = SafeMath.div(_domain.properties.fee, 10); + uint256 nonceCoefficient = SafeMath.mul(userRecord.nonce, userRecord.nonce); + return SafeMath.add(SafeMath.mul(feeCoefficient, nonceCoefficient), _domain.properties.fee); + } + + function _resolveRecord(NameQuery memory query) private view returns (bool, Record memory) { + if ((query.wallet == address(0)) && (query.id == bytes32(0)) && (query.name == bytes32(0))) { + Record memory rv; + return (false, rv); + } + + MultipassStorageStruct storage s = MultipassStorage(); + DomainNameService storage _domain = s.domains[s.domainNameToIndex[query.domainName]]; + DomainNameService storage _targetDomain = s.domains[ + s.domainNameToIndex[query.targetDomain == bytes32(0) ? query.domainName : query.targetDomain] + ]; + address _wallet; + { + // resolve wallet + if (query.wallet != address(0)) { + _wallet = query.wallet; + } else if (query.id != bytes32(0)) { + _wallet = _domain.idToAddress[query.id]; + } else if (query.name != bytes32(0)) { + bytes32 _id = _domain.nameToId[query.name]; + _wallet = _domain.idToAddress[_id]; + } + } + + //from wallet find and return record + return _resolveFromAddress(_wallet, _targetDomain); + } + + /** + @dev resolves Record of name query in to status and identity */ + function resolveRecord(NameQuery memory query) internal view returns (bool, Record memory) { + return _resolveRecord(query); + } + + /** @dev this function bears no security checks, it will ignore nonce in arg and will increment + * nonce value stored in domain instread + */ + function _setRecord(DomainNameService storage domain, Record memory record) internal { + domain.addressToId[record.wallet] = record.id; + domain.idToAddress[record.id] = record.wallet; + domain.idToName[record.id] = record.name; + domain.nameToId[record.name] = record.id; + domain.nonce[record.id] += 1; + } + + function _resolveFromAddress( + address _address, + DomainNameService storage _domain + ) private view returns (bool, Record memory) { + Record memory resolved; + + resolved.id = _domain.addressToId[_address]; + resolved.name = _domain.idToName[resolved.id]; + resolved.nonce = _domain.nonce[resolved.id]; + resolved.wallet = _address; + resolved.domainName = _domain.properties.name; + + if (resolved.id == bytes32(0)) { + return (false, resolved); + } + return (true, resolved); + } + + function queryFromRecord(Record memory _record, bytes32 _domainName) internal pure returns (NameQuery memory) { + NameQuery memory _query; + _query.id = _record.id; + _query.domainName = _domainName; + _query.name = _record.name; + _query.wallet = _record.wallet; + return _query; + } + + function shouldRegisterForFree(DomainNameService storage domain) internal view returns (bool) { + return domain.properties.freeRegistrationsNumber > domain.properties.registerSize ? true : false; + } + + function _registerNew(Record memory newRecord, DomainNameService storage domain) internal { + _setRecord(domain, newRecord); + domain.properties.registerSize += 1; + } + + function _getContractState() internal view returns (uint256) { + LibMultipass.MultipassStorageStruct storage ms = LibMultipass.MultipassStorage(); + return ms.numDomains; + } + + function _getDomainStorageByIdx(uint256 index) internal view returns (DomainNameService storage) { + MultipassStorageStruct storage s = MultipassStorage(); + + return s.domains[index]; + } + + using LibMultipass for NameQuery; +} \ No newline at end of file diff --git a/test/DNSFacet.ts b/test/DNSFacet.ts new file mode 100644 index 0000000..a56a8ba --- /dev/null +++ b/test/DNSFacet.ts @@ -0,0 +1,872 @@ +import { AdrSetupResult, EnvSetupResult, SignerIdentity, setupTest } from './utils'; +import { setupAddresses, setupEnvironment, getUserRegisterProps, signRegistrarMessage } from './utils'; +import { getInterfaceID } from '../scripts/libraries/utils'; +import { expect } from 'chai'; +import { ethers } from 'hardhat'; +import { IMultipass__factory } from '../types'; +const path = require('path'); +import { LibMultipass } from '../types/src/facets/DNSFacet'; + +const scriptName = path.basename(__filename); +const NEW_DOMAIN_NAME1 = 'newDomainName1'; +const NEW_DOMAIN_NAME2 = 'newDomainName2'; +const DEFAULT_FREE_REGISTRATIONS = ethers.BigNumber.from(3); +const NOT_ENOUGH_FEE = ethers.utils.parseEther('0.17'); +const DEFAULT_FEE = ethers.utils.parseEther('2'); +const FEE_AFTER_CHANGE = ethers.utils.parseEther('3'); +const DEFAULT_DISCOUNT = ethers.utils.parseEther('1'); +const DEFAULT_REWARD = ethers.utils.parseEther('0.5'); +let adr: AdrSetupResult; + +let env: EnvSetupResult; + +const emptyUserQuery: LibMultipass.NameQueryStruct = { + name: ethers.utils.formatBytes32String(''), + id: ethers.utils.formatBytes32String(''), + domainName: ethers.utils.formatBytes32String(''), + wallet: ethers.constants.AddressZero, + targetDomain: ethers.utils.formatBytes32String(''), +}; + +describe(scriptName, () => { + beforeEach(async () => { + const setup = await setupTest(); + adr = setup.adr; + env = setup.env; + }); + it('Is Owned by contract owner', async () => { + expect(await env.multipass.owner()).to.be.equal(adr.multipassOwner.wallet.address); + }); + it('Transfer ownership can be done only by contract owner', async () => { + await expect( + env.multipass.connect(adr.multipassOwner.wallet).transferOwnership(adr.gameCreator1.wallet.address), + ).to.emit(env.multipass, 'OwnershipTransferred(address,address)'); + + await expect( + env.multipass.connect(adr.maliciousActor1.wallet).transferOwnership(adr.gameCreator1.wallet.address), + ).to.revertedWith('LibDiamond: Must be contract owner'); + }); + it('Has zero domains', async () => { + expect(await env.multipass.getContractState()).to.be.equal(0); + }); + it('Supports multipass interface', async () => { + const MultipassInterface = IMultipass__factory.createInterface(); + const multipassInterfaceId = getInterfaceID(MultipassInterface); + expect(await env.multipass.supportsInterface(multipassInterfaceId._hex)).to.be.true; + }); + it('Emits and increments when new domain initialized', async () => { + await expect( + await env.multipass + .connect(adr.multipassOwner.wallet) + .initializeDomain( + adr.registrar1.wallet.address, + 1000, + ethers.utils.parseEther('3'), + ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + ethers.utils.parseEther('1'), + ethers.utils.parseEther('1'), + ), + ).to.emit(env.multipass, 'InitializedDomain'); + expect(await env.multipass.getContractState()).to.be.equal(1); + }); + it('Reverts domain specific methods if domain does not exists', async () => { + await expect( + env.multipass + .connect(adr.multipassOwner.wallet) + .activateDomain(ethers.utils.formatBytes32String('invalidDomain')), + ).to.be.revertedWith('Domain does not exist'); + await expect( + env.multipass + .connect(adr.multipassOwner.wallet) + .deactivateDomain(ethers.utils.formatBytes32String('invalidDomain')), + ).to.be.revertedWith('Domain does not exist'); + await expect( + env.multipass + .connect(adr.multipassOwner.wallet) + .changeFee(ethers.utils.formatBytes32String('invalidDomain'), '1'), + ).to.be.revertedWith('Domain does not exist'); + await expect( + env.multipass + .connect(adr.multipassOwner.wallet) + .changeRegistrar(ethers.utils.formatBytes32String('invalidDomain'), adr.gameCreator1.wallet.address), + ).to.be.revertedWith('Domain does not exist'); + const invalidDomainQuery = { ...emptyUserQuery }; + invalidDomainQuery.domainName = ethers.utils.formatBytes32String('invalidDomain'); + invalidDomainQuery.targetDomain = ethers.utils.formatBytes32String('invalidDomain'); + + const registrarMessage = { + name: ethers.utils.formatBytes32String(adr.player1.name), + id: ethers.utils.formatBytes32String(adr.player1.id), + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + deadline: ethers.BigNumber.from(9999), + nonce: ethers.BigNumber.from(0), + }; + + const registarSignature = await signRegistrarMessage(registrarMessage, env.multipass.address, adr.registrar1); + + await expect(env.multipass.connect(adr.multipassOwner.wallet).deleteName(invalidDomainQuery)).to.be.revertedWith( + 'Domain does not exist', + ); + + let applicantData: LibMultipass.RecordStruct = { + name: ethers.utils.formatBytes32String(adr.player1.name), + id: ethers.utils.formatBytes32String(adr.player1.id), + wallet: adr.player1.wallet.address, + nonce: 0, + domainName: ethers.utils.formatBytes32String('invalidDomain'), + }; + + await expect( + env.multipass + .connect(adr.multipassOwner.wallet) + .register( + applicantData, + ethers.utils.formatBytes32String('invalidDomain'), + registarSignature, + registrarMessage.deadline, + emptyUserQuery, + ethers.constants.HashZero, + ), + ).to.be.revertedWith('Domain does not exist'); + }); + it('Reverts if intializing domain name props are wrong', async () => { + await expect( + env.multipass + .connect(adr.multipassOwner.wallet) + .initializeDomain( + adr.registrar1.wallet.address, + DEFAULT_FREE_REGISTRATIONS, + ethers.utils.parseEther('3'), + ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + ethers.utils.parseEther('1.0001'), + ethers.utils.parseEther('2'), + ), + ).to.be.revertedWith('Multipass->initializeDomain: referral values are higher then fee itself'); + await expect( + env.multipass + .connect(adr.multipassOwner.wallet) + .initializeDomain( + ethers.constants.AddressZero, + 1000, + ethers.utils.parseEther('3'), + ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + ethers.utils.parseEther('1.0001'), + ethers.utils.parseEther('2'), + ), + ).to.be.revertedWith('Multipass->initializeDomain: You must provide a registrar address'); + await expect( + env.multipass + .connect(adr.multipassOwner.wallet) + .initializeDomain( + adr.registrar1.wallet.address, + 1000, + ethers.utils.parseEther('3'), + ethers.utils.formatBytes32String(''), + ethers.utils.parseEther('1'), + ethers.utils.parseEther('2'), + ), + ).to.be.revertedWith('Multipass->initializeDomain: Domain name cannot be empty'); + + await expect( + env.multipass + .connect(adr.multipassOwner.wallet) + .initializeDomain( + adr.registrar1.wallet.address, + 1000, + ethers.constants.MaxUint256, + ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + ethers.constants.MaxUint256, + ethers.utils.parseEther('1'), + ), + ).to.be.revertedWith('Multipass->initializeDomain: referrerReward + referralDiscount overflow'); + }); + it('Reverts any ownerOnly call by not an owner', async () => { + await expect( + env.multipass.connect(adr.maliciousActor1.wallet).withrawFunds(adr.maliciousActor1.wallet.address), + ).to.be.revertedWith('LibDiamond: Must be contract owner'); + await expect( + env.multipass + .connect(adr.maliciousActor1.wallet) + .changeReferralProgram( + DEFAULT_REWARD, + DEFAULT_FREE_REGISTRATIONS, + DEFAULT_DISCOUNT, + ethers.utils.formatBytes32String(''), + ), + ).to.be.revertedWith('LibDiamond: Must be contract owner'); + await expect(env.multipass.connect(adr.maliciousActor1.wallet).deleteName(emptyUserQuery)).to.be.revertedWith( + 'LibDiamond: Must be contract owner', + ); + await expect( + env.multipass + .connect(adr.maliciousActor1.wallet) + .changeRegistrar(ethers.utils.formatBytes32String(''), adr.maliciousActor1.wallet.address), + ).to.be.revertedWith('LibDiamond: Must be contract owner'); + await expect( + env.multipass.connect(adr.maliciousActor1.wallet).changeFee(ethers.utils.formatBytes32String(''), DEFAULT_FEE), + ).to.be.revertedWith('LibDiamond: Must be contract owner'); + await expect( + env.multipass.connect(adr.maliciousActor1.wallet).activateDomain(ethers.utils.formatBytes32String('')), + ).to.be.revertedWith('LibDiamond: Must be contract owner'); + await expect( + env.multipass.connect(adr.maliciousActor1.wallet).deactivateDomain(ethers.utils.formatBytes32String('')), + ).to.be.revertedWith('LibDiamond: Must be contract owner'); + await expect( + env.multipass + .connect(adr.maliciousActor1.wallet) + .initializeDomain( + adr.maliciousActor1.wallet.address, + DEFAULT_FREE_REGISTRATIONS, + DEFAULT_FEE, + ethers.utils.formatBytes32String(''), + DEFAULT_REWARD, + DEFAULT_DISCOUNT, + ), + ).to.be.revertedWith('LibDiamond: Must be contract owner'); + }); + describe('When a new domain was initialized', () => { + let numDomains = 0; + beforeEach(async () => { + await env.multipass + .connect(adr.multipassOwner.wallet) + .initializeDomain( + adr.registrar1.wallet.address, + DEFAULT_FREE_REGISTRATIONS, + DEFAULT_FEE, + ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + DEFAULT_REWARD, + DEFAULT_DISCOUNT, + ); + numDomains = 1; + }); + it('Reverts if domain name already registered', async () => { + await expect( + env.multipass + .connect(adr.multipassOwner.wallet) + .initializeDomain( + adr.registrar1.wallet.address, + DEFAULT_FREE_REGISTRATIONS, + DEFAULT_FEE, + ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + DEFAULT_REWARD, + DEFAULT_DISCOUNT, + ), + ).to.be.revertedWith('Multipass->initializeDomain: Domain name already exists'); + }); + it('Domain name state is equal to initial values and is not active', async () => { + const resp = await env.multipass.getDomainState(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1)); + // expect(ethers.utils.parseBytes32String(resp)).to.be.equal( + // NEW_DOMAIN_NAME1 + // ); + expect(ethers.utils.parseBytes32String(resp['name'])).to.be.equal(NEW_DOMAIN_NAME1); + expect(resp['fee']).to.be.equal(DEFAULT_FEE); + expect(resp['freeRegistrationsNumber']).to.be.equal(DEFAULT_FREE_REGISTRATIONS); + expect(resp['referrerReward']).to.be.equal(DEFAULT_REWARD); + expect(resp['referralDiscount']).to.be.equal(DEFAULT_DISCOUNT); + expect(resp['isActive']).to.be.equal(false); + expect(resp['registrar']).to.be.equal(adr.registrar1.wallet.address); + expect(resp['ttl']).to.be.equal(0); + expect(resp['registerSize'].toString()).to.be.equal('0'); + }); + it('Incremented number of domains', async () => { + expect(await env.multipass.getContractState()).to.be.equal(numDomains); + }); + it('emits when domain activated', async () => { + await expect( + env.multipass + .connect(adr.multipassOwner.wallet) + .activateDomain(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1)), + ).to.emit(env.multipass, 'DomainActivated'); + }); + it('Does not allow to register because is not active', async () => { + let applicantData: LibMultipass.RecordStruct = { + name: ethers.utils.formatBytes32String(adr.player1.name), + id: ethers.utils.formatBytes32String(adr.player1.id), + wallet: adr.player1.wallet.address, + nonce: 0, + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + }; + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + applicantData, + ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + ethers.constants.HashZero, + ethers.constants.HashZero, + emptyUserQuery, + ethers.constants.HashZero, + ), + ).to.be.revertedWith('Multipass->register: domain is not active'); + }); + it('Emits and changes fee', async () => { + await expect( + env.multipass + .connect(adr.multipassOwner.wallet) + .changeFee(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), FEE_AFTER_CHANGE), + ).to.emit(env.multipass, 'DomainFeeChanged'); + + const resp = await env.multipass + .connect(adr.player1.wallet) + .getDomainState(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1)); + expect(resp[1]).to.be.equal(FEE_AFTER_CHANGE); + }); + describe('when domain was set to active', () => { + beforeEach(async () => { + await env.multipass + .connect(adr.multipassOwner.wallet) + .activateDomain(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1)); + }); + it('Is set to active', async () => { + const resp = await env.multipass.getDomainState(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1)); + expect(resp['isActive']).to.be.true; + }); + + it('Emits on register when properties are valid', async () => { + const registrarMessage = { + name: ethers.utils.formatBytes32String(adr.player1.name), + id: ethers.utils.formatBytes32String(adr.player1.id), + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + deadline: ethers.BigNumber.from(9999), + nonce: ethers.BigNumber.from(0), + }; + + const registarSignature = await signRegistrarMessage(registrarMessage, env.multipass.address, adr.registrar1); + + let applicantData: LibMultipass.RecordStruct = { + name: ethers.utils.formatBytes32String(adr.player1.name), + id: ethers.utils.formatBytes32String(adr.player1.id), + wallet: adr.player1.wallet.address, + nonce: 0, + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + }; + + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + applicantData, + registrarMessage.domainName, + registarSignature, + registrarMessage.deadline, + emptyUserQuery, + ethers.constants.HashZero, + ), + ).to.emit(env.multipass, 'Registered'); + }); + + it('Reverts on register if properties are invalid', async () => { + const registrarMessage = { + name: ethers.utils.formatBytes32String(adr.player1.name), + id: ethers.utils.formatBytes32String(adr.player1.id), + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + deadline: ethers.BigNumber.from(9999), + nonce: ethers.BigNumber.from(0), + }; + + const invalidRegistrarSignature = await signRegistrarMessage( + registrarMessage, + env.multipass.address, + adr.maliciousActor1, + ); + + let applicantData: LibMultipass.RecordStruct = { + name: ethers.utils.formatBytes32String(adr.player1.name), + id: ethers.utils.formatBytes32String(adr.player1.id), + wallet: adr.player1.wallet.address, + nonce: 0, + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + }; + + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + applicantData, + registrarMessage.domainName, + invalidRegistrarSignature, + registrarMessage.deadline, + emptyUserQuery, + ethers.constants.HashZero, + ), + ).to.be.revertedWith('Multipass->register: Registrar signature is not valid'); + + registrarMessage.deadline = ethers.BigNumber.from(ethers.provider.blockNumber); + + // await expect( + // env.multipass + // .connect(adr.player1.wallet) + // .register( + // applicantData, + // registrarMessage.domainName, + // await signRegistrarMessage(registrarMessage, env.multipass.address, adr.registrar1), + // registrarMessage.deadline, + // emptyUserQuery, + // ethers.constants.HashZero, + // ), + // ).to.be.revertedWith('Multipass->register: Deadline is less than current block number'); + }); + it('Reverts if signature is outdated', async () => { + const registrantProps1 = await getUserRegisterProps( + adr.player2, + adr.registrar1, + NEW_DOMAIN_NAME1, + 1, + env.multipass.address, + ); + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + registrantProps1.applicantData, + registrantProps1.registrarMessage.domainName, + registrantProps1.validSignature, + registrantProps1.registrarMessage.deadline, + emptyUserQuery, + ethers.constants.HashZero, + ), + ).to.be.revertedWith('Multipass->register: Deadline is less than current block number'); + }); + it('Allows valid registrations for free until free tier has reached', async () => { + const size = DEFAULT_FREE_REGISTRATIONS.toNumber(); + const players = [adr.player1, adr.player2, adr.player3, adr.player4, adr.player5]; + let i = 0; + for (i = 0; i < size; i++) { + const regProps = await getUserRegisterProps( + players[i], + adr.registrar1, + NEW_DOMAIN_NAME1, + 99999, + env.multipass.address, + ); + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + regProps.applicantData, + regProps.registrarMessage.domainName, + regProps.validSignature, + regProps.registrarMessage.deadline, + emptyUserQuery, + ethers.constants.HashZero, + ), + ).to.emit(env.multipass, 'Registered'); + } + const regProps = await getUserRegisterProps( + players[i], + adr.registrar1, + NEW_DOMAIN_NAME1, + 99999, + env.multipass.address, + ); + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + regProps.applicantData, + regProps.registrarMessage.domainName, + regProps.validSignature, + regProps.registrarMessage.deadline, + emptyUserQuery, + ethers.constants.HashZero, + ), + ).to.be.revertedWith('Multipass->register: Payment value is not enough'); + }); + + describe('When user was registered', () => { + let numDomains = 0; + beforeEach(async () => { + const regProps = await getUserRegisterProps( + adr.player1, + adr.registrar1, + NEW_DOMAIN_NAME1, + 99999, + env.multipass.address, + ); + + await env.multipass + .connect(adr.player1.wallet) + .register( + regProps.applicantData, + regProps.registrarMessage.domainName, + regProps.validSignature, + regProps.registrarMessage.deadline, + emptyUserQuery, + ethers.constants.HashZero, + ); + }); + it('Can find newly registered user ', async () => { + //By full query + let query: LibMultipass.NameQueryStruct = { + name: ethers.utils.formatBytes32String(adr.player1.name + `.` + NEW_DOMAIN_NAME1), + id: ethers.utils.formatBytes32String(adr.player1.id + `.` + NEW_DOMAIN_NAME1), + wallet: adr.player1.wallet.address, + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + targetDomain: ethers.utils.formatBytes32String(''), + }; + + let resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(query); + expect(resp[0]).to.be.true; + + //With id and address + query = { + name: ethers.utils.formatBytes32String(''), + id: ethers.utils.formatBytes32String(adr.player1.id + `.` + NEW_DOMAIN_NAME1), + wallet: adr.player1.wallet.address, + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + targetDomain: ethers.utils.formatBytes32String(''), + }; + + resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(query); + expect(resp[0]).to.be.true; + + //With only id + query = { + name: ethers.utils.formatBytes32String(''), + id: ethers.utils.formatBytes32String(adr.player1.id + `.` + NEW_DOMAIN_NAME1), + wallet: ethers.constants.AddressZero, + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + targetDomain: ethers.utils.formatBytes32String(''), + }; + + resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(query); + expect(resp[0]).to.be.true; + + //With only address + query = { + name: ethers.utils.formatBytes32String(''), + id: ethers.utils.formatBytes32String(''), + wallet: adr.player1.wallet.address, + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + targetDomain: ethers.utils.formatBytes32String(''), + }; + + resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(query); + expect(resp[0]).to.be.true; + + //With only name + query = { + name: ethers.utils.formatBytes32String(adr.player1.name + `.` + NEW_DOMAIN_NAME1), + id: ethers.utils.formatBytes32String(''), + wallet: ethers.constants.AddressZero, + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + targetDomain: ethers.utils.formatBytes32String(''), + }; + + resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(query); + expect(resp[0]).to.be.true; + }); + it('Reverts registration if user id already exist', async () => { + const regProps = await getUserRegisterProps( + adr.player1, + adr.registrar1, + NEW_DOMAIN_NAME1, + 99999, + env.multipass.address, + ); + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + regProps.applicantData, + regProps.registrarMessage.domainName, + regProps.validSignature, + regProps.registrarMessage.deadline, + emptyUserQuery, + ethers.constants.HashZero, + ), + ).to.be.revertedWith('User already registered, use modify instead'); + regProps.applicantData.id = ethers.utils.formatBytes32String(adr.player2.id); + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + regProps.applicantData, + regProps.registrarMessage.domainName, + regProps.validSignature, + regProps.registrarMessage.deadline, + emptyUserQuery, + ethers.constants.HashZero, + ), + ).to.be.revertedWith('User already registered, use modify instead'); + regProps.applicantData.name = ethers.utils.formatBytes32String(adr.player2.name); + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + regProps.applicantData, + regProps.registrarMessage.domainName, + regProps.validSignature, + regProps.registrarMessage.deadline, + emptyUserQuery, + ethers.constants.HashZero, + ), + ).to.be.revertedWith('User already registered, use modify instead'); + }); + it('Emits when register with valid referral code', async () => { + const registrantProps = await getUserRegisterProps( + adr.player2, + adr.registrar1, + NEW_DOMAIN_NAME1, + 99999, + env.multipass.address, + adr.player1, + ); + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + registrantProps.applicantData, + registrantProps.registrarMessage.domainName, + registrantProps.validSignature, + registrantProps.registrarMessage.deadline, + registrantProps.referrerData, + registrantProps.referrerSignature, + ), + ).to.emit(env.multipass, 'Referred'); + }); + it('Can modify username with a valid arguments', async () => { + const registrarMessage = { + name: ethers.utils.formatBytes32String(adr.player1.name + `.` + NEW_DOMAIN_NAME1 + `new`), + id: ethers.utils.formatBytes32String(adr.player1.id + `.` + NEW_DOMAIN_NAME1), + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + deadline: ethers.BigNumber.from(99999), + nonce: ethers.BigNumber.from(1), + }; + + const modifyQuery: LibMultipass.NameQueryStruct = { + name: ethers.utils.formatBytes32String(adr.player1.name + `.` + NEW_DOMAIN_NAME1), + id: registrarMessage.id, + domainName: registrarMessage.domainName, + wallet: adr.player1.wallet.address, + targetDomain: ethers.utils.formatBytes32String(''), + }; + + const validSignature = await signRegistrarMessage(registrarMessage, env.multipass.address, adr.registrar1); + + await expect( + env.multipass + .connect(adr.player1.wallet) + .modifyUserName( + registrarMessage.domainName, + modifyQuery, + registrarMessage.name, + validSignature, + registrarMessage.deadline, + ), + ).to.be.revertedWith('Multipass->modifyUserName: Not enough payment'); + + const valueToPay = env.multipass.getModifyPrice(modifyQuery); + await expect( + env.multipass + .connect(adr.player1.wallet) + .modifyUserName( + registrarMessage.domainName, + modifyQuery, + registrarMessage.name, + validSignature, + registrarMessage.deadline, + { value: valueToPay }, + ), + ).to.be.emit(env.multipass, 'UserRecordModified'); + }); + it('Emits and deletes user', async () => { + let query: LibMultipass.NameQueryStruct = { + name: ethers.utils.formatBytes32String(adr.player1.name + `.` + NEW_DOMAIN_NAME1), + id: ethers.utils.formatBytes32String(adr.player1.id + `.` + NEW_DOMAIN_NAME1), + wallet: adr.player1.wallet.address, + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + targetDomain: ethers.utils.formatBytes32String(''), + }; + + await expect(env.multipass.connect(adr.multipassOwner.wallet).deleteName(query)).to.emit( + env.multipass, + 'nameDeleted', + ); + + let resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(query); + expect(resp[0]).to.be.false; + }); + describe('When second domain is initialized and active', () => { + beforeEach(async () => { + await env.multipass + .connect(adr.multipassOwner.wallet) + .initializeDomain( + adr.registrar1.wallet.address, + DEFAULT_FREE_REGISTRATIONS, + DEFAULT_FEE, + ethers.utils.formatBytes32String(NEW_DOMAIN_NAME2), + DEFAULT_REWARD, + DEFAULT_DISCOUNT, + ); + await env.multipass + .connect(adr.multipassOwner.wallet) + .activateDomain(ethers.utils.formatBytes32String(NEW_DOMAIN_NAME2)); + }); + it('Reverts on referring yourself from a different domain', async () => { + const registrantProps1 = await getUserRegisterProps( + adr.player2, + adr.registrar1, + NEW_DOMAIN_NAME1, + 99999, + env.multipass.address, + ); + await env.multipass + .connect(adr.player2.wallet) + .register( + registrantProps1.applicantData, + registrantProps1.registrarMessage.domainName, + registrantProps1.validSignature, + registrantProps1.registrarMessage.deadline, + registrantProps1.referrerData, + registrantProps1.referrerSignature, + ); + const registrantProps2 = await getUserRegisterProps( + adr.player2, + adr.registrar1, + NEW_DOMAIN_NAME2, + 99999, + env.multipass.address, + adr.player2, + NEW_DOMAIN_NAME1, + ); + const registrantProps21 = await getUserRegisterProps( + adr.player2, + adr.registrar1, + NEW_DOMAIN_NAME1, + 99999, + env.multipass.address, + adr.player2, + ); + await expect( + env.multipass + .connect(adr.player2.wallet) + .register( + registrantProps2.applicantData, + registrantProps2.registrarMessage.domainName, + registrantProps2.validSignature, + registrantProps2.registrarMessage.deadline, + registrantProps21.referrerData, + registrantProps2.referrerSignature, + ), + ).to.revertedWith('Cannot refer yourself'); + }); + + it('Can register same user on both domains and do cross domain lookup', async () => { + const registrantProps1 = await getUserRegisterProps( + adr.player1, + adr.registrar1, + NEW_DOMAIN_NAME2, + 99999, + env.multipass.address, + ); + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + registrantProps1.applicantData, + registrantProps1.registrarMessage.domainName, + registrantProps1.validSignature, + registrantProps1.registrarMessage.deadline, + registrantProps1.referrerData, + registrantProps1.referrerSignature, + ), + ).to.emit(env.multipass, 'Registered'); + + const crossDomainQuery: LibMultipass.NameQueryStruct = { + name: ethers.utils.formatBytes32String(adr.player1.name + `.` + NEW_DOMAIN_NAME2), + id: ethers.utils.formatBytes32String(''), + domainName: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME2), + wallet: ethers.constants.AddressZero, + targetDomain: ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + }; + const resp = await env.multipass.connect(adr.player1.wallet).resolveRecord(crossDomainQuery); + expect(resp[0]).to.be.true; + expect(ethers.utils.parseBytes32String(resp[1][1])).to.be.equal(adr.player1.name + `.` + NEW_DOMAIN_NAME1); + }); + }); + }); + describe('When free number of free registrations has been reached', () => { + beforeEach(async () => { + await env.multipass + .connect(adr.multipassOwner.wallet) + .changeReferralProgram( + DEFAULT_REWARD, + 0, + DEFAULT_DISCOUNT, + ethers.utils.formatBytes32String(NEW_DOMAIN_NAME1), + ); + }); + it('Should allow registering with paying ether', async () => { + const registrantProps1 = await getUserRegisterProps( + adr.player1, + adr.registrar1, + NEW_DOMAIN_NAME1, + 99999, + env.multipass.address, + ); + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + registrantProps1.applicantData, + registrantProps1.registrarMessage.domainName, + registrantProps1.validSignature, + registrantProps1.registrarMessage.deadline, + registrantProps1.referrerData, + registrantProps1.referrerSignature, + { value: DEFAULT_FEE }, + ), + ).to.emit(env.multipass, 'Registered'); + }); + it('Should be able withraw ammount payed', async () => { + const registrantProps1 = await getUserRegisterProps( + adr.player1, + adr.registrar1, + NEW_DOMAIN_NAME1, + 99999, + env.multipass.address, + ); + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + registrantProps1.applicantData, + registrantProps1.registrarMessage.domainName, + registrantProps1.validSignature, + registrantProps1.registrarMessage.deadline, + registrantProps1.referrerData, + registrantProps1.referrerSignature, + { value: DEFAULT_FEE }, + ), + ).to.emit(env.multipass, 'Registered'); + await expect( + await env.multipass.connect(adr.multipassOwner.wallet).withrawFunds(adr.multipassOwner.wallet.address), + ).to.changeEtherBalance(adr.multipassOwner.wallet, DEFAULT_FEE); + }); + it('Should revert register if not enough ether', async () => { + const registrantProps1 = await getUserRegisterProps( + adr.player1, + adr.registrar1, + NEW_DOMAIN_NAME1, + 99999, + env.multipass.address, + ); + await expect( + env.multipass + .connect(adr.player1.wallet) + .register( + registrantProps1.applicantData, + registrantProps1.registrarMessage.domainName, + registrantProps1.validSignature, + registrantProps1.registrarMessage.deadline, + registrantProps1.referrerData, + registrantProps1.referrerSignature, + { value: NOT_ENOUGH_FEE }, + ), + ).to.be.revertedWith('Multipass->register: Payment value is not enough'); + }); + }); + }); + }); +}); diff --git a/test/RankifyInstance.ts b/test/RankifyInstance.ts index b6ac86c..f744b21 100644 --- a/test/RankifyInstance.ts +++ b/test/RankifyInstance.ts @@ -13,15 +13,7 @@ import { RInstance_MAX_TURNS, RInstance_TIME_PER_TURN, } from './utils'; -import { - setupAddresses, - setupEnvironment, - RInstanceSettings, - mineBlocks, - mockProposals, - mockVotes, - getPlayers, -} from './utils'; +import { RInstanceSettings, mineBlocks, mockProposals, mockVotes, getPlayers } from './utils'; import { expect } from 'chai'; import { time } from '@nomicfoundation/hardhat-network-helpers'; import { RankifyDiamondInstance } from '../types/'; diff --git a/test/utils.ts b/test/utils.ts index 5be40d3..a29c242 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -6,12 +6,25 @@ import hre, { deployments, config } from 'hardhat'; import aes from 'crypto-js/aes'; import { ethers } from 'hardhat'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; -import { Rankify, MockERC1155, MockERC20, MockERC721, RankToken, RankifyDiamondInstance } from '../types'; +import { + Rankify, + MockERC1155, + MockERC20, + MockERC721, + RankToken, + RankifyDiamondInstance, + MultipassDiamond, +} from '../types'; import { BigNumber, BigNumberish, BytesLike, Wallet } from 'ethers'; // @ts-ignore import { assert } from 'console'; import { Deployment } from 'hardhat-deploy/types'; import { HardhatEthersHelpers } from '@nomiclabs/hardhat-ethers/types'; +import MultipassJs from '../../sdk/src/multipass'; +import { LibMultipass } from '../types/src/facets/DNSFacet'; + +export const MULTIPASS_CONTRACT_NAME = 'MultipassDNS'; +export const MULTIPASS_CONTRACT_VERSION = '0.0.1'; export interface SignerIdentity { name: string; @@ -48,12 +61,14 @@ export interface AdrSetupResult { gameMaster2: SignerIdentity; gameMaster3: SignerIdentity; gameOwner: SignerIdentity; + multipassOwner: SignerIdentity; registrar1: SignerIdentity; } export interface EnvSetupResult { rankifyToken: Rankify; rankifyInstance: RankifyDiamondInstance; + multipass: MultipassDiamond; rankToken: RankToken; mockERC20: MockERC20; mockERC1155: MockERC1155; @@ -251,6 +266,7 @@ export const setupAddresses = async ( maliciousActor2, maliciousActor3, gameOwner, + multipassOwner: gameOwner, }; }; @@ -293,7 +309,7 @@ export const setupTest = deployments.createFixture(async ({ deployments, getName to: owner, value: _eth.utils.parseEther('1'), }); - await deployments.fixture(['rankify']); + await deployments.fixture(['rankify', 'multipass']); const MockERC20F = await _eth.getContractFactory('MockERC20', adr.contractDeployer.wallet); const mockERC20 = (await MockERC20F.deploy('Mock ERC20', 'MCK20', adr.contractDeployer.wallet.address)) as MockERC20; await mockERC20.deployed(); @@ -313,6 +329,7 @@ export const setupTest = deployments.createFixture(async ({ deployments, getName RankifyToken: await deployments.get('Rankify'), RankToken: await deployments.get('RankToken'), RankifyInstance: await deployments.get('RankifyInstance'), + multipass: await deployments.get('Multipass'), mockERC20: mockERC20, mockERC721: mockERC721, mockERC1155: mockERC1155, @@ -429,6 +446,7 @@ export const setupEnvironment = async (setup: { mockERC20: MockERC20; mockERC721: MockERC721; mockERC1155: MockERC1155; + multipass: Deployment; adr: AdrSetupResult; }): Promise => { const rankToken = (await ethers.getContractAt(setup.RankToken.abi, setup.RankToken.address)) as RankToken; @@ -437,10 +455,12 @@ export const setupEnvironment = async (setup: { setup.RankifyInstance.abi, setup.RankifyInstance.address, )) as RankifyDiamondInstance; + const multipass = (await ethers.getContractAt(setup.multipass.abi, setup.multipass.address)) as MultipassDiamond; return { rankifyToken, rankifyInstance, + multipass, rankToken, mockERC1155: setup.mockERC1155, mockERC20: setup.mockERC20, @@ -461,13 +481,6 @@ interface RegisterMessage { type signatureMessage = ReferrerMesage | RegisterMessage; -export default { - setupAddresses, - setupEnvironment, - addPlayerNameId, - baseFee, -}; - export async function mineBlocks(count: any) { for (let i = 0; i < count; i += 1) { await ethers.provider.send('evm_mine', []); @@ -840,3 +853,99 @@ export const mockProposals = async ({ } return proposals; }; + +export const signReferralCode = async (message: ReferrerMesage, verifierAddress: string, signer: SignerIdentity) => { + let { chainId } = await ethers.provider.getNetwork(); + + const domain = { + name: MULTIPASS_CONTRACT_NAME, + version: MULTIPASS_CONTRACT_VERSION, + chainId, + verifyingContract: verifierAddress, + }; + + const types = { + proofOfReferrer: [ + { + type: 'address', + name: 'referrerAddress', + }, + ], + }; + const s = await signer.wallet._signTypedData(domain, types, { ...message }); + return s; +}; + +export const getUserRegisterProps = async ( + account: SignerIdentity, + registrar: SignerIdentity, + domainName: string, + deadline: number, + multipassAddress: string, + referrer?: SignerIdentity, + referrerDomain?: string, +) => { + const registrarMessage = { + name: ethers.utils.formatBytes32String(account.name + `.` + domainName), + id: ethers.utils.formatBytes32String(account.id + `.` + domainName), + domainName: ethers.utils.formatBytes32String(domainName), + deadline: ethers.BigNumber.from(deadline), + nonce: ethers.BigNumber.from(0), + }; + + const validSignature = await signRegistrarMessage(registrarMessage, multipassAddress, registrar); + + const applicantData: LibMultipass.RecordStruct = { + name: ethers.utils.formatBytes32String(account.name + `.` + domainName), + id: ethers.utils.formatBytes32String(account.id + `.` + domainName), + wallet: account.wallet.address, + nonce: 0, + domainName: ethers.utils.formatBytes32String(domainName), + }; + + const referrerData: LibMultipass.NameQueryStruct = { + name: ethers.utils.formatBytes32String(referrer?.name ? referrer?.name + `.` + domainName : ''), + domainName: ethers.utils.formatBytes32String(domainName), + id: ethers.utils.formatBytes32String(''), + wallet: ethers.constants.AddressZero, + targetDomain: ethers.utils.formatBytes32String(referrerDomain ?? ''), + }; + let referrerSignature = ethers.constants.HashZero; + const proofOfReferrer: ReferrerMesage = { + referrerAddress: referrer?.wallet.address ?? ethers.constants.AddressZero, + }; + if (referrer?.wallet.address) { + referrerSignature = await signReferralCode(proofOfReferrer, multipassAddress, referrer); + } + + return { + registrarMessage, + validSignature, + applicantData, + referrerData, + referrerSignature, + }; +}; +export const signRegistrarMessage = async ( + message: RegisterMessage, + verifierAddress: string, + signer: SignerIdentity, +) => { + let { chainId } = await ethers.provider.getNetwork(); + + const multipassJs = new MultipassJs({ + chainId: chainId, + contractName: MULTIPASS_CONTRACT_NAME, + version: MULTIPASS_CONTRACT_VERSION, + ...hre.network, + }); + return await multipassJs.signRegistrarMessage(message, verifierAddress, signer.wallet); +}; + +export default { + setupAddresses, + setupEnvironment, + addPlayerNameId, + baseFee, + signMessage: signRegistrarMessage, +}; diff --git a/types/index.ts b/types/index.ts index 12e473b..b6a9fa1 100644 --- a/types/index.ts +++ b/types/index.ts @@ -44,12 +44,16 @@ export type { ERC165 } from "./@openzeppelin/contracts/utils/introspection/ERC16 export { ERC165__factory } from "./factories/@openzeppelin/contracts/utils/introspection/ERC165__factory"; export type { IERC165 } from "./@openzeppelin/contracts/utils/introspection/IERC165"; export { IERC165__factory } from "./factories/@openzeppelin/contracts/utils/introspection/IERC165__factory"; +export type { MultipassDiamond } from "./hardhat-diamond-abi/HardhatDiamondABI.sol/MultipassDiamond"; +export { MultipassDiamond__factory } from "./factories/hardhat-diamond-abi/HardhatDiamondABI.sol/MultipassDiamond__factory"; export type { RankifyDiamondInstance } from "./hardhat-diamond-abi/HardhatDiamondABI.sol/RankifyDiamondInstance"; export { RankifyDiamondInstance__factory } from "./factories/hardhat-diamond-abi/HardhatDiamondABI.sol/RankifyDiamondInstance__factory"; export type { CompositeERC1155 } from "./src/abstracts/CompositeERC1155"; export { CompositeERC1155__factory } from "./factories/src/abstracts/CompositeERC1155__factory"; export type { LockableERC1155 } from "./src/abstracts/LockableERC1155"; export { LockableERC1155__factory } from "./factories/src/abstracts/LockableERC1155__factory"; +export type { DNSFacet } from "./src/facets/DNSFacet"; +export { DNSFacet__factory } from "./factories/src/facets/DNSFacet__factory"; export type { EIP712InspectorFacet } from "./src/facets/EIP712InspectorFacet"; export { EIP712InspectorFacet__factory } from "./factories/src/facets/EIP712InspectorFacet__factory"; export type { RankifyInstanceGameMastersFacet } from "./src/facets/RankifyInstanceGameMastersFacet"; @@ -62,10 +66,14 @@ export type { RankifyInstanceRequirementsFacet } from "./src/facets/RankifyInsta export { RankifyInstanceRequirementsFacet__factory } from "./factories/src/facets/RankifyInstanceRequirementsFacet__factory"; export type { DiamondInit } from "./src/initializers/DiamondInit"; export { DiamondInit__factory } from "./factories/src/initializers/DiamondInit__factory"; +export type { MultipassInit } from "./src/initializers/MultipassInit"; +export { MultipassInit__factory } from "./factories/src/initializers/MultipassInit__factory"; export type { RankifyInstanceInit } from "./src/initializers/RankifyInstanceInit"; export { RankifyInstanceInit__factory } from "./factories/src/initializers/RankifyInstanceInit__factory"; export type { ILockableERC1155 } from "./src/interfaces/ILockableERC1155"; export { ILockableERC1155__factory } from "./factories/src/interfaces/ILockableERC1155__factory"; +export type { IMultipass } from "./src/interfaces/IMultipass"; +export { IMultipass__factory } from "./factories/src/interfaces/IMultipass__factory"; export type { IRankifyInstanceCommons } from "./src/interfaces/IRankifyInstanceCommons"; export { IRankifyInstanceCommons__factory } from "./factories/src/interfaces/IRankifyInstanceCommons__factory"; export type { IRankToken } from "./src/interfaces/IRankToken"; From c9eb6b540a6f2fe780984eb4e979753f56a6bf88 Mon Sep 17 00:00:00 2001 From: Tim Pechersky Date: Thu, 25 Jan 2024 16:00:39 +0000 Subject: [PATCH 2/3] changeset --- .changeset/beige-hounds-trade.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/beige-hounds-trade.md diff --git a/.changeset/beige-hounds-trade.md b/.changeset/beige-hounds-trade.md new file mode 100644 index 0000000..2cb1036 --- /dev/null +++ b/.changeset/beige-hounds-trade.md @@ -0,0 +1,5 @@ +--- +'rankify-contracts': minor +--- + +Adding multipass contracts From 69302dab9f93eba944909b8b5b7e90252023232e Mon Sep 17 00:00:00 2001 From: Tim Pechersky Date: Thu, 25 Jan 2024 16:03:17 +0000 Subject: [PATCH 3/3] linter fixes --- src/facets/DNSFacet.sol | 2 +- src/initializers/MultipassInit.sol | 7 +++---- src/interfaces/IMultipass.sol | 9 ++++----- src/libraries/LibMultipass.sol | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/facets/DNSFacet.sol b/src/facets/DNSFacet.sol index 55669af..7b35dfd 100644 --- a/src/facets/DNSFacet.sol +++ b/src/facets/DNSFacet.sol @@ -301,4 +301,4 @@ contract DNSFacet is EIP712, IMultipass { function withrawFunds(address to) public override onlyOwner { payable(to).transfer(address(this).balance); } -} \ No newline at end of file +} diff --git a/src/initializers/MultipassInit.sol b/src/initializers/MultipassInit.sol index dd28722..c1b3b26 100644 --- a/src/initializers/MultipassInit.sol +++ b/src/initializers/MultipassInit.sol @@ -23,8 +23,7 @@ import "../modifiers/OnlyOwnerDiamond.sol"; // of your diamond. Add parameters to the init funciton if you need to. contract MultipassInit is OnlyOwnerDiamond { - - function _buildDomainSeparator( + function _buildDomainSeparator( bytes32 typeHash, bytes32 nameHash, bytes32 versionHash @@ -40,7 +39,7 @@ contract MultipassInit is OnlyOwnerDiamond { ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true; ds.supportedInterfaces[type(IERC173).interfaceId] = true; - bytes32 hashedName = keccak256(bytes(name)); + bytes32 hashedName = keccak256(bytes(name)); bytes32 hashedVersion = keccak256(bytes(version)); bytes32 typeHash = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" @@ -62,4 +61,4 @@ contract MultipassInit is OnlyOwnerDiamond { // in order to set state variables in the diamond during deployment or an upgrade // More info here: https://eips.ethereum.org/EIPS/eip-2535#diamond-interface } -} \ No newline at end of file +} diff --git a/src/interfaces/IMultipass.sol b/src/interfaces/IMultipass.sol index e859cd7..e090b2d 100644 --- a/src/interfaces/IMultipass.sol +++ b/src/interfaces/IMultipass.sol @@ -4,10 +4,9 @@ pragma solidity ^0.8.4; import "../libraries/LibMultipass.sol"; interface IMultipass { - function resolveRecord(LibMultipass.NameQuery memory query) - external - view - returns (bool, LibMultipass.Record memory); + function resolveRecord( + LibMultipass.NameQuery memory query + ) external view returns (bool, LibMultipass.Record memory); /** @dev same as resolveRecord but returns username, id and LibMultipass.Domain as string */ // function resolveRecordToString(LibMultipass.NameQuery memory query) @@ -237,4 +236,4 @@ interface IMultipass { bytes32 indexed oldName, bytes32 indexed domainName ); -} \ No newline at end of file +} diff --git a/src/libraries/LibMultipass.sol b/src/libraries/LibMultipass.sol index 5606357..821a4e5 100644 --- a/src/libraries/LibMultipass.sol +++ b/src/libraries/LibMultipass.sol @@ -273,4 +273,4 @@ library LibMultipass { } using LibMultipass for NameQuery; -} \ No newline at end of file +}