From c9eb84d31256bf70870a854b8e966161fe1be760 Mon Sep 17 00:00:00 2001 From: ButterflyEffect Date: Thu, 26 Oct 2023 09:39:16 +0000 Subject: [PATCH] ens error if domain wrapped + tests --- contracts/RelayerRegistry.sol | 6 +- contracts/libraries/Permit.sol | 41 ++ hardhat.config.js | 22 +- test/RelayerRegistry.js | 227 +++++----- test/abi/ensWrapper.abi.json | 1 + test/abi/relayerRegistry.abi.json | 696 ++++++++++++++++++++++++++++++ test/utils.js | 169 ++++++++ 7 files changed, 1051 insertions(+), 111 deletions(-) create mode 100644 contracts/libraries/Permit.sol create mode 100644 test/abi/ensWrapper.abi.json create mode 100644 test/abi/relayerRegistry.abi.json create mode 100644 test/utils.js diff --git a/contracts/RelayerRegistry.sol b/contracts/RelayerRegistry.sol index 7c9db6b..1e1c180 100644 --- a/contracts/RelayerRegistry.sol +++ b/contracts/RelayerRegistry.sol @@ -130,7 +130,11 @@ contract RelayerRegistry is Initializable, EnsResolve { address[] calldata workersToRegister ) internal { bytes32 ensHash = bytes(ensName).namehash(); - require(relayer == ens.owner(ensHash), "only ens owner"); + address domainOwner = ens.owner(ensHash); + address ensNameWrapper = 0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401; + + require(domainOwner != ensNameWrapper, "only unwrapped ens domains"); + require(relayer == domainOwner, "only ens domain owner"); require(workers[relayer] == address(0), "cant register again"); RelayerState storage metadata = relayers[relayer]; diff --git a/contracts/libraries/Permit.sol b/contracts/libraries/Permit.sol new file mode 100644 index 0000000..4aee2f8 --- /dev/null +++ b/contracts/libraries/Permit.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +contract Permit { + function getPermitMessage( + string memory tokenName, + address tokenAddress, + string memory version, + address owner, + address spender, + uint256 chainId, + uint256 nonce, + uint256 amount, + uint256 deadline + ) public pure returns (bytes32) { + bytes32 permitTypehash = keccak256( + "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" + ); + uint16 permitFuncSelector = uint16(0x1901); + bytes32 domain = keccak256( + abi.encode( + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), + keccak256(bytes(tokenName)), + keccak256(bytes(version)), + chainId, + tokenAddress + ) + ); + + return + keccak256( + abi.encodePacked( + permitFuncSelector, + domain, + keccak256(abi.encode(permitTypehash, owner, spender, amount, nonce, deadline)) + ) + ); + } +} diff --git a/hardhat.config.js b/hardhat.config.js index 3dfa0fb..f6a6abc 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -1,27 +1,33 @@ require("@nomicfoundation/hardhat-toolbox"); -require("dotenv").config() +require("dotenv").config(); /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: "0.6.12", + mocha: { + timeout: 100000000, + }, networks: { mainnet: { - url: "https://eth.llamarpc.com" + url: "https://eth.llamarpc.com", + accounts: [process.env.REAL_PK], }, testnet: { url: "https://ethereum-goerli.publicnode.com", - accounts: [process.env.TEST_PK] + accounts: [process.env.TEST_PK], }, hardhat: { forking: { url: "https://eth.llamarpc.com", enabled: true, - blockNumber: 18391425, + blockNumber: 18431526, + accounts: [process.env.REAL_PK], }, - chainId: 1 - } + chainId: 1, + accounts: [{ privateKey: process.env.REAL_PK, balance: "10000000000000000000000000000000" }], + }, }, etherscan: { - apiKey: process.env.ETHERSCAN_KEY - } + apiKey: process.env.ETHERSCAN_KEY, + }, }; diff --git a/test/RelayerRegistry.js b/test/RelayerRegistry.js index 3207606..3d6c5f1 100644 --- a/test/RelayerRegistry.js +++ b/test/RelayerRegistry.js @@ -1,80 +1,33 @@ -const { time, loadFixture } = require("@nomicfoundation/hardhat-toolbox/network-helpers"); +const { loadFixture } = require("@nomicfoundation/hardhat-toolbox/network-helpers"); const { expect, assert } = require("chai"); -const { ethers, network } = require("hardhat"); -const { relayerRegistry } = require("torn-token"); +const { ethers, network, config } = require("hardhat"); +const { + sendMinimalStakeAmount, + resolveAddr, + getEnsRegistryContract, + getOldRelayerRegistryContractFixture, + getRegisterRelayerParams, + getManyEth, + deployAndExecuteFixture, + unregisterRelayer, + getRelayerRegistryContract, +} = require("./utils"); describe("Registry update", function () { - async function deployAndExecuteFixture() { - const RelayerRegistryFactory = await ethers.getContractFactory("RelayerRegistry"); - const constructorArgs = [ - "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C", - "0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce", - "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e", - "0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29", - "0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7", - ]; - const relayerRegistry = await RelayerRegistryFactory.deploy(...constructorArgs); - const deployedRegistryAddr = await relayerRegistry.getAddress(); - const proposalFactory = await ethers.getContractFactory("Proposal"); - const proposal = await proposalFactory.deploy(deployedRegistryAddr); - const deployedProposalAddr = await proposal.getAddress(); - - - const bigStakerAddr = "0xE4143f6377AEcd7193b9731d1C28815b57C4f5Ab"; - await network.provider.send("hardhat_setBalance", [ - bigStakerAddr, - "0x116663015358483537" - ]); - const stakerSigner = await ethers.getImpersonatedSigner(bigStakerAddr); - const governanceAddr = "0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce"; - const governanceContract = await ethers.getContractAt(require("./abi/governance.abi.json"), governanceAddr, stakerSigner); - await governanceContract.propose(deployedProposalAddr, ""); - const proposalId = await governanceContract.proposalCount(); - await time.increase(60 * 60); - await governanceContract.castVote(proposalId, true); - await time.increase(60 * 60 * 24 * 7 + 60); - await governanceContract.execute(proposalId); - - const governanceSigner = await ethers.getImpersonatedSigner(governanceAddr); - const relayerRegistryProxyAddr = "0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2"; - const relayerRegistryContract = await ethers.getContractAt("RelayerRegistry", relayerRegistryProxyAddr, governanceSigner); - - return { relayerRegistryProxyAddr, deployedRegistryAddr, relayerRegistryContract, governanceSigner }; - } - - async function unregisterRelayer(ensNameOrAddress){ - const { relayerRegistryContract } = await loadFixture(deployAndExecuteFixture); - const relayerAddr = ethers.isAddress(ensNameOrAddress) ? ensNameOrAddress : resolveAddr(ensNameOrAddress); - await relayerRegistryContract.unregisterRelayer(relayerAddr); - } - - async function resolveAddr(ensName){ - const ensNode = ethers.namehash(ensName); - const ensAddr = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"; - const registryContract = await ethers.getContractAt(require("./abi/ensRegistry.abi.json"), ensAddr); - const resolverAddr = await registryContract.resolver(ensNode); - const resolverContract = await ethers.getContractAt(require("./abi/ensResolver.abi.json"), resolverAddr); - - return await resolverContract.addr(ensNode); - } - - async function getOldRelayerRegistryContractFixture(){ - const relayerRegistryProxyAddr = "0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2"; - return await ethers.getContractAt(require("./abi/relayerRegistryOld.abi.json"), relayerRegistryProxyAddr); - } - it("Implementation address should be updated", async function () { await loadFixture(getOldRelayerRegistryContractFixture); const { relayerRegistryProxyAddr, deployedRegistryAddr } = await loadFixture(deployAndExecuteFixture); - const implementation = await ethers.provider.getStorage(relayerRegistryProxyAddr, "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"); + const implementation = await ethers.provider.getStorage( + relayerRegistryProxyAddr, + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", + ); const [implementationAddr] = new ethers.AbiCoder().decode(["address"], implementation); - - expect(implementationAddr).to.equal(deployedRegistryAddr) + + expect(implementationAddr).to.equal(deployedRegistryAddr); }); - describe("Unregister relayer", function(){ - - it("Uregister relayer function should work", async function(){ + describe("Unregister relayer", function () { + it("Uregister relayer function should work", async function () { const testRelayerAddr = await resolveAddr("first-relayer.eth"); const relayerRegistryOldContract = await loadFixture(getOldRelayerRegistryContractFixture); @@ -91,32 +44,50 @@ describe("Registry update", function () { expect(isRelayer).to.equal(false); expect(isRelayerRegistered).to.equal(false); - }) + }); - it("Unregistered relayer should have zero balance", async function(){ + it("Unregistered relayer should have zero balance", async function () { const testRelayerAddr = await resolveAddr("first-relayer.eth"); const { relayerRegistryContract } = await loadFixture(deployAndExecuteFixture); await unregisterRelayer(testRelayerAddr); const relayerBalance = await relayerRegistryContract.getRelayerBalance(testRelayerAddr); expect(relayerBalance).to.equal(0); - }) + }); - it("Tornado router address should be valid", async function(){ + it("Unregister function should revert if called not by Governance", async function () { + const { relayerRegistryContract } = await loadFixture(deployAndExecuteFixture); + + const me = await resolveAddr("🦋️-effect.eth"); + await getManyEth(me); + const meAsSigner = await ethers.getImpersonatedSigner(me); + const testRelayerAddr = await resolveAddr("first-relayer.eth"); + const relayerRegistryWithMeAsSigner = relayerRegistryContract.connect(meAsSigner); + + await expect(relayerRegistryWithMeAsSigner.unregisterRelayer(testRelayerAddr)).to.be.revertedWith( + "only governance", + ); + }); + + it("Tornado router address should be valid", async function () { const relayerRegistryOldContract = await loadFixture(getOldRelayerRegistryContractFixture); const oldRegistryRouterAddress = await relayerRegistryOldContract.tornadoRouter(); const { relayerRegistryContract } = await loadFixture(deployAndExecuteFixture); expect(await relayerRegistryContract.tornadoRouter()).to.equal(oldRegistryRouterAddress); - }) + }); - it("Aggregator contract data for unregistered relayers should be valid", async function(){ + it("Aggregator contract data for unregistered relayers should be valid", async function () { const aggregatorAddr = "0xE8F47A78A6D52D317D0D2FFFac56739fE14D1b49"; const aggregatorContract = await ethers.getContractAt(require("./abi/aggregator.abi.json"), aggregatorAddr); const notRelayer = "🦋️-effect.eth"; const testRelayer = "first-relayer.eth"; - const callAggr = async () => await aggregatorContract.relayersData([notRelayer, testRelayer].map(ethers.namehash), ["mainnet-tornado", "bsc-tornado"]); + const callAggr = async () => + await aggregatorContract.relayersData([notRelayer, testRelayer].map(ethers.namehash), [ + "mainnet-tornado", + "bsc-tornado", + ]); const oldData = await callAggr(); await loadFixture(deployAndExecuteFixture); @@ -129,47 +100,99 @@ describe("Registry update", function () { expect(newData[1][1]).to.equal(0); expect(oldData[1][2]).to.equal(true); expect(newData[1][2]).to.equal(false); - }) + }); - it("Cheating relayers should be unregistered", async function(){ - const cheatingRelayers = await Promise.all([ - "available-reliable-relayer.eth", - "0xtornadocash.eth", - "0xtorn365.eth", - "tornrelayers.eth", - "moon-relayer.eth" - ].map(resolveAddr)); + it("Cheating relayers should be unregistered", async function () { + const cheatingRelayers = await Promise.all( + [ + "available-reliable-relayer.eth", + "0xtornadocash.eth", + "0xtorn365.eth", + "tornrelayers.eth", + "moon-relayer.eth", + ].map(resolveAddr), + ); const relayerRegistryOldContract = await loadFixture(getOldRelayerRegistryContractFixture); - let areRegistered = await Promise.all(cheatingRelayers.map(r => relayerRegistryOldContract.isRelayer(r))); - let balances = await Promise.all(cheatingRelayers.map(r => relayerRegistryOldContract.getRelayerBalance(r))); + let areRegistered = await Promise.all(cheatingRelayers.map((r) => relayerRegistryOldContract.isRelayer(r))); + let balances = await Promise.all(cheatingRelayers.map((r) => relayerRegistryOldContract.getRelayerBalance(r))); - expect(areRegistered).satisfy(v => v.every(v => v === true)); - expect(balances).satisfy(v => v.every(v => v >= 0n)); + expect(areRegistered).satisfy((v) => v.every((v) => v === true)); + expect(balances).satisfy((v) => v.every((v) => v >= 0n)); const { relayerRegistryContract } = await loadFixture(deployAndExecuteFixture); - areRegistered = await Promise.all(cheatingRelayers.map(r => relayerRegistryContract.isRelayer(r))); - balances = await Promise.all(cheatingRelayers.map(r => relayerRegistryContract.getRelayerBalance(r))); - - expect(areRegistered).satisfy(v => v.every(v => v === false)); - expect(balances).satisfy(v => v.every(v => v === 0n)); - }) - }) + areRegistered = await Promise.all(cheatingRelayers.map((r) => relayerRegistryContract.isRelayer(r))); + balances = await Promise.all(cheatingRelayers.map((r) => relayerRegistryContract.getRelayerBalance(r))); - it("Unregistered relayers can register again", async function(){ - const { relayerRegistryContract, governanceSigner } = await loadFixture(deployAndExecuteFixture); + expect(areRegistered).satisfy((v) => v.every((v) => v === false)); + expect(balances).satisfy((v) => v.every((v) => v === 0n)); + }); + }); + + it("Unregistered relayers can register again", async function () { + const { relayerRegistryContract } = await loadFixture(deployAndExecuteFixture); const relayerEns = "moon-relayer.eth"; const unregisteredRelayer = await resolveAddr(relayerEns); - const tornContract = await ethers.getContractAt(require("./abi/torn.abi.json"), "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C", governanceSigner); - const toStake = 2000n * 10n ** 18n; - await tornContract.transfer(unregisteredRelayer, toStake); + const toStake = await sendMinimalStakeAmount(unregisteredRelayer); const relayerSigner = await ethers.getImpersonatedSigner(unregisteredRelayer); await relayerRegistryContract.connect(relayerSigner).register(relayerEns, toStake, []); - const isRelayerRegistered = await relayerRegistryContract.isRelayerRegistered(unregisteredRelayer, unregisteredRelayer); + const isRelayerRegistered = await relayerRegistryContract.isRelayerRegistered( + unregisteredRelayer, + unregisteredRelayer, + ); const relayerBalance = await relayerRegistryContract.getRelayerBalance(unregisteredRelayer); expect(isRelayerRegistered).to.be.equal(true); expect(relayerBalance).to.be.equal(toStake); - }) + }); + + describe("Fix ENS owner checks", async function () { + it("ENS owner of wrapped domain should be a wrapper", async function () { + const wrappedDomain = "butterfly-attractor.eth"; + const realOwner = await resolveAddr(wrappedDomain); + const ensRegistry = await getEnsRegistryContract(); + const wrapperOwner = await ensRegistry.owner(ethers.namehash(wrappedDomain)); + + expect(wrapperOwner).to.be.not.equal(realOwner); + }); + + it("Registering relayer with wrapped ENS domain should revert", async function () { + const { relayerRegistryContract } = await loadFixture(deployAndExecuteFixture); + const relayerWrappedEns = "butterfly-attractor.eth"; + const registerParams = await getRegisterRelayerParams(relayerWrappedEns); + const [relayer] = await ethers.getSigners(); + const relayerRegistry = relayerRegistryContract.connect(relayer); + + await expect(relayerRegistry.registerPermit(...registerParams)).to.be.revertedWith("only unwrapped ens domains"); + }); + + it("After ENS domain unwrapping owner can register relayer", async function () { + const { relayerRegistryProxyAddr } = await loadFixture(deployAndExecuteFixture); + const ensWrapperAddr = "0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401"; + const wrappedEnsDomain = "butterfly-attractor.eth"; + const addr = await resolveAddr(wrappedEnsDomain); + const ensContract = await getEnsRegistryContract(); + const wrapperContract = await ethers.getContractAt(require("./abi/ensWrapper.abi.json"), ensWrapperAddr); + const labelhash = ethers.keccak256(ethers.toUtf8Bytes(wrappedEnsDomain.split(".")[0])); + console.log(await ensContract.owner(ethers.namehash(wrappedEnsDomain))); + console.log(await ethers.provider.getBlockNumber()); + await wrapperContract.unwrapETH2LD(labelhash, addr, addr); + console.log(await ensContract.owner(ethers.namehash(wrappedEnsDomain))); + console.log(await ethers.provider.getBlockNumber()); + console.log(await network.provider.send("evm_mine")); + console.log(await ethers.provider.getBlockNumber()); + + const [relayerSigner] = await ethers.getSigners(); + const relayerRegistry = await ethers.getContractAt( + require("./abi/relayerRegistry.abi.json"), + relayerRegistryProxyAddr, + relayerSigner, + ); + const registerParams = await getRegisterRelayerParams(wrappedEnsDomain); + await relayerRegistry.registerPermit(...registerParams); + + expect(await relayerRegistry.isRelayerRegistered(addr, addr)).to.be.equal(true); + }); + }); }); diff --git a/test/abi/ensWrapper.abi.json b/test/abi/ensWrapper.abi.json new file mode 100644 index 0000000..47c8945 --- /dev/null +++ b/test/abi/ensWrapper.abi.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"contract ENS","name":"_ens","type":"address"},{"internalType":"contract IBaseRegistrar","name":"_registrar","type":"address"},{"internalType":"contract IMetadataService","name":"_metadataService","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CannotUpgrade","type":"error"},{"inputs":[],"name":"IncompatibleParent","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"IncorrectTargetOwner","type":"error"},{"inputs":[],"name":"IncorrectTokenType","type":"error"},{"inputs":[{"internalType":"bytes32","name":"labelHash","type":"bytes32"},{"internalType":"bytes32","name":"expectedLabelhash","type":"bytes32"}],"name":"LabelMismatch","type":"error"},{"inputs":[{"internalType":"string","name":"label","type":"string"}],"name":"LabelTooLong","type":"error"},{"inputs":[],"name":"LabelTooShort","type":"error"},{"inputs":[],"name":"NameIsNotWrapped","type":"error"},{"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"OperationProhibited","type":"error"},{"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"addr","type":"address"}],"name":"Unauthorised","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":false,"internalType":"bool","name":"active","type":"bool"}],"name":"ControllerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"uint64","name":"expiry","type":"uint64"}],"name":"ExpiryExtended","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"uint32","name":"fuses","type":"uint32"}],"name":"FusesSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"NameUnwrapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"node","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"name","type":"bytes"},{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint32","name":"fuses","type":"uint32"},{"indexed":false,"internalType":"uint64","name":"expiry","type":"uint64"}],"name":"NameWrapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"values","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"TransferSingle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"_tokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint32","name":"fuseMask","type":"uint32"}],"name":"allFusesBurned","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"addr","type":"address"}],"name":"canExtendSubnames","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"addr","type":"address"}],"name":"canModifyName","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"controllers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ens","outputs":[{"internalType":"contract ENS","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"parentNode","type":"bytes32"},{"internalType":"bytes32","name":"labelhash","type":"bytes32"},{"internalType":"uint64","name":"expiry","type":"uint64"}],"name":"extendExpiry","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"operator","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getData","outputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint32","name":"fuses","type":"uint32"},{"internalType":"uint64","name":"expiry","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"parentNode","type":"bytes32"},{"internalType":"bytes32","name":"labelhash","type":"bytes32"}],"name":"isWrapped","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"name":"isWrapped","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadataService","outputs":[{"internalType":"contract IMetadataService","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"names","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"recoverFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"label","type":"string"},{"internalType":"address","name":"wrappedOwner","type":"address"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"uint16","name":"ownerControlledFuses","type":"uint16"}],"name":"registerAndWrapETH2LD","outputs":[{"internalType":"uint256","name":"registrarExpiry","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"registrar","outputs":[{"internalType":"contract IBaseRegistrar","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"renew","outputs":[{"internalType":"uint256","name":"expires","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"parentNode","type":"bytes32"},{"internalType":"bytes32","name":"labelhash","type":"bytes32"},{"internalType":"uint32","name":"fuses","type":"uint32"},{"internalType":"uint64","name":"expiry","type":"uint64"}],"name":"setChildFuses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"bool","name":"active","type":"bool"}],"name":"setController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint16","name":"ownerControlledFuses","type":"uint16"}],"name":"setFuses","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMetadataService","name":"_metadataService","type":"address"}],"name":"setMetadataService","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"setRecord","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"address","name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"parentNode","type":"bytes32"},{"internalType":"string","name":"label","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint32","name":"fuses","type":"uint32"},{"internalType":"uint64","name":"expiry","type":"uint64"}],"name":"setSubnodeOwner","outputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"parentNode","type":"bytes32"},{"internalType":"string","name":"label","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"resolver","type":"address"},{"internalType":"uint64","name":"ttl","type":"uint64"},{"internalType":"uint32","name":"fuses","type":"uint32"},{"internalType":"uint64","name":"expiry","type":"uint64"}],"name":"setSubnodeRecord","outputs":[{"internalType":"bytes32","name":"node","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"node","type":"bytes32"},{"internalType":"uint64","name":"ttl","type":"uint64"}],"name":"setTTL","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract INameWrapperUpgrade","name":"_upgradeAddress","type":"address"}],"name":"setUpgradeContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"parentNode","type":"bytes32"},{"internalType":"bytes32","name":"labelhash","type":"bytes32"},{"internalType":"address","name":"controller","type":"address"}],"name":"unwrap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"labelhash","type":"bytes32"},{"internalType":"address","name":"registrant","type":"address"},{"internalType":"address","name":"controller","type":"address"}],"name":"unwrapETH2LD","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"name","type":"bytes"},{"internalType":"bytes","name":"extraData","type":"bytes"}],"name":"upgrade","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upgradeContract","outputs":[{"internalType":"contract INameWrapperUpgrade","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"name","type":"bytes"},{"internalType":"address","name":"wrappedOwner","type":"address"},{"internalType":"address","name":"resolver","type":"address"}],"name":"wrap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"label","type":"string"},{"internalType":"address","name":"wrappedOwner","type":"address"},{"internalType":"uint16","name":"ownerControlledFuses","type":"uint16"},{"internalType":"address","name":"resolver","type":"address"}],"name":"wrapETH2LD","outputs":[{"internalType":"uint64","name":"expiry","type":"uint64"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/test/abi/relayerRegistry.abi.json b/test/abi/relayerRegistry.abi.json new file mode 100644 index 0000000..58ae1ca --- /dev/null +++ b/test/abi/relayerRegistry.abi.json @@ -0,0 +1,696 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_torn", + "type": "address" + }, + { + "internalType": "address", + "name": "_governance", + "type": "address" + }, + { + "internalType": "address", + "name": "_ens", + "type": "address" + }, + { + "internalType": "address", + "name": "_staking", + "type": "address" + }, + { + "internalType": "address", + "name": "_feeManager", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "minStakeAmount", + "type": "uint256" + } + ], + "name": "MinimumStakeAmount", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "relayer", + "type": "address" + } + ], + "name": "RelayerBalanceNullified", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "relayer", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "ensName", + "type": "string" + }, + { + "indexed": false, + "internalType": "address", + "name": "relayerAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "stakedAmount", + "type": "uint256" + } + ], + "name": "RelayerRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "relayer", + "type": "address" + } + ], + "name": "RelayerUnregistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "tornadoRouter", + "type": "address" + } + ], + "name": "RouterRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "relayer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountStakeAdded", + "type": "uint256" + } + ], + "name": "StakeAddedToRelayer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "relayer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountBurned", + "type": "uint256" + } + ], + "name": "StakeBurned", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "relayer", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "worker", + "type": "address" + } + ], + "name": "WorkerRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "relayer", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "worker", + "type": "address" + } + ], + "name": "WorkerUnregistered", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "domains", + "type": "bytes32[]" + } + ], + "name": "bulkResolve", + "outputs": [ + { + "internalType": "address[]", + "name": "result", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "relayer", + "type": "address" + }, + { + "internalType": "contract ITornadoInstance", + "name": "pool", + "type": "address" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "ens", + "outputs": [ + { + "internalType": "contract IENS", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "feeManager", + "outputs": [ + { + "internalType": "contract IFeeManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "relayer", + "type": "address" + } + ], + "name": "getRelayerBalance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "relayer", + "type": "address" + } + ], + "name": "getRelayerEnsHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governance", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_tornadoRouter", + "type": "bytes32" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "toResolve", + "type": "address" + } + ], + "name": "isRelayer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "relayer", + "type": "address" + }, + { + "internalType": "address", + "name": "toResolve", + "type": "address" + } + ], + "name": "isRelayerRegistered", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minStakeAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "relayer", + "type": "address" + } + ], + "name": "nullifyBalance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "ensName", + "type": "string" + }, + { + "internalType": "uint256", + "name": "stake", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "workersToRegister", + "type": "address[]" + } + ], + "name": "register", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "ensName", + "type": "string" + }, + { + "internalType": "uint256", + "name": "stake", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "workersToRegister", + "type": "address[]" + }, + { + "internalType": "address", + "name": "relayer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "registerPermit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "relayer", + "type": "address" + }, + { + "internalType": "address", + "name": "worker", + "type": "address" + } + ], + "name": "registerWorker", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "relayers", + "outputs": [ + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "ensHash", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "node", + "type": "bytes32" + } + ], + "name": "resolve", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "minAmount", + "type": "uint256" + } + ], + "name": "setMinStakeAmount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tornadoRouterAddress", + "type": "address" + } + ], + "name": "setTornadoRouter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "relayer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "stake", + "type": "uint256" + } + ], + "name": "stakeToRelayer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "relayer", + "type": "address" + }, + { + "internalType": "uint256", + "name": "stake", + "type": "uint256" + }, + { + "internalType": "address", + "name": "staker", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "stakeToRelayerPermit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "staking", + "outputs": [ + { + "internalType": "contract ITornadoStakingRewards", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "torn", + "outputs": [ + { + "internalType": "contract TORN", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tornadoRouter", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "relayer", + "type": "address" + } + ], + "name": "unregisterRelayer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "worker", + "type": "address" + } + ], + "name": "unregisterWorker", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "workers", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ] \ No newline at end of file diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 0000000..62f369f --- /dev/null +++ b/test/utils.js @@ -0,0 +1,169 @@ +const { ethers, network } = require("hardhat"); +const { time, loadFixture } = require("@nomicfoundation/hardhat-toolbox/network-helpers"); +const { keccak256, toUtf8Bytes } = require("ethers"); + +const ensAddr = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"; +const relayerRegistryProxyAddr = "0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2"; +const tornAddr = "0x77777FeDdddFfC19Ff86DB637967013e6C6A116C"; +const governanceAddr = "0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce"; + +async function getPermitSignature(signer, tokenContract, spender, value, deadline) { + const [nonce, name, version, chainId] = await Promise.all([ + tokenContract.nonces(signer.address), + tokenContract.name(), + "1", + tokenContract.chainID(), + ]); + + const domain = { + name, + version, + chainId, + verifyingContract: tornAddr, + }; + const types = { + Permit: [ + { + name: "owner", + type: "address", + }, + { + name: "spender", + type: "address", + }, + { + name: "value", + type: "uint256", + }, + { + name: "nonce", + type: "uint256", + }, + { + name: "deadline", + type: "uint256", + }, + ], + }; + const values = { + owner: signer.address, + spender, + value, + nonce, + deadline, + }; + + const signature = await signer.signTypedData(domain, types, values); + return ethers.Signature.from(signature); +} + +async function getRegisterRelayerParams(ensDomain, additionalStake = 0, workerAddrs = []) { + const relayerAddr = await resolveAddr(ensDomain); + const toStake = await sendMinimalStakeAmount(relayerAddr, BigInt(additionalStake)); + const tornContract = await ethers.getContractAt(require("./abi/torn.abi.json"), tornAddr); + const relayerSigner = await ethers.getSigner(relayerAddr); + const { v, r, s } = await getPermitSignature( + relayerSigner, + tornContract, + relayerRegistryProxyAddr, + toStake, + ethers.MaxUint256, + ); + + return [ensDomain, toStake, workerAddrs, relayerAddr, ethers.MaxUint256, v, r, s]; +} + +async function getEnsRegistryContract() { + return await ethers.getContractAt(require("./abi/ensRegistry.abi.json"), ensAddr); +} + +async function resolveAddr(ensName) { + const ensNode = ethers.namehash(ensName); + const registryContract = await getEnsRegistryContract(); + const resolverAddr = await registryContract.resolver(ensNode); + const resolverContract = await ethers.getContractAt(require("./abi/ensResolver.abi.json"), resolverAddr); + + return await resolverContract.addr(ensNode); +} + +async function getRelayerRegistryContract(signer) { + return await ethers.getContractAt("RelayerRegistry", relayerRegistryProxyAddr, signer); +} + +async function getOldRelayerRegistryContractFixture() { + return await ethers.getContractAt(require("./abi/relayerRegistryOld.abi.json"), relayerRegistryProxyAddr); +} + +async function getManyEth(addr) { + await network.provider.send("hardhat_setBalance", [addr, "0x111166630153555558483537"]); +} + +async function sendMinimalStakeAmount(addr, additionalStake = 0n) { + const { relayerRegistryContract } = await loadFixture(deployAndExecuteFixture); + const minRelayerStakeAmount = await relayerRegistryContract.minStakeAmount(); + const governanceSigner = await ethers.getImpersonatedSigner(governanceAddr); + const tornContract = await ethers.getContractAt(require("./abi/torn.abi.json"), tornAddr, governanceSigner); + await tornContract.transfer(addr, minRelayerStakeAmount + additionalStake); + + return minRelayerStakeAmount + additionalStake; +} + +async function deployAndExecuteFixture() { + const RelayerRegistryFactory = await ethers.getContractFactory("RelayerRegistry"); + const constructorArgs = [ + tornAddr, + governanceAddr, + ensAddr, + "0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29", + "0x5f6c97C6AD7bdd0AE7E0Dd4ca33A4ED3fDabD4D7", + ]; + const relayerRegistry = await RelayerRegistryFactory.deploy(...constructorArgs); + const deployedRegistryAddr = await relayerRegistry.getAddress(); + const proposalFactory = await ethers.getContractFactory("Proposal"); + const proposal = await proposalFactory.deploy(deployedRegistryAddr); + const deployedProposalAddr = await proposal.getAddress(); + + const bigStakerAddr = "0xE4143f6377AEcd7193b9731d1C28815b57C4f5Ab"; + await getManyEth(bigStakerAddr); + const stakerSigner = await ethers.getImpersonatedSigner(bigStakerAddr); + const governanceContract = await ethers.getContractAt( + require("./abi/governance.abi.json"), + governanceAddr, + stakerSigner, + ); + await governanceContract.propose(deployedProposalAddr, ""); + const proposalId = await governanceContract.proposalCount(); + await time.increase(60 * 60); + await governanceContract.castVote(proposalId, true); + await time.increase(60 * 60 * 24 * 7 + 60); + await governanceContract.execute(proposalId); + + const governanceSigner = await ethers.getImpersonatedSigner(governanceAddr); + const relayerRegistryContract = await ethers.getContractAt( + "RelayerRegistry", + relayerRegistryProxyAddr, + governanceSigner, + ); + + return { relayerRegistryProxyAddr, deployedRegistryAddr, relayerRegistryContract, governanceSigner, constructorArgs }; +} + +async function unregisterRelayer(ensNameOrAddress) { + const governanceSigner = await ethers.getImpersonatedSigner(governanceAddr); + const relayerRegistryContract = await getRelayerRegistryContract(governanceSigner); + const relayerAddr = ethers.isAddress(ensNameOrAddress) ? ensNameOrAddress : resolveAddr(ensNameOrAddress); + await relayerRegistryContract.unregisterRelayer(relayerAddr); +} + +module.exports = { + sendMinimalStakeAmount, + getEnsRegistryContract, + getOldRelayerRegistryContractFixture, + getRelayerRegistryContract, + getPermitSignature, + resolveAddr, + getManyEth, + unregisterRelayer, + deployAndExecuteFixture, + getRegisterRelayerParams, +};