From d780294b8dccf047391a71b75dade50a6b89003b Mon Sep 17 00:00:00 2001 From: Aleksandr Kuperman Date: Mon, 18 Sep 2023 18:34:06 +0100 Subject: [PATCH 1/7] chore: updating scripts and deploying token-based service contracts on chiado --- .../l2/deploy_14_15_change_ownerships.js | 4 +- .../deployment/l2/globals_gnosis_chiado.json | 2 +- test/ServiceManagerToken.js | 955 ++++++++++-------- 3 files changed, 533 insertions(+), 428 deletions(-) diff --git a/scripts/deployment/l2/deploy_14_15_change_ownerships.js b/scripts/deployment/l2/deploy_14_15_change_ownerships.js index 8dad18d7..3711ddbc 100644 --- a/scripts/deployment/l2/deploy_14_15_change_ownerships.js +++ b/scripts/deployment/l2/deploy_14_15_change_ownerships.js @@ -14,7 +14,7 @@ async function main() { const gasPriceInGwei = parsedData.gasPriceInGwei; const serviceRegistryTokenUtilityAddress = parsedData.serviceRegistryTokenUtilityAddress; const serviceManagerTokenAddress = parsedData.serviceManagerTokenAddress; - const bridgeMediatorAddress = parsedData.bridgeMediatorAddress; + let bridgeMediatorAddress = parsedData.bridgeMediatorAddress; let EOA; let networkURL; @@ -37,6 +37,8 @@ async function main() { networkURL = "https://rpc.gnosischain.com"; } else if (providerName === "chiado") { networkURL = "https://rpc.chiadochain.net"; + // For the chiado network, the mock timelock contract is set as the owner + bridgeMediatorAddress = parsedData.bridgeMediatorMockTimelockAddress; } else { console.log("Unknown network provider", providerName); return; diff --git a/scripts/deployment/l2/globals_gnosis_chiado.json b/scripts/deployment/l2/globals_gnosis_chiado.json index 2f418202..1a9cb419 100644 --- a/scripts/deployment/l2/globals_gnosis_chiado.json +++ b/scripts/deployment/l2/globals_gnosis_chiado.json @@ -1 +1 @@ -{"contractVerification":true,"useLedger":false,"derivationPath":"m/44'/60'/2'/0/0","providerName":"chiado","gasPriceInGwei":"3","gnosisSafeAddress":"0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552","gnosisSafeProxyFactoryAddress":"0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2","baseURI":"https://gateway.autonolas.tech/ipfs/","serviceRegistryName":"Service Registry","serviceRegistrySymbol":"AUTONOLAS-SERVICE-V1","AMBContractProxyForeignAddress":"0x87A19d769D875964E9Cd41dDBfc397B2543764E6","bridgeMediatorAddress":"0x670Ac235EE13C0B2a5065282bBB0c61cfB354592","bridgeMediatorMockTimelockAddress":"0x0a50009D55Ed5700ac8FF713709d5Ad5fa843896","serviceRegistryAddress":"0x31D3202d8744B16A120117A053459DDFAE93c855","serviceManagerAddress":"0x29086141ecdc310058fc23273F8ef7881d20C2f7","gnosisSafeMultisigImplementationAddress":"0xeB49bE5DF00F74bd240DE4535DDe6Bc89CEfb994","gnosisSafeSameAddressMultisigImplementationAddress":"0x5BA58970c2Ae16Cf6218783018100aF2dCcFc915"} \ No newline at end of file +{"contractVerification":true,"useLedger":false,"derivationPath":"m/44'/60'/2'/0/0","providerName":"chiado","gasPriceInGwei":"3","gnosisSafeAddress":"0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552","gnosisSafeProxyFactoryAddress":"0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2","baseURI":"https://gateway.autonolas.tech/ipfs/","serviceRegistryName":"Service Registry","serviceRegistrySymbol":"AUTONOLAS-SERVICE-V1","AMBContractProxyForeignAddress":"0x87A19d769D875964E9Cd41dDBfc397B2543764E6","bridgeMediatorAddress":"0x670Ac235EE13C0B2a5065282bBB0c61cfB354592","bridgeMediatorMockTimelockAddress":"0x0a50009D55Ed5700ac8FF713709d5Ad5fa843896","serviceRegistryAddress":"0x31D3202d8744B16A120117A053459DDFAE93c855","serviceManagerAddress":"0x29086141ecdc310058fc23273F8ef7881d20C2f7","gnosisSafeMultisigImplementationAddress":"0xeB49bE5DF00F74bd240DE4535DDe6Bc89CEfb994","gnosisSafeSameAddressMultisigImplementationAddress":"0x5BA58970c2Ae16Cf6218783018100aF2dCcFc915","operatorWhitelistAddress":"0x6f7661F52fE1919996d0A4F68D09B344093a349d","serviceRegistryTokenUtilityAddress":"0xc2c7E40674f1C7Bb99eFe5680Efd79842502bED4","serviceManagerTokenAddress":"0xc965a32185590Eb5a5fffDba29E96126b7650eDe"} \ No newline at end of file diff --git a/test/ServiceManagerToken.js b/test/ServiceManagerToken.js index 40af2b4f..6fb00ff0 100644 --- a/test/ServiceManagerToken.js +++ b/test/ServiceManagerToken.js @@ -9,11 +9,15 @@ describe("ServiceManagerToken", function () { let gnosisSafe; let gnosisSafeProxyFactory; let serviceRegistry; + let serviceRegistryL2; let serviceRegistryTokenUtility; + let serviceRegistryTokenUtilityL2; let serviceManager; + let serviceManagerL2; let gnosisSafeMultisig; let token; let operatorWhitelist; + let operatorWhitelistL2; let reentrancyAttacker; let signers; let deployer; @@ -34,6 +38,7 @@ describe("ServiceManagerToken", function () { const AddressZero = "0x" + "0".repeat(40); const ETHAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; const initSupply = "5" + "0".repeat(26); + const serviceRegistryImplementations = ["l1", "l2"]; beforeEach(async function () { const ComponentRegistry = await ethers.getContractFactory("ComponentRegistry"); @@ -59,10 +64,17 @@ describe("ServiceManagerToken", function () { agentRegistry.address); await serviceRegistry.deployed(); + const ServiceRegistryL2 = await ethers.getContractFactory("ServiceRegistryL2"); + serviceRegistryL2 = await ServiceRegistryL2.deploy("Service Registry L2", "SERVICE", "https://localhost/service/"); + await serviceRegistryL2.deployed(); + const ServiceRegistryTokenUtility = await ethers.getContractFactory("ServiceRegistryTokenUtility"); serviceRegistryTokenUtility = await ServiceRegistryTokenUtility.deploy(serviceRegistry.address); await serviceRegistryTokenUtility.deployed(); + serviceRegistryTokenUtilityL2 = await ServiceRegistryTokenUtility.deploy(serviceRegistryL2.address); + await serviceRegistryTokenUtilityL2.deployed(); + const GnosisSafeMultisig = await ethers.getContractFactory("GnosisSafeMultisig"); gnosisSafeMultisig = await GnosisSafeMultisig.deploy(gnosisSafe.address, gnosisSafeProxyFactory.address); await gnosisSafeMultisig.deployed(); @@ -71,11 +83,18 @@ describe("ServiceManagerToken", function () { operatorWhitelist = await OperatorWhitelist.deploy(serviceRegistry.address); await operatorWhitelist.deployed(); + operatorWhitelistL2 = await OperatorWhitelist.deploy(serviceRegistryL2.address); + await operatorWhitelistL2.deployed(); + const ServiceManager = await ethers.getContractFactory("ServiceManagerToken"); serviceManager = await ServiceManager.deploy(serviceRegistry.address, serviceRegistryTokenUtility.address, operatorWhitelist.address); await serviceManager.deployed(); + serviceManagerL2 = await ServiceManager.deploy(serviceRegistryL2.address, serviceRegistryTokenUtilityL2.address, + operatorWhitelistL2.address); + await serviceManagerL2.deployed(); + const Token = await ethers.getContractFactory("ERC20Token"); token = await Token.deploy(); await token.deployed(); @@ -269,454 +288,538 @@ describe("ServiceManagerToken", function () { }); context("Service manipulations via manager", async function () { - it("Creating a service with a custom token, update, register, deploy, terminate, unbond", async function () { - const manager = signers[4]; - const owner = signers[5]; - const operator = signers[6]; - const agentInstances = [signers[7].address, signers[8].address]; - await agentRegistry.changeManager(manager.address); - - // Creating 4 canonical agents - await agentRegistry.connect(manager).create(owner.address, unitHash, [1]); - await agentRegistry.connect(manager).create(owner.address, unitHash1, [1]); - await agentRegistry.connect(manager).create(owner.address, unitHash2, [1]); - await serviceRegistry.changeManager(serviceManager.address); - await serviceRegistryTokenUtility.changeManager(serviceManager.address); - - // Try to create a service with a token having specified at least one zero bond - let errAgentIds = [1, 4]; - let errAgentParams = [[1, 0], [1, regBond]]; - await expect( - serviceManager.create(deployer.address, token.address, configHash, errAgentIds, errAgentParams, maxThreshold) - ).to.be.revertedWithCustomError(serviceManager, "ZeroValue"); - - // Create one service with the ERC20 token bond - const initAgentIds = [1, 3]; - await serviceManager.create(deployer.address, token.address, configHash, initAgentIds, agentParams, maxThreshold); - - // Try to update a service with at least one slot being non zero and its corresponding bond being a zero - errAgentIds = [1, 2, 3]; - errAgentParams = [[1, regBond], [1, 0], [1, regBond]]; - const errThreshold = errAgentParams[0][0] + errAgentParams[1][0] + errAgentParams[2][0]; - await expect( - serviceManager.update(token.address, configHash, errAgentIds, errAgentParams, errThreshold, serviceIds[0]) - ).to.be.revertedWithCustomError(serviceManager, "ZeroValue"); - - // Construct correct values for the service update - const newAgentIds = [1, 2, 3]; - const newAgentParams = [[1, regBond], [1, regBond], [0, 0]]; - const newMaxThreshold = newAgentParams[0][0] + newAgentParams[1][0]; - - // Update a service with the ERC20 token bond - await serviceManager.update(token.address, configHash, newAgentIds, newAgentParams, newMaxThreshold, serviceIds[0]); - // Approve token for the serviceRegistryTokenUtility contract - await token.connect(deployer).approve(serviceRegistryTokenUtility.address, regBond); - - // Activate the registration - await serviceManager.connect(deployer).activateRegistration(serviceIds[0], {value: 1}); - let service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(2); - - // Set the operator whitelist checker contract - // Whitelist a random operator address for the service - await operatorWhitelist.setOperatorsStatuses(serviceIds[0], [signers[15].address], [true], true); - // Try to register agents with the non-whitelited operator address - await expect( - serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[0], agentInstances[1]], - agentIds, {value: 2}) - ).to.be.revertedWithCustomError(serviceManager, "WrongOperator"); - - // Whitelist a correct operator address - await operatorWhitelist.setOperatorsStatuses(serviceIds[0], [operator.address], [true], true); - // Try to register agents without the approve from the operator - await expect( - serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[0], agentInstances[1]], - agentIds, {value: 2}) - ).to.be.revertedWithCustomError(serviceManager, "IncorrectAgentBondingValue"); - - // Approve token for the serviceRegistryTokenUtility contract by the operator - await token.mint(operator.address, 2 * regBond); - await token.connect(operator).approve(serviceRegistryTokenUtility.address, 2 * regBond); - // Registering agents for the service Id - await serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[0], agentInstances[1]], - agentIds, {value: 2*regBond}); - service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(3); - - // Whitelist gnosis multisig implementation - await serviceRegistry.changeMultisigPermission(gnosisSafeMultisig.address, true); - // Deploying the service - await serviceManager.connect(deployer).deploy(serviceIds[0], gnosisSafeMultisig.address, payload); - service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(4); - - // Terminate the service - await serviceManager.connect(deployer).terminate(serviceIds[0]); - service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(5); - - // Unbond agent instances - await serviceManager.connect(operator).unbond(serviceIds[0]); - service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(1); - }); - - it("Creating a service with a custom token, register agent instances and slash", async function () { - const manager = signers[4]; - const owner = signers[5]; - const operator = signers[6]; - const agentInstance = signers[7]; - await agentRegistry.changeManager(manager.address); - - // Creating 3 canonical agents - await agentRegistry.connect(manager).create(owner.address, unitHash, [1]); - await agentRegistry.connect(manager).create(owner.address, unitHash1, [1]); - await agentRegistry.connect(manager).create(owner.address, unitHash2, [1]); - await serviceRegistry.changeManager(serviceManager.address); - await serviceRegistryTokenUtility.changeManager(serviceManager.address); - - const newAgentIds = [1]; - const newAgentParams = [[1, regBond]]; - - // Remove the operator whitelist check completely - serviceManager.connect(deployer).setOperatorWhitelist(AddressZero); - - // Creating one service with the ERC20 token bond - await serviceManager.create(deployer.address, token.address, configHash, newAgentIds, newAgentParams, threshold); - // Approve token for the serviceRegistryTokenUtility contract - await token.connect(deployer).approve(serviceRegistryTokenUtility.address, regBond); - - // Activate the registration - await serviceManager.connect(deployer).activateRegistration(serviceIds[0], {value: 1}); - let service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(2); - - // Approve token for the serviceRegistryTokenUtility contract by the operator - await token.mint(operator.address, regBond); - await token.connect(operator).approve(serviceRegistryTokenUtility.address, regBond); - // Registering agents for the service Id - await serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstance.address], - newAgentIds, {value: regBond}); - service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(3); - - // Whitelist gnosis multisig implementation - await serviceRegistry.changeMultisigPermission(gnosisSafeMultisig.address, true); - // Deploying the service - const safe = await serviceManager.connect(deployer).deploy(serviceIds[0], gnosisSafeMultisig.address, payload); - const result = await safe.wait(); - const proxyAddress = result.events[0].address; - service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(4); - - // Check initial operator's balance - const balanceOperator = Number(await serviceRegistryTokenUtility.getOperatorBalance(operator.address, serviceIds[0])); - expect(balanceOperator).to.equal(regBond); - - // Get all the necessary info about multisig and slash the operator - const multisig = await ethers.getContractAt("GnosisSafe", proxyAddress); - const safeContracts = require("@gnosis.pm/safe-contracts"); - const nonce = await multisig.nonce(); - const txHashData = await safeContracts.buildContractCall(serviceRegistryTokenUtility, "slash", - [[agentInstance.address], [regFine], serviceIds[0]], nonce, 0, 0); - const signMessageData = await safeContracts.safeSignMessage(agentInstance, multisig, txHashData, 0); - - // Slash the agent instance operator with the correct multisig - await safeContracts.executeTx(multisig, txHashData, [signMessageData], 0); - - // Check the new operator's balance, it must be the original balance minus the fine - const newBalanceOperator = Number(await serviceRegistryTokenUtility.getOperatorBalance(operator.address, serviceIds[0])); - expect(newBalanceOperator).to.equal(balanceOperator - regFine); - - // Terminate the service - await serviceManager.connect(deployer).terminate(serviceIds[0]); - service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(5); - - // Unbond agent instances - await serviceManager.connect(operator).unbond(serviceIds[0]); - service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(1); - - // Check the balance of the operator that has to correspond the balance after slashing - const balance = Number(await token.balanceOf(operator.address)); - expect(newBalanceOperator).to.equal(balance); - - // Set drainer and drain funds - const balanceBefore = Number(await token.balanceOf(deployer.address)); - await serviceRegistryTokenUtility.changeDrainer(deployer.address); - await serviceRegistryTokenUtility.connect(deployer).drain(token.address); - const balanceAfter = Number(await token.balanceOf(deployer.address)); - expect(balanceAfter).to.equal(balanceBefore + regFine); - }); - - it("Creating services, updating one of them, activating, registering agent instances", async function () { - const manager = signers[4]; - const owner = signers[5]; - const operator = signers[6]; - const agentInstances = [signers[7].address, signers[8].address, signers[9].address, signers[10].address]; - await agentRegistry.changeManager(manager.address); - - // Creating 3 canonical agents - await agentRegistry.connect(manager).create(owner.address, unitHash, [1]); - await agentRegistry.connect(manager).create(owner.address, unitHash1, [1]); - await agentRegistry.connect(manager).create(owner.address, unitHash2, [1]); - await serviceRegistry.changeManager(serviceManager.address); - await serviceRegistryTokenUtility.changeManager(serviceManager.address); - - // Creating two services - await serviceManager.create(owner.address, ETHAddress, configHash, agentIds, agentParams, - maxThreshold); - await serviceManager.create(owner.address, ETHAddress, configHash, agentIds, agentParams, - maxThreshold); - - // Updating service Id == 1 - const newAgentIds = [1, 2, 3]; - const newAgentParams = [[2, regBond], [0, regBond], [1, regBond]]; - const newMaxThreshold = newAgentParams[0][0] + newAgentParams[2][0]; - await serviceManager.connect(owner).update(ETHAddress, configHash, newAgentIds, - newAgentParams, newMaxThreshold, serviceIds[0]); - let service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(1); - - // Activate the registration - await serviceManager.connect(owner).activateRegistration(serviceIds[0], {value: regDeposit}); - await serviceManager.connect(owner).activateRegistration(serviceIds[1], {value: regDeposit}); - service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(2); - - // Fail when trying to update the service again, even though no agent instances are registered yet - await expect( - serviceManager.connect(owner).update(ETHAddress, configHash, newAgentIds, - newAgentParams, newMaxThreshold, serviceIds[0]) - ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); - - // Registering agents for service Id == 1 - await serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[0], agentInstances[2]], - [newAgentIds[0], newAgentIds[0]], {value: 2*regBond}); - // After the update, service has only 2 slots for canonical agent 1 and 1 slot for canonical agent 3 - await expect( - serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[3]], [newAgentIds[0]], {value: regBond}) - ).to.be.revertedWithCustomError(serviceManager, "AgentInstancesSlotsFilled"); - // Registering agent instance for the last possible slot - await serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[1]], - [newAgentIds[2]], {value: regBond}); - // Now all slots are filled and the service cannot register more agent instances - await expect( - serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[3]], [newAgentIds[2]], {value: regBond}) - ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); - - // When terminated, no agent instance registration is possible - await serviceManager.connect(owner).terminate(serviceIds[1]); - const newAgentInstance = signers[11].address; - await expect( - serviceManager.connect(operator).registerAgents(serviceIds[1], [newAgentInstance], [agentIds[0]], {value: regBond}) - ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); - - expect(await serviceRegistry.exists(2)).to.equal(true); - expect(await serviceRegistry.exists(3)).to.equal(false); - }); - - it("Should fail when updating a service with zero bonds and non-zero slots", async function () { - const manager = signers[4]; - const owner = signers[5]; - await agentRegistry.changeManager(manager.address); - - // Creating 3 canonical agents - await agentRegistry.connect(manager).create(owner.address, unitHash, [1]); - await agentRegistry.connect(manager).create(owner.address, unitHash1, [1]); - await agentRegistry.connect(manager).create(owner.address, unitHash2, [1]); - await serviceRegistry.changeManager(serviceManager.address); - await serviceRegistryTokenUtility.changeManager(serviceManager.address); - - // Creating a service - await serviceManager.create(owner.address, ETHAddress, configHash, agentIds, agentParams, maxThreshold); + serviceRegistryImplementations.forEach(async function (serviceRegistryImplementation) { + it("Creating a service with a custom token, update, register, deploy, terminate, unbond", async function () { + // Choose the service registry implementation: L1 or L2 + if (serviceRegistryImplementation == "l2") { + serviceRegistry = serviceRegistryL2; + serviceManager = serviceManagerL2; + serviceRegistryTokenUtility = serviceRegistryTokenUtilityL2; + operatorWhitelist = operatorWhitelistL2; + } - // Updating service Id == 1 - const newAgentIds = [1, 2, 3]; - const newAgentParams = [[2, regBond], [0, regBond], [1, 0]]; - const newMaxThreshold = newAgentParams[0][0] + newAgentParams[2][0]; - // Try to update a service with the non-zero slot and a zero bond - await expect( - serviceManager.connect(owner).update(ETHAddress, configHash, newAgentIds, newAgentParams, - newMaxThreshold, serviceIds[0]) - ).to.be.revertedWithCustomError(serviceManager, "ZeroValue"); - }); + const manager = signers[4]; + const owner = signers[5]; + const operator = signers[6]; + const agentInstances = [signers[7].address, signers[8].address]; + + if (serviceRegistryImplementation == "l1") { + await agentRegistry.changeManager(manager.address); + // Creating 3 canonical agents + await agentRegistry.connect(manager).create(owner.address, unitHash, [1]); + await agentRegistry.connect(manager).create(owner.address, unitHash1, [1]); + await agentRegistry.connect(manager).create(owner.address, unitHash2, [1]); + } + await serviceRegistry.changeManager(serviceManager.address); + await serviceRegistryTokenUtility.changeManager(serviceManager.address); + + // Try to create a service with a token having specified at least one zero bond + let errAgentIds = [1, 4]; + let errAgentParams = [[1, 0], [1, regBond]]; + await expect( + serviceManager.create(deployer.address, token.address, configHash, errAgentIds, errAgentParams, maxThreshold) + ).to.be.revertedWithCustomError(serviceManager, "ZeroValue"); + + // Create one service with the ERC20 token bond + const initAgentIds = [1, 3]; + await serviceManager.create(deployer.address, token.address, configHash, initAgentIds, agentParams, maxThreshold); + + // Try to update a service with at least one slot being non zero and its corresponding bond being a zero + errAgentIds = [1, 2, 3]; + errAgentParams = [[1, regBond], [1, 0], [1, regBond]]; + const errThreshold = errAgentParams[0][0] + errAgentParams[1][0] + errAgentParams[2][0]; + await expect( + serviceManager.update(token.address, configHash, errAgentIds, errAgentParams, errThreshold, serviceIds[0]) + ).to.be.revertedWithCustomError(serviceManager, "ZeroValue"); + + // Construct correct values for the service update + const newAgentIds = [1, 2, 3]; + const newAgentParams = [[1, regBond], [1, regBond], [0, 0]]; + const newMaxThreshold = newAgentParams[0][0] + newAgentParams[1][0]; + + // Update a service with the ERC20 token bond + await serviceManager.update(token.address, configHash, newAgentIds, newAgentParams, newMaxThreshold, serviceIds[0]); + // Approve token for the serviceRegistryTokenUtility contract + await token.connect(deployer).approve(serviceRegistryTokenUtility.address, regBond); + + // Activate the registration + await serviceManager.connect(deployer).activateRegistration(serviceIds[0], {value: 1}); + let service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(2); + + // Set the operator whitelist checker contract + // Whitelist a random operator address for the service + await operatorWhitelist.setOperatorsStatuses(serviceIds[0], [signers[15].address], [true], true); + // Try to register agents with the non-whitelited operator address + await expect( + serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[0], agentInstances[1]], + agentIds, {value: 2}) + ).to.be.revertedWithCustomError(serviceManager, "WrongOperator"); + + // Whitelist a correct operator address + await operatorWhitelist.setOperatorsStatuses(serviceIds[0], [operator.address], [true], true); + // Try to register agents without the approve from the operator + await expect( + serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[0], agentInstances[1]], + agentIds, {value: 2}) + ).to.be.revertedWithCustomError(serviceManager, "IncorrectAgentBondingValue"); + + // Approve token for the serviceRegistryTokenUtility contract by the operator + await token.mint(operator.address, 2 * regBond); + await token.connect(operator).approve(serviceRegistryTokenUtility.address, 2 * regBond); + // Registering agents for the service Id + await serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[0], agentInstances[1]], + agentIds, {value: 2*regBond}); + service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(3); + + // Whitelist gnosis multisig implementation + await serviceRegistry.changeMultisigPermission(gnosisSafeMultisig.address, true); + // Deploying the service + await serviceManager.connect(deployer).deploy(serviceIds[0], gnosisSafeMultisig.address, payload); + service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(4); + + // Terminate the service + await serviceManager.connect(deployer).terminate(serviceIds[0]); + service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(5); + + // Unbond agent instances + await serviceManager.connect(operator).unbond(serviceIds[0]); + service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(1); + }); + + it("Creating a service with a custom token, register agent instances and slash", async function () { + // Choose the service registry implementation: L1 or L2 + if (serviceRegistryImplementation == "l2") { + serviceRegistry = serviceRegistryL2; + serviceManager = serviceManagerL2; + serviceRegistryTokenUtility = serviceRegistryTokenUtilityL2; + } - it("Creating a service, registering agent instances from different operators, calling Safe", async function () { - const manager = signers[4]; - const owner = signers[5]; - const operators = [signers[6], signers[7]]; - const agentInstances = [signers[8].address, signers[9].address, signers[10].address, signers[11].address]; - await agentRegistry.changeManager(manager.address); + const manager = signers[4]; + const owner = signers[5]; + const operator = signers[6]; + const agentInstance = signers[7]; + + if (serviceRegistryImplementation == "l1") { + await agentRegistry.changeManager(manager.address); + // Creating 3 canonical agents + await agentRegistry.connect(manager).create(owner.address, unitHash, [1]); + await agentRegistry.connect(manager).create(owner.address, unitHash1, [1]); + await agentRegistry.connect(manager).create(owner.address, unitHash2, [1]); + } - // Creating 2 canonical agents - await agentRegistry.connect(manager).create(owner.address, unitHash, [1]); - await agentRegistry.connect(manager).create(owner.address, unitHash1, [1]); - await serviceRegistry.changeManager(serviceManager.address); - await serviceRegistryTokenUtility.changeManager(serviceManager.address); + await serviceRegistry.changeManager(serviceManager.address); + await serviceRegistryTokenUtility.changeManager(serviceManager.address); + + const newAgentIds = [1]; + const newAgentParams = [[1, regBond]]; + + // Remove the operator whitelist check completely + serviceManager.connect(deployer).setOperatorWhitelist(AddressZero); + + // Creating one service with the ERC20 token bond + await serviceManager.create(deployer.address, token.address, configHash, newAgentIds, newAgentParams, threshold); + // Approve token for the serviceRegistryTokenUtility contract + await token.connect(deployer).approve(serviceRegistryTokenUtility.address, regBond); + + // Activate the registration + await serviceManager.connect(deployer).activateRegistration(serviceIds[0], {value: 1}); + let service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(2); + + // Approve token for the serviceRegistryTokenUtility contract by the operator + await token.mint(operator.address, regBond); + await token.connect(operator).approve(serviceRegistryTokenUtility.address, regBond); + // Registering agents for the service Id + await serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstance.address], + newAgentIds, {value: regBond}); + service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(3); + + // Whitelist gnosis multisig implementation + await serviceRegistry.changeMultisigPermission(gnosisSafeMultisig.address, true); + // Deploying the service + const safe = await serviceManager.connect(deployer).deploy(serviceIds[0], gnosisSafeMultisig.address, payload); + const result = await safe.wait(); + const proxyAddress = result.events[0].address; + service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(4); + + // Check initial operator's balance + const balanceOperator = Number(await serviceRegistryTokenUtility.getOperatorBalance(operator.address, serviceIds[0])); + expect(balanceOperator).to.equal(regBond); + + // Get all the necessary info about multisig and slash the operator + const multisig = await ethers.getContractAt("GnosisSafe", proxyAddress); + const safeContracts = require("@gnosis.pm/safe-contracts"); + const nonce = await multisig.nonce(); + const txHashData = await safeContracts.buildContractCall(serviceRegistryTokenUtility, "slash", + [[agentInstance.address], [regFine], serviceIds[0]], nonce, 0, 0); + const signMessageData = await safeContracts.safeSignMessage(agentInstance, multisig, txHashData, 0); + + // Slash the agent instance operator with the correct multisig + await safeContracts.executeTx(multisig, txHashData, [signMessageData], 0); + + // Check the new operator's balance, it must be the original balance minus the fine + const newBalanceOperator = Number(await serviceRegistryTokenUtility.getOperatorBalance(operator.address, serviceIds[0])); + expect(newBalanceOperator).to.equal(balanceOperator - regFine); + + // Terminate the service + await serviceManager.connect(deployer).terminate(serviceIds[0]); + service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(5); + + // Unbond agent instances + await serviceManager.connect(operator).unbond(serviceIds[0]); + service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(1); + + // Check the balance of the operator that has to correspond the balance after slashing + const balance = Number(await token.balanceOf(operator.address)); + expect(newBalanceOperator).to.equal(balance); + + // Set drainer and drain funds + const balanceBefore = Number(await token.balanceOf(deployer.address)); + await serviceRegistryTokenUtility.changeDrainer(deployer.address); + await serviceRegistryTokenUtility.connect(deployer).drain(token.address); + const balanceAfter = Number(await token.balanceOf(deployer.address)); + expect(balanceAfter).to.equal(balanceBefore + regFine); + }); + + it("Creating services, updating one of them, activating, registering agent instances", async function () { + // Choose the service registry implementation: L1 or L2 + if (serviceRegistryImplementation == "l2") { + serviceRegistry = serviceRegistryL2; + serviceManager = serviceManagerL2; + serviceRegistryTokenUtility = serviceRegistryTokenUtilityL2; + } - // Creating a service - const newAgentIds = [1, 2]; - const newAgentParams = [[2, regBond], [1, regBond]]; - const newMaxThreshold = newAgentParams[0][0] + newAgentParams[1][0]; - await serviceManager.create(owner.address, ETHAddress, configHash, newAgentIds, - newAgentParams, newMaxThreshold); - await serviceManager.connect(owner).activateRegistration(serviceIds[0], {value: regDeposit}); + const manager = signers[4]; + const owner = signers[5]; + const operator = signers[6]; + const agentInstances = [signers[7].address, signers[8].address, signers[9].address, signers[10].address]; + + if (serviceRegistryImplementation == "l1") { + await agentRegistry.changeManager(manager.address); + // Creating 3 canonical agents + await agentRegistry.connect(manager).create(owner.address, unitHash, [1]); + await agentRegistry.connect(manager).create(owner.address, unitHash1, [1]); + await agentRegistry.connect(manager).create(owner.address, unitHash2, [1]); + } - // Registering agents for service Id == 1 - await serviceManager.connect(operators[0]).registerAgents(serviceIds[0], [agentInstances[0], agentInstances[1]], - [newAgentIds[0], newAgentIds[1]], {value: 2*regBond}); + await serviceRegistry.changeManager(serviceManager.address); + await serviceRegistryTokenUtility.changeManager(serviceManager.address); + + // Creating two services + await serviceManager.create(owner.address, ETHAddress, configHash, agentIds, agentParams, + maxThreshold); + await serviceManager.create(owner.address, ETHAddress, configHash, agentIds, agentParams, + maxThreshold); + + // Updating service Id == 1 + const newAgentIds = [1, 2, 3]; + const newAgentParams = [[2, regBond], [0, regBond], [1, regBond]]; + const newMaxThreshold = newAgentParams[0][0] + newAgentParams[2][0]; + await serviceManager.connect(owner).update(ETHAddress, configHash, newAgentIds, + newAgentParams, newMaxThreshold, serviceIds[0]); + let service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(1); + + // Activate the registration + await serviceManager.connect(owner).activateRegistration(serviceIds[0], {value: regDeposit}); + await serviceManager.connect(owner).activateRegistration(serviceIds[1], {value: regDeposit}); + service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(2); + + // Fail when trying to update the service again, even though no agent instances are registered yet + await expect( + serviceManager.connect(owner).update(ETHAddress, configHash, newAgentIds, + newAgentParams, newMaxThreshold, serviceIds[0]) + ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); + + // Registering agents for service Id == 1 + await serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[0], agentInstances[2]], + [newAgentIds[0], newAgentIds[0]], {value: 2*regBond}); + // After the update, service has only 2 slots for canonical agent 1 and 1 slot for canonical agent 3 + await expect( + serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[3]], [newAgentIds[0]], {value: regBond}) + ).to.be.revertedWithCustomError(serviceManager, "AgentInstancesSlotsFilled"); + // Registering agent instance for the last possible slot + await serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[1]], + [newAgentIds[2]], {value: regBond}); + // Now all slots are filled and the service cannot register more agent instances + await expect( + serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[3]], [newAgentIds[2]], {value: regBond}) + ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); + + // When terminated, no agent instance registration is possible + await serviceManager.connect(owner).terminate(serviceIds[1]); + const newAgentInstance = signers[11].address; + await expect( + serviceManager.connect(operator).registerAgents(serviceIds[1], [newAgentInstance], [agentIds[0]], {value: regBond}) + ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); + + expect(await serviceRegistry.exists(2)).to.equal(true); + expect(await serviceRegistry.exists(3)).to.equal(false); + }); + + it("Should fail when updating a service with zero bonds and non-zero slots", async function () { + // Choose the service registry implementation: L1 or L2 + if (serviceRegistryImplementation == "l2") { + serviceRegistry = serviceRegistryL2; + serviceManager = serviceManagerL2; + serviceRegistryTokenUtility = serviceRegistryTokenUtilityL2; + } - // Whitelist gnosis multisig implementation - await serviceRegistry.changeMultisigPermission(gnosisSafeMultisig.address, true); + const manager = signers[4]; + const owner = signers[5]; - // Safe is not possible without all the registered agent instances - await expect( - serviceManager.connect(owner).deploy(serviceIds[0], gnosisSafeMultisig.address, payload) - ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); - // Registering the final agent instance - await serviceManager.connect(operators[0]).registerAgents(serviceIds[0], [agentInstances[2]], - [newAgentIds[0]], {value: regBond}); + if (serviceRegistryImplementation == "l1") { + await agentRegistry.changeManager(manager.address); + // Creating 3 canonical agents + await agentRegistry.connect(manager).create(owner.address, unitHash, [1]); + await agentRegistry.connect(manager).create(owner.address, unitHash1, [1]); + await agentRegistry.connect(manager).create(owner.address, unitHash2, [1]); + } - // Check that neither of operators can register the agent after all slots have been filled - await expect( - serviceManager.connect(operators[0]).registerAgents(serviceIds[0], [agentInstances[3]], - [newAgentIds[0]], {value: regBond}) - ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); - await expect( - serviceManager.connect(operators[1]).registerAgents(serviceIds[0], [agentInstances[3]], - [newAgentIds[1]], {value: regBond}) - ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); + await serviceRegistry.changeManager(serviceManager.address); + await serviceRegistryTokenUtility.changeManager(serviceManager.address); + + // Creating a service + await serviceManager.create(owner.address, ETHAddress, configHash, agentIds, agentParams, maxThreshold); + + // Updating service Id == 1 + const newAgentIds = [1, 2, 3]; + const newAgentParams = [[2, regBond], [0, regBond], [1, 0]]; + const newMaxThreshold = newAgentParams[0][0] + newAgentParams[2][0]; + // Try to update a service with the non-zero slot and a zero bond + await expect( + serviceManager.connect(owner).update(ETHAddress, configHash, newAgentIds, newAgentParams, + newMaxThreshold, serviceIds[0]) + ).to.be.revertedWithCustomError(serviceManager, "ZeroValue"); + }); + + it("Creating a service, registering agent instances from different operators, calling Safe", async function () { + // Choose the service registry implementation: L1 or L2 + if (serviceRegistryImplementation == "l2") { + serviceRegistry = serviceRegistryL2; + serviceManager = serviceManagerL2; + serviceRegistryTokenUtility = serviceRegistryTokenUtilityL2; + } - // Creating Safe with blanc safe parameters for the test - const safe = await serviceManager.connect(owner).deploy(serviceIds[0], gnosisSafeMultisig.address, payload); - const result = await safe.wait(); - const proxyAddress = result.events[0].address; + const manager = signers[4]; + const owner = signers[5]; + const operators = [signers[6], signers[7]]; + const agentInstances = [signers[8].address, signers[9].address, signers[10].address, signers[11].address]; - // Verify the deployment of the created Safe: checking threshold and owners - const proxyContract = await ethers.getContractAt("GnosisSafe", proxyAddress); - if (await proxyContract.getThreshold() != newMaxThreshold) { - throw new Error("incorrect threshold"); - } - const actualAgentInstances = [agentInstances[0], agentInstances[1], agentInstances[2]]; - for (const aInstance of actualAgentInstances) { - const isOwner = await proxyContract.isOwner(aInstance); - if (!isOwner) { - throw new Error("incorrect agent instance"); + if (serviceRegistryImplementation == "l1") { + await agentRegistry.changeManager(manager.address); + // Creating 2 canonical agents + await agentRegistry.connect(manager).create(owner.address, unitHash, [1]); + await agentRegistry.connect(manager).create(owner.address, unitHash1, [1]); } - } - }); - it("Creating services, getting resulting information", async function () { - const manager = signers[4]; - const sigOwner = signers[5]; - const owner = signers[5].address; - await agentRegistry.changeManager(manager.address); + await serviceRegistry.changeManager(serviceManager.address); + await serviceRegistryTokenUtility.changeManager(serviceManager.address); + + // Creating a service + const newAgentIds = [1, 2]; + const newAgentParams = [[2, regBond], [1, regBond]]; + const newMaxThreshold = newAgentParams[0][0] + newAgentParams[1][0]; + await serviceManager.create(owner.address, ETHAddress, configHash, newAgentIds, + newAgentParams, newMaxThreshold); + await serviceManager.connect(owner).activateRegistration(serviceIds[0], {value: regDeposit}); + + // Registering agents for service Id == 1 + await serviceManager.connect(operators[0]).registerAgents(serviceIds[0], [agentInstances[0], agentInstances[1]], + [newAgentIds[0], newAgentIds[1]], {value: 2*regBond}); + + // Whitelist gnosis multisig implementation + await serviceRegistry.changeMultisigPermission(gnosisSafeMultisig.address, true); + + // Safe is not possible without all the registered agent instances + await expect( + serviceManager.connect(owner).deploy(serviceIds[0], gnosisSafeMultisig.address, payload) + ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); + // Registering the final agent instance + await serviceManager.connect(operators[0]).registerAgents(serviceIds[0], [agentInstances[2]], + [newAgentIds[0]], {value: regBond}); + + // Check that neither of operators can register the agent after all slots have been filled + await expect( + serviceManager.connect(operators[0]).registerAgents(serviceIds[0], [agentInstances[3]], + [newAgentIds[0]], {value: regBond}) + ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); + await expect( + serviceManager.connect(operators[1]).registerAgents(serviceIds[0], [agentInstances[3]], + [newAgentIds[1]], {value: regBond}) + ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); + + // Creating Safe with blanc safe parameters for the test + const safe = await serviceManager.connect(owner).deploy(serviceIds[0], gnosisSafeMultisig.address, payload); + const result = await safe.wait(); + const proxyAddress = result.events[0].address; + + // Verify the deployment of the created Safe: checking threshold and owners + const proxyContract = await ethers.getContractAt("GnosisSafe", proxyAddress); + if (await proxyContract.getThreshold() != newMaxThreshold) { + throw new Error("incorrect threshold"); + } + const actualAgentInstances = [agentInstances[0], agentInstances[1], agentInstances[2]]; + for (const aInstance of actualAgentInstances) { + const isOwner = await proxyContract.isOwner(aInstance); + if (!isOwner) { + throw new Error("incorrect agent instance"); + } + } + }); + + it("Creating services, getting resulting information", async function () { + // Choose the service registry implementation: L1 or L2 + if (serviceRegistryImplementation == "l2") { + serviceRegistry = serviceRegistryL2; + serviceManager = serviceManagerL2; + serviceRegistryTokenUtility = serviceRegistryTokenUtilityL2; + } - // Creating 2 canonical agents - await agentRegistry.connect(manager).create(owner, unitHash, [1]); - await agentRegistry.connect(manager).create(owner, unitHash1, [1]); - await serviceRegistry.changeManager(serviceManager.address); - await serviceRegistryTokenUtility.changeManager(serviceManager.address); + const manager = signers[4]; + const sigOwner = signers[5]; + const owner = signers[5].address; - // Creating two services - await serviceManager.create(owner, ETHAddress, configHash, agentIds, agentParams, - maxThreshold); - await serviceManager.create(owner, ETHAddress, configHash, agentIds, agentParams, - maxThreshold); + if (serviceRegistryImplementation == "l1") { + await agentRegistry.changeManager(manager.address); + // Creating 2 canonical agents + await agentRegistry.connect(manager).create(owner, unitHash, [1]); + await agentRegistry.connect(manager).create(owner, unitHash1, [1]); + } - // Initial checks - // Total supply must be 2 - let totalSupply = await serviceRegistry.totalSupply(); - expect(totalSupply).to.equal(2); - // Balance of owner is 2, each service id belongs to the owner - expect(await serviceRegistry.balanceOf(owner)).to.equal(2); - expect(await serviceRegistry.ownerOf(serviceIds[0])).to.equal(owner); - expect(await serviceRegistry.ownerOf(serviceIds[1])).to.equal(owner); - // Getting the set of service Ids of the owner - let serviceIdsRet = await serviceRegistry.balanceOf(owner); - for (let i = 0; i < serviceIdsRet; i++) { - const serviceIdCheck = await serviceRegistry.tokenByIndex(i); - expect(serviceIdCheck).to.be.equal(i + 1); - } - - // Activate registration and terminate the very first service and destroy it - await serviceManager.connect(sigOwner).activateRegistration(serviceIds[0], {value: regDeposit}); - await serviceManager.connect(sigOwner).terminate(serviceIds[0]); - - // Check for the information consistency - totalSupply = await serviceRegistry.totalSupply(); - expect(totalSupply).to.equal(2); - // Balance of owner is 1, only service Id 2 belongs to the owner - expect(await serviceRegistry.balanceOf(owner)).to.equal(2); - expect(await serviceRegistry.exists(serviceIds[0])).to.equal(true); - expect(await serviceRegistry.exists(serviceIds[1])).to.equal(true); - expect(await serviceRegistry.ownerOf(serviceIds[0])).to.equal(owner); - expect(await serviceRegistry.ownerOf(serviceIds[1])).to.equal(owner); - // Getting the set of service Ids of the owner, must be service Id 2 only - serviceIdsRet = await serviceRegistry.balanceOf(owner); - expect(serviceIdsRet).to.equal(2); - expect(await serviceRegistry.tokenByIndex(0)).to.equal(1); - expect(await serviceRegistry.tokenByIndex(1)).to.equal(2); - }); + await serviceRegistry.changeManager(serviceManager.address); + await serviceRegistryTokenUtility.changeManager(serviceManager.address); + + // Creating two services + await serviceManager.create(owner, ETHAddress, configHash, agentIds, agentParams, + maxThreshold); + await serviceManager.create(owner, ETHAddress, configHash, agentIds, agentParams, + maxThreshold); + + // Initial checks + // Total supply must be 2 + let totalSupply = await serviceRegistry.totalSupply(); + expect(totalSupply).to.equal(2); + // Balance of owner is 2, each service id belongs to the owner + expect(await serviceRegistry.balanceOf(owner)).to.equal(2); + expect(await serviceRegistry.ownerOf(serviceIds[0])).to.equal(owner); + expect(await serviceRegistry.ownerOf(serviceIds[1])).to.equal(owner); + // Getting the set of service Ids of the owner + let serviceIdsRet = await serviceRegistry.balanceOf(owner); + for (let i = 0; i < serviceIdsRet; i++) { + const serviceIdCheck = await serviceRegistry.tokenByIndex(i); + expect(serviceIdCheck).to.be.equal(i + 1); + } - it("Terminated service is unbonded", async function () { - const mechManager = signers[3]; - const owner = signers[4]; - const operator = signers[5]; - const agentInstances = [signers[6].address, signers[7].address]; - const maxThreshold = 2; - await agentRegistry.changeManager(mechManager.address); - await agentRegistry.connect(mechManager).create(owner.address, unitHash, [1]); - await agentRegistry.connect(mechManager).create(owner.address, unitHash1, [1]); - await serviceRegistry.changeManager(serviceManager.address); - await serviceRegistryTokenUtility.changeManager(serviceManager.address); - await serviceManager.connect(owner).create(owner.address, ETHAddress, configHash, [agentIds[0]], - [[maxThreshold, regBond]], maxThreshold); + // Activate registration and terminate the very first service and destroy it + await serviceManager.connect(sigOwner).activateRegistration(serviceIds[0], {value: regDeposit}); + await serviceManager.connect(sigOwner).terminate(serviceIds[0]); + + // Check for the information consistency + totalSupply = await serviceRegistry.totalSupply(); + expect(totalSupply).to.equal(2); + // Balance of owner is 1, only service Id 2 belongs to the owner + expect(await serviceRegistry.balanceOf(owner)).to.equal(2); + expect(await serviceRegistry.exists(serviceIds[0])).to.equal(true); + expect(await serviceRegistry.exists(serviceIds[1])).to.equal(true); + expect(await serviceRegistry.ownerOf(serviceIds[0])).to.equal(owner); + expect(await serviceRegistry.ownerOf(serviceIds[1])).to.equal(owner); + // Getting the set of service Ids of the owner, must be service Id 2 only + serviceIdsRet = await serviceRegistry.balanceOf(owner); + expect(serviceIdsRet).to.equal(2); + expect(await serviceRegistry.tokenByIndex(0)).to.equal(1); + expect(await serviceRegistry.tokenByIndex(1)).to.equal(2); + }); + + it("Terminated service is unbonded", async function () { + // Choose the service registry implementation: L1 or L2 + if (serviceRegistryImplementation == "l2") { + serviceRegistry = serviceRegistryL2; + serviceManager = serviceManagerL2; + serviceRegistryTokenUtility = serviceRegistryTokenUtilityL2; + } - // Activate agent instance registration and register an agent instance - await serviceManager.connect(owner).activateRegistration(serviceIds[0], {value: regDeposit}); - await serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[0]], - [agentIds[0]], {value: regBond}); + const mechManager = signers[3]; + const owner = signers[4]; + const operator = signers[5]; + const agentInstances = [signers[6].address, signers[7].address]; + const maxThreshold = 2; - // Try to unbond when service is still in active registration - await expect( - serviceManager.connect(operator).unbond(serviceIds[0]) - ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); + if (serviceRegistryImplementation == "l1") { + await agentRegistry.changeManager(mechManager.address); + await agentRegistry.connect(mechManager).create(owner.address, unitHash, [1]); + await agentRegistry.connect(mechManager).create(owner.address, unitHash1, [1]); + } - // Registering the remaining agent instance - await serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[1]], - [agentIds[0]], {value: regBond}); + await serviceRegistry.changeManager(serviceManager.address); + await serviceRegistryTokenUtility.changeManager(serviceManager.address); + await serviceManager.connect(owner).create(owner.address, ETHAddress, configHash, [agentIds[0]], + [[maxThreshold, regBond]], maxThreshold); + + // Activate agent instance registration and register an agent instance + await serviceManager.connect(owner).activateRegistration(serviceIds[0], {value: regDeposit}); + await serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[0]], + [agentIds[0]], {value: regBond}); + + // Try to unbond when service is still in active registration + await expect( + serviceManager.connect(operator).unbond(serviceIds[0]) + ).to.be.revertedWithCustomError(serviceManager, "WrongServiceState"); + + // Registering the remaining agent instance + await serviceManager.connect(operator).registerAgents(serviceIds[0], [agentInstances[1]], + [agentIds[0]], {value: regBond}); + + // Terminate the service before it's deployed + await serviceManager.connect(owner).terminate(serviceIds[0]); + let service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(5); + + // Unbond agent instances. Since all the agents will eb unbonded, the service state is terminated-unbonded + await serviceManager.connect(operator).unbond(serviceIds[0]); + service = await serviceRegistry.getService(serviceIds[0]); + expect(service.state).to.equal(1); + }); + + it("Update the terminated service without registered agent instances", async function () { + // Choose the service registry implementation: L1 or L2 + if (serviceRegistryImplementation == "l2") { + serviceRegistry = serviceRegistryL2; + serviceManager = serviceManagerL2; + serviceRegistryTokenUtility = serviceRegistryTokenUtilityL2; + } - // Terminate the service before it's deployed - await serviceManager.connect(owner).terminate(serviceIds[0]); - let service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(5); + const manager = signers[4]; + const owner = signers[5]; - // Unbond agent instances. Since all the agents will eb unbonded, the service state is terminated-unbonded - await serviceManager.connect(operator).unbond(serviceIds[0]); - service = await serviceRegistry.getService(serviceIds[0]); - expect(service.state).to.equal(1); - }); + if (serviceRegistryImplementation == "l1") { + await agentRegistry.changeManager(manager.address); + await agentRegistry.connect(manager).create(owner.address, unitHash, [1]); + await agentRegistry.connect(manager).create(owner.address, unitHash1, [1]); + await agentRegistry.connect(manager).create(owner.address, unitHash2, [1]); + } - it("Update the terminated service without registered agent instances", async function () { - const manager = signers[4]; - const owner = signers[5]; - await agentRegistry.changeManager(manager.address); - await agentRegistry.connect(manager).create(owner.address, unitHash, [1]); - await agentRegistry.connect(manager).create(owner.address, unitHash1, [1]); - await agentRegistry.connect(manager).create(owner.address, unitHash2, [1]); - await serviceRegistry.changeManager(serviceManager.address); - await serviceRegistryTokenUtility.changeManager(serviceManager.address); - await serviceManager.create(owner.address, ETHAddress, configHash, agentIds, agentParams, - maxThreshold); - await serviceManager.create(owner.address, ETHAddress, configHash, agentIds, agentParams, - maxThreshold); - await serviceManager.connect(owner).activateRegistration(serviceIds[0], {value: regDeposit}); - await serviceManager.connect(owner).terminate(serviceIds[0]); - await serviceManager.connect(owner).update(ETHAddress, configHash, [1, 2, 3], - [[3, regBond], [0, regBond], [4, regBond]], maxThreshold, serviceIds[0]); + await serviceRegistry.changeManager(serviceManager.address); + await serviceRegistryTokenUtility.changeManager(serviceManager.address); + await serviceManager.create(owner.address, ETHAddress, configHash, agentIds, agentParams, + maxThreshold); + await serviceManager.create(owner.address, ETHAddress, configHash, agentIds, agentParams, + maxThreshold); + await serviceManager.connect(owner).activateRegistration(serviceIds[0], {value: regDeposit}); + await serviceManager.connect(owner).terminate(serviceIds[0]); + await serviceManager.connect(owner).update(ETHAddress, configHash, [1, 2, 3], + [[3, regBond], [0, regBond], [4, regBond]], maxThreshold, serviceIds[0]); + }); }); }); From f7b0036b3b5d2be2b81a56d2a4f10a91653fa872 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuperman Date: Tue, 19 Sep 2023 18:33:07 +0100 Subject: [PATCH 2/7] chore: finalizing setup on chiado, preparing scripts for a service manager change on gnosis --- .gitleaksignore | 3 +- docs/configuration.json | 16 ++- hardhat.config.js | 4 +- scripts/audit_chains/audit_contracts_setup.js | 24 +++- ...est_service_registry_l2_change_drainer.js} | 2 +- ...test_service_registry_l2_change_manager.js | 113 ++++++++++++++++++ ...e_registry_token_utility_change_drainer.js | 111 +++++++++++++++++ .../l2/deploy_13_change_managers.js | 12 +- .../l2/deploy_14_15_change_ownerships.js | 4 +- ...ice_registry_l2_change_drainer_polygon.js} | 0 ...vice_registry_l2_change_drainer_gnosis.js} | 2 +- ...service_registry_change_drainer_goerli.js} | 0 ...try_token_utility_change_drainer_gnosis.js | 89 ++++++++++++++ 13 files changed, 363 insertions(+), 17 deletions(-) rename scripts/deployment/l2/bridges/gnosis/{test_change_drainer.js => test_service_registry_l2_change_drainer.js} (98%) create mode 100644 scripts/deployment/l2/bridges/gnosis/test_service_registry_l2_change_manager.js create mode 100644 scripts/deployment/l2/bridges/gnosis/test_service_registry_token_utility_change_drainer.js rename scripts/proposals/{proposal_01_change_drainer_polygon.js => proposal_01_service_registry_l2_change_drainer_polygon.js} (100%) rename scripts/proposals/{proposal_02_change_drainer_gnosis.js => proposal_02_service_registry_l2_change_drainer_gnosis.js} (98%) rename scripts/proposals/{proposal_03_goerli_change_drainer.js => proposal_03_service_registry_change_drainer_goerli.js} (100%) create mode 100644 scripts/proposals/proposal_04_service_registry_token_utility_change_drainer_gnosis.js diff --git a/.gitleaksignore b/.gitleaksignore index c580f600..0267a129 100644 --- a/.gitleaksignore +++ b/.gitleaksignore @@ -33,4 +33,5 @@ f0bbda32a3c2c12a69a1fcef0c360eb524b27ab9:scripts/deployment/globals_mainnet.json d818b0e6ed7f4ebd557b2502cdedea2cadeb22d5:scripts/audit_3_chains/audit.sh:generic-api-key:32 d818b0e6ed7f4ebd557b2502cdedea2cadeb22d5:scripts/audit_3_chains/audit.sh:generic-api-key:43 d818b0e6ed7f4ebd557b2502cdedea2cadeb22d5:scripts/audit_3_chains/audit.sh:generic-api-key:85 -d818b0e6ed7f4ebd557b2502cdedea2cadeb22d5:scripts/audit_3_chains/audit.sh:generic-api-key:86 \ No newline at end of file +d818b0e6ed7f4ebd557b2502cdedea2cadeb22d5:scripts/audit_3_chains/audit.sh:generic-api-key:86 +d780294b8dccf047391a71b75dade50a6b89003b:scripts/deployment/l2/globals_gnosis_chiado.json:generic-api-key:1 \ No newline at end of file diff --git a/docs/configuration.json b/docs/configuration.json index b6516b59..60ce1d37 100644 --- a/docs/configuration.json +++ b/docs/configuration.json @@ -189,9 +189,19 @@ "address": "0x31D3202d8744B16A120117A053459DDFAE93c855" }, { - "name": "ServiceManager", - "artifact": "abis/0.8.19/ServiceManager.json", - "address": "0x29086141ecdc310058fc23273F8ef7881d20C2f7" + "name": "ServiceRegistryTokenUtility", + "artifact": "abis/0.8.19/ServiceRegistryTokenUtility.json", + "address": "0xc2c7E40674f1C7Bb99eFe5680Efd79842502bED4" + }, + { + "name": "ServiceManagerToken", + "artifact": "abis/0.8.19/ServiceManagerToken.json", + "address": "0xc965a32185590Eb5a5fffDba29E96126b7650eDe" + }, + { + "name": "OperatorWhitelist", + "artifact": "abis/0.8.19/OperatorWhitelist.json", + "address": "0x6f7661F52fE1919996d0A4F68D09B344093a349d" }, { "name": "GnosisSafeMultisig", diff --git a/hardhat.config.js b/hardhat.config.js index 973ef36d..072d1b45 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -77,8 +77,8 @@ module.exports = { network: "chiado", chainId: 10200, urls: { - apiURL: "https://blockscout.com/gnosis/chiado/api", - browserURL: "https://blockscout.com/gnosis/chiado", + apiURL: "https://gnosis-chiado.blockscout.com/api", + browserURL: "https://gnosis-chiado.blockscout.com/", }, }, { diff --git a/scripts/audit_chains/audit_contracts_setup.js b/scripts/audit_chains/audit_contracts_setup.js index f0a1b031..679bc0c4 100644 --- a/scripts/audit_chains/audit_contracts_setup.js +++ b/scripts/audit_chains/audit_contracts_setup.js @@ -164,8 +164,8 @@ async function checkServiceRegistry(chainId, provider, globalsInstance, configCo // Check manager const manager = await serviceRegistry.manager(); - if (chainId === "1" || chainId === "5") { - // ServiceRegistryManagerToken for L1 + if (chainId === "1" || chainId === "5" || chainId === "10200") { + // ServiceRegistryManagerToken for L1 and L2 that currently have the full setup customExpect(manager, globalsInstance["serviceManagerTokenAddress"], log + ", function: manager()"); } else { // ServiceRegistryManager for L2 @@ -217,8 +217,8 @@ async function checkServiceManager(chainId, provider, globalsInstance, configCon const paused = await serviceManager.paused(); customExpect(paused, false, log + ", function: paused()"); - // Checks for L1 only - if (chainId === "1" || chainId === "5") { + // Checks for L1 and L2 that currently have the full setup + if (chainId === "1" || chainId === "5" || chainId === "10200") { // Version const version = await serviceManager.version(); customExpect(version, "1.1.1", log + ", function: version()"); @@ -423,8 +423,20 @@ async function main() { let log = initLog + ", contract: " + "ServiceRegistryL2"; await checkServiceRegistry(configs[i]["chainId"], providers[i], globals[i], configs[i]["contracts"], "ServiceRegistryL2", log); - log = initLog + ", contract: " + "ServiceManager"; - await checkServiceManager(configs[i]["chainId"], providers[i], globals[i], configs[i]["contracts"], "ServiceManager", log); + // Make an exception for the chiado network for now as it operates with the ServiceManagerToken + if (configs[i]["chainId"] === "10200") { + log = initLog + ", contract: " + "ServiceManagerToken"; + await checkServiceManager(configs[i]["chainId"], providers[i], globals[i], configs[i]["contracts"], "ServiceManagerToken", log); + + log = initLog + ", contract: " + "ServiceRegistryTokenUtility"; + await checkServiceRegistryTokenUtility(configs[i]["chainId"], providers[i], globals[i], configs[i]["contracts"], "ServiceRegistryTokenUtility", log); + + log = initLog + ", contract: " + "OperatorWhitelist"; + await checkOperatorWhitelist(configs[i]["chainId"], providers[i], globals[i], configs[i]["contracts"], "OperatorWhitelist", log); + } else { + log = initLog + ", contract: " + "ServiceManager"; + await checkServiceManager(configs[i]["chainId"], providers[i], globals[i], configs[i]["contracts"], "ServiceManager", log); + } log = initLog + ", contract: " + "GnosisSafeMultisig"; await checkGnosisSafeImplementation(configs[i]["chainId"], providers[i], globals[i], configs[i]["contracts"], "GnosisSafeMultisig", log); diff --git a/scripts/deployment/l2/bridges/gnosis/test_change_drainer.js b/scripts/deployment/l2/bridges/gnosis/test_service_registry_l2_change_drainer.js similarity index 98% rename from scripts/deployment/l2/bridges/gnosis/test_change_drainer.js rename to scripts/deployment/l2/bridges/gnosis/test_service_registry_l2_change_drainer.js index 92562766..77f12c3f 100644 --- a/scripts/deployment/l2/bridges/gnosis/test_change_drainer.js +++ b/scripts/deployment/l2/bridges/gnosis/test_service_registry_l2_change_drainer.js @@ -57,7 +57,7 @@ async function main() { console.log("Correct wallet setup"); } - // Mock Token contract across the bridge must change the drainer address + // Mock Timelock contract across the bridge must change the drainer address const rawPayload = serviceRegistryL2.interface.encodeFunctionData("changeDrainer", [homeMediatorAddress]); // Pack the second part of data const target = serviceRegistryL2Address; diff --git a/scripts/deployment/l2/bridges/gnosis/test_service_registry_l2_change_manager.js b/scripts/deployment/l2/bridges/gnosis/test_service_registry_l2_change_manager.js new file mode 100644 index 00000000..3a1b3ea0 --- /dev/null +++ b/scripts/deployment/l2/bridges/gnosis/test_service_registry_l2_change_manager.js @@ -0,0 +1,113 @@ +/*global process*/ + +const { ethers } = require("ethers"); + +async function main() { + const ALCHEMY_API_KEY_GOERLI = process.env.ALCHEMY_API_KEY_GOERLI; + const goerliURL = "https://eth-goerli.g.alchemy.com/v2/" + ALCHEMY_API_KEY_GOERLI; + const goerliProvider = new ethers.providers.JsonRpcProvider(goerliURL); + await goerliProvider.getBlockNumber().then((result) => { + console.log("Current block number goerli: " + result); + }); + + const chiadoURL = "https://rpc.chiadochain.net"; + const chiadoProvider = new ethers.providers.JsonRpcProvider(chiadoURL); + await chiadoProvider.getBlockNumber().then((result) => { + console.log("Current block number chiado: " + result); + }); + + const fs = require("fs"); + // AMBProxy address on goerli + const AMBProxyAddress = "0x87A19d769D875964E9Cd41dDBfc397B2543764E6"; + const AMBProxyJSON = "abis/bridges/gnosis/EternalStorageProxy.json"; + let contractFromJSON = fs.readFileSync(AMBProxyJSON, "utf8"); + const AMBProxyABI = JSON.parse(contractFromJSON); + const AMBProxy = new ethers.Contract(AMBProxyAddress, AMBProxyABI, goerliProvider); + + // Test deployed HomeMediator address on chiado + const homeMediatorAddress = "0x0a50009D55Ed5700ac8FF713709d5Ad5fa843896"; + const homeMediatorJSON = "abis/bridges/gnosis/HomeMediator.json"; + contractFromJSON = fs.readFileSync(homeMediatorJSON, "utf8"); + let parsedFile = JSON.parse(contractFromJSON); + const homeMediatorABI = parsedFile["abi"]; + const homeMediator = new ethers.Contract(homeMediatorAddress, homeMediatorABI, chiadoProvider); + + // Mock Timelock contract address on goerli (has AMBProxy address in it already) + const mockTimelockAddress = "0x5b03476a21e9c7cEB8dB1Bd9F24664e480FDcc43"; + const mockTimelockJSON = "abis/bridges/gnosis/test/MockTimelock.json"; + contractFromJSON = fs.readFileSync(mockTimelockJSON, "utf8"); + parsedFile = JSON.parse(contractFromJSON); + const mockTimelockABI = parsedFile["abi"]; + const mockTimelock = new ethers.Contract(mockTimelockAddress, mockTimelockABI, goerliProvider); + + // ServiceRegistryL2 address on chiado + const serviceRegistryL2Address = "0x31D3202d8744B16A120117A053459DDFAE93c855"; + const serviceRegistryL2JSON = "artifacts/contracts/ServiceRegistryL2.sol/ServiceRegistryL2.json"; + contractFromJSON = fs.readFileSync(serviceRegistryL2JSON, "utf8"); + parsedFile = JSON.parse(contractFromJSON); + const serviceRegistryL2ABI = parsedFile["abi"]; + const serviceRegistryL2 = new ethers.Contract(serviceRegistryL2Address, serviceRegistryL2ABI, chiadoProvider); + + // Get the EOA + const account = ethers.utils.HDNode.fromMnemonic(process.env.TESTNET_MNEMONIC).derivePath("m/44'/60'/0'/0/0"); + const EOAgoerli = new ethers.Wallet(account, goerliProvider); + const EOAchiado = new ethers.Wallet(account, chiadoProvider); + console.log("EOA address",EOAgoerli.address); + if (EOAchiado.address == EOAgoerli.address) { + console.log("Correct wallet setup"); + } + + // Mock Timelock contract across the bridge must change the manager address + const globalsFile = fs.readFileSync("globals.json", "utf8"); + parsedFile = JSON.parse(globalsFile); + const rawPayload = serviceRegistryL2.interface.encodeFunctionData("changeManager", [parsedFile.serviceManagerTokenAddress]); + // Pack the second part of data + const target = serviceRegistryL2Address; + const value = 0; + const payload = ethers.utils.arrayify(rawPayload); + const data = ethers.utils.solidityPack( + ["address", "uint96", "uint32", "bytes"], + [target, value, payload.length, payload] + ); + + // Build the final payload to be passed from the imaginary Timelock + const mediatorPayload = await homeMediator.interface.encodeFunctionData("processMessageFromForeign", [data]); + const requestGasLimit = "2000000"; + const timelockPayload = await AMBProxy.interface.encodeFunctionData("requireToPassMessage", [homeMediatorAddress, + mediatorPayload, requestGasLimit]); + + // Send the message to chiado receiver + const tx = await mockTimelock.connect(EOAgoerli).execute(timelockPayload); + console.log("Timelock data execution hash", tx.hash); + await tx.wait(); + + // Wait for the event of a processed data on chiado + // catch NewFxMessage event from serviceRegistryL2 and MessageReceived event from homeMediator + // Compare the data sent and the data from the NewFxMessage event that must match + // MessageReceived(uint256 indexed stateId, address indexed sender, bytes message) + let waitForEvent = true; + while (waitForEvent) { + // Check for the last 100 blocks in order to catch the event + const events = await homeMediator.queryFilter("MessageReceived", -200); + events.forEach((item) => { + const msg = item["args"]["data"]; + if (msg == data) { + console.log("Event MessageReceived. Message in chiado:", msg); + waitForEvent = false; + } + }); + // Continue waiting for the event if none was received + if (waitForEvent) { + console.log("Waiting for the receive event, next update in 5 minutes ..."); + // Sleep for a minute + await new Promise(r => setTimeout(r, 300000)); + } + } +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deployment/l2/bridges/gnosis/test_service_registry_token_utility_change_drainer.js b/scripts/deployment/l2/bridges/gnosis/test_service_registry_token_utility_change_drainer.js new file mode 100644 index 00000000..daf40596 --- /dev/null +++ b/scripts/deployment/l2/bridges/gnosis/test_service_registry_token_utility_change_drainer.js @@ -0,0 +1,111 @@ +/*global process*/ + +const { ethers } = require("ethers"); + +async function main() { + const ALCHEMY_API_KEY_GOERLI = process.env.ALCHEMY_API_KEY_GOERLI; + const goerliURL = "https://eth-goerli.g.alchemy.com/v2/" + ALCHEMY_API_KEY_GOERLI; + const goerliProvider = new ethers.providers.JsonRpcProvider(goerliURL); + await goerliProvider.getBlockNumber().then((result) => { + console.log("Current block number goerli: " + result); + }); + + const chiadoURL = "https://rpc.chiadochain.net"; + const chiadoProvider = new ethers.providers.JsonRpcProvider(chiadoURL); + await chiadoProvider.getBlockNumber().then((result) => { + console.log("Current block number chiado: " + result); + }); + + const fs = require("fs"); + // AMBProxy address on goerli + const AMBProxyAddress = "0x87A19d769D875964E9Cd41dDBfc397B2543764E6"; + const AMBProxyJSON = "abis/bridges/gnosis/EternalStorageProxy.json"; + let contractFromJSON = fs.readFileSync(AMBProxyJSON, "utf8"); + const AMBProxyABI = JSON.parse(contractFromJSON); + const AMBProxy = new ethers.Contract(AMBProxyAddress, AMBProxyABI, goerliProvider); + + // Test deployed HomeMediator address on chiado + const homeMediatorAddress = "0x0a50009D55Ed5700ac8FF713709d5Ad5fa843896"; + const homeMediatorJSON = "abis/bridges/gnosis/HomeMediator.json"; + contractFromJSON = fs.readFileSync(homeMediatorJSON, "utf8"); + let parsedFile = JSON.parse(contractFromJSON); + const homeMediatorABI = parsedFile["abi"]; + const homeMediator = new ethers.Contract(homeMediatorAddress, homeMediatorABI, chiadoProvider); + + // Mock Timelock contract address on goerli (has AMBProxy address in it already) + const mockTimelockAddress = "0x5b03476a21e9c7cEB8dB1Bd9F24664e480FDcc43"; + const mockTimelockJSON = "abis/bridges/gnosis/test/MockTimelock.json"; + contractFromJSON = fs.readFileSync(mockTimelockJSON, "utf8"); + parsedFile = JSON.parse(contractFromJSON); + const mockTimelockABI = parsedFile["abi"]; + const mockTimelock = new ethers.Contract(mockTimelockAddress, mockTimelockABI, goerliProvider); + + // serviceRegistryTokenUtility address on chiado + const serviceRegistryTokenUtilityAddress = "0xc2c7E40674f1C7Bb99eFe5680Efd79842502bED4"; + const serviceRegistryTokenUtilityJSON = "artifacts/contracts/ServiceRegistryTokenUtility.sol/ServiceRegistryTokenUtility.json"; + contractFromJSON = fs.readFileSync(serviceRegistryTokenUtilityJSON, "utf8"); + parsedFile = JSON.parse(contractFromJSON); + const serviceRegistryTokenUtilityABI = parsedFile["abi"]; + const serviceRegistryTokenUtility = new ethers.Contract(serviceRegistryTokenUtilityAddress, serviceRegistryTokenUtilityABI, chiadoProvider); + + // Get the EOA + const account = ethers.utils.HDNode.fromMnemonic(process.env.TESTNET_MNEMONIC).derivePath("m/44'/60'/0'/0/0"); + const EOAgoerli = new ethers.Wallet(account, goerliProvider); + const EOAchiado = new ethers.Wallet(account, chiadoProvider); + console.log("EOA address",EOAgoerli.address); + if (EOAchiado.address == EOAgoerli.address) { + console.log("Correct wallet setup"); + } + + // Mock Timelock contract across the bridge must change the drainer address + const rawPayload = serviceRegistryTokenUtility.interface.encodeFunctionData("changeDrainer", [homeMediatorAddress]); + // Pack the second part of data + const target = serviceRegistryTokenUtilityAddress; + const value = 0; + const payload = ethers.utils.arrayify(rawPayload); + const data = ethers.utils.solidityPack( + ["address", "uint96", "uint32", "bytes"], + [target, value, payload.length, payload] + ); + + // Build the final payload to be passed from the imaginary Timelock + const mediatorPayload = await homeMediator.interface.encodeFunctionData("processMessageFromForeign", [data]); + const requestGasLimit = "2000000"; + const timelockPayload = await AMBProxy.interface.encodeFunctionData("requireToPassMessage", [homeMediatorAddress, + mediatorPayload, requestGasLimit]); + + // Send the message to chiado receiver + const tx = await mockTimelock.connect(EOAgoerli).execute(timelockPayload); + console.log("Timelock data execution hash", tx.hash); + await tx.wait(); + + // Wait for the event of a processed data on chiado + // catch NewFxMessage event from serviceRegistryTokenUtility and MessageReceived event from homeMediator + // Compare the data sent and the data from the NewFxMessage event that must match + // MessageReceived(uint256 indexed stateId, address indexed sender, bytes message) + let waitForEvent = true; + while (waitForEvent) { + // Check for the last 100 blocks in order to catch the event + const events = await homeMediator.queryFilter("MessageReceived", -200); + events.forEach((item) => { + const msg = item["args"]["data"]; + if (msg == data) { + console.log("Event MessageReceived. Message in chiado:", msg); + waitForEvent = false; + } + }); + // Continue waiting for the event if none was received + if (waitForEvent) { + console.log("Waiting for the receive event, next update in 5 minutes ..."); + // Sleep for a minute + await new Promise(r => setTimeout(r, 300000)); + } + } +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deployment/l2/deploy_13_change_managers.js b/scripts/deployment/l2/deploy_13_change_managers.js index f08383af..6b15b999 100644 --- a/scripts/deployment/l2/deploy_13_change_managers.js +++ b/scripts/deployment/l2/deploy_13_change_managers.js @@ -14,6 +14,7 @@ async function main() { const gasPriceInGwei = parsedData.gasPriceInGwei; const serviceRegistryTokenUtilityAddress = parsedData.serviceRegistryTokenUtilityAddress; const serviceManagerTokenAddress = parsedData.serviceManagerTokenAddress; + let bridgeMediatorAddress = parsedData.bridgeMediatorAddress; let EOA; let networkURL; @@ -36,6 +37,8 @@ async function main() { networkURL = "https://rpc.gnosischain.com"; } else if (providerName === "chiado") { networkURL = "https://rpc.chiadochain.net"; + // For the chiado network, the mock timelock contract is set as the owner + bridgeMediatorAddress = parsedData.bridgeMediatorMockTimelockAddress; } else { console.log("Unknown network provider", providerName); return; @@ -60,12 +63,19 @@ async function main() { const gasPrice = ethers.utils.parseUnits(gasPriceInGwei, "gwei"); // Transaction signing and execution - // 13. EOA to change the manager of ServiceRegistryTokenUtility to ServiceManagerToken calling `changeManager(ServiceManagerToken)`; + // 13a. EOA to change the manager of ServiceRegistryTokenUtility to ServiceManagerToken calling `changeManager(ServiceManagerToken)`; console.log("You are signing the following transaction: serviceRegistryTokenUtility.connect(EOA).changeManager(serviceManagerTokenAddress)"); let result = await serviceRegistryTokenUtility.connect(EOA).changeManager(serviceManagerTokenAddress, { gasPrice }); // Transaction details console.log("Contract address:", serviceRegistryTokenUtilityAddress); console.log("Transaction:", result.hash); + + // 13b. EOA to change the drainer of ServiceRegistryTokenUtility to BridgeMediator + console.log("You are signing the following transaction: serviceRegistryTokenUtility.connect(EOA).changeDrainer(bridgeMediatorAddress)"); + result = await serviceRegistryTokenUtility.connect(EOA).changeDrainer(bridgeMediatorAddress, { gasPrice }); + // Transaction details + console.log("Contract address:", serviceRegistryTokenUtilityAddress); + console.log("Transaction:", result.hash); } main() diff --git a/scripts/deployment/l2/deploy_14_15_change_ownerships.js b/scripts/deployment/l2/deploy_14_15_change_ownerships.js index 3711ddbc..05902360 100644 --- a/scripts/deployment/l2/deploy_14_15_change_ownerships.js +++ b/scripts/deployment/l2/deploy_14_15_change_ownerships.js @@ -64,14 +64,14 @@ async function main() { const gasPrice = ethers.utils.parseUnits(gasPriceInGwei, "gwei"); // Transaction signing and execution - // 14. EOA to transfer ownership rights of ServiceRegistryTokenUtility to FxGovernorTunnel calling `changeOwner(FxGovernorTunnel)`; + // 14. EOA to transfer ownership rights of ServiceRegistryTokenUtility to BridgeMediator calling `changeOwner(BridgeMediator)`; console.log("You are signing the following transaction: ServiceRegistryTokenUtility.connect(EOA).changeOwner()"); let result = await serviceRegistryTokenUtility.connect(EOA).changeOwner(bridgeMediatorAddress, { gasPrice }); // Transaction details console.log("Contract address:", serviceRegistryTokenUtilityAddress); console.log("Transaction:", result.hash); - // 15. EOA to transfer ownership rights of ServiceManagerToken to FxGovernorTunnel calling `changeOwner(FxGovernorTunnel)`. + // 15. EOA to transfer ownership rights of ServiceManagerToken to BridgeMediator calling `changeOwner(BridgeMediator)`. console.log("You are signing the following transaction: serviceManagerToken.connect(EOA).changeOwner()"); result = await serviceManagerToken.connect(EOA).changeOwner(bridgeMediatorAddress, { gasPrice }); // Transaction details diff --git a/scripts/proposals/proposal_01_change_drainer_polygon.js b/scripts/proposals/proposal_01_service_registry_l2_change_drainer_polygon.js similarity index 100% rename from scripts/proposals/proposal_01_change_drainer_polygon.js rename to scripts/proposals/proposal_01_service_registry_l2_change_drainer_polygon.js diff --git a/scripts/proposals/proposal_02_change_drainer_gnosis.js b/scripts/proposals/proposal_02_service_registry_l2_change_drainer_gnosis.js similarity index 98% rename from scripts/proposals/proposal_02_change_drainer_gnosis.js rename to scripts/proposals/proposal_02_service_registry_l2_change_drainer_gnosis.js index 328b8f58..85de7cf8 100644 --- a/scripts/proposals/proposal_02_change_drainer_gnosis.js +++ b/scripts/proposals/proposal_02_service_registry_l2_change_drainer_gnosis.js @@ -46,7 +46,7 @@ async function main() { const serviceRegistryABI = parsedFile["abi"]; const serviceRegistry = new ethers.Contract(serviceRegistryAddress, serviceRegistryABI, gnosisProvider); - // Mock Token contract across the bridge must change the drainer address + // Timelock contract across the bridge must change the drainer address const rawPayload = serviceRegistry.interface.encodeFunctionData("changeDrainer", [homeMediatorAddress]); // Pack the second part of data const target = serviceRegistryAddress; diff --git a/scripts/proposals/proposal_03_goerli_change_drainer.js b/scripts/proposals/proposal_03_service_registry_change_drainer_goerli.js similarity index 100% rename from scripts/proposals/proposal_03_goerli_change_drainer.js rename to scripts/proposals/proposal_03_service_registry_change_drainer_goerli.js diff --git a/scripts/proposals/proposal_04_service_registry_token_utility_change_drainer_gnosis.js b/scripts/proposals/proposal_04_service_registry_token_utility_change_drainer_gnosis.js new file mode 100644 index 00000000..eb18637d --- /dev/null +++ b/scripts/proposals/proposal_04_service_registry_token_utility_change_drainer_gnosis.js @@ -0,0 +1,89 @@ +/*global process*/ + +const { ethers } = require("ethers"); + +async function main() { + const fs = require("fs"); + // Polygon mainnet globals file + const globalsFile = "globals.json"; + const dataFromJSON = fs.readFileSync(globalsFile, "utf8"); + const parsedData = JSON.parse(dataFromJSON); + + const ALCHEMY_API_KEY_MAINNET = process.env.ALCHEMY_API_KEY_MAINNET; + const mainnetURL = "https://eth-mainnet.g.alchemy.com/v2/" + ALCHEMY_API_KEY_MAINNET; + const mainnetProvider = new ethers.providers.JsonRpcProvider(mainnetURL); + await mainnetProvider.getBlockNumber().then((result) => { + console.log("Current block number mainnet: " + result); + }); + + const gnosisURL = "https://rpc.gnosischain.com"; + const gnosisProvider = new ethers.providers.JsonRpcProvider(gnosisURL); + await gnosisProvider.getBlockNumber().then((result) => { + console.log("Current block number gnosis: " + result); + }); + + + // AMBProxy address on mainnet + const AMBProxyAddress = parsedData.AMBContractProxyForeignAddress; + const AMBProxyJSON = "abis/bridges/gnosis/EternalStorageProxy.json"; + let contractFromJSON = fs.readFileSync(AMBProxyJSON, "utf8"); + const AMBProxyABI = JSON.parse(contractFromJSON); + const AMBProxy = new ethers.Contract(AMBProxyAddress, AMBProxyABI, mainnetProvider); + + // Test deployed HomeMediator address on chiado + const homeMediatorAddress = parsedData.bridgeMediatorAddress; + const homeMediatorJSON = "abis/bridges/gnosis/HomeMediator.json"; + contractFromJSON = fs.readFileSync(homeMediatorJSON, "utf8"); + let parsedFile = JSON.parse(contractFromJSON); + const homeMediatorABI = parsedFile["abi"]; + const homeMediator = new ethers.Contract(homeMediatorAddress, homeMediatorABI, gnosisProvider); + + // ServiceRegistryL2 address on gnosis + const serviceRegistryAddress = parsedData.serviceRegistryAddress; + const serviceRegistryJSON = "artifacts/contracts/ServiceRegistryL2.sol/ServiceRegistryL2.json"; + contractFromJSON = fs.readFileSync(serviceRegistryJSON, "utf8"); + parsedFile = JSON.parse(contractFromJSON); + const serviceRegistryABI = parsedFile["abi"]; + const serviceRegistry = new ethers.Contract(serviceRegistryAddress, serviceRegistryABI, gnosisProvider); + + // Timelock contract across the bridge must change the manager address + const rawPayload = serviceRegistry.interface.encodeFunctionData("changeManager", [parsedFile.serviceManagerTokenAddress]); + // Pack the second part of data + const target = serviceRegistryAddress; + const value = 0; + const payload = ethers.utils.arrayify(rawPayload); + const data = ethers.utils.solidityPack( + ["address", "uint96", "uint32", "bytes"], + [target, value, payload.length, payload] + ); + + // Proposal preparation + console.log("Proposal 4. Change manager for gnosis ServiceRegistryL2\n"); + const mediatorPayload = await homeMediator.interface.encodeFunctionData("processMessageFromForeign", [data]); + + // AMBContractProxyHomeAddress on gnosis mainnet: 0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59 + // Function to call by homeMediator: processMessageFromForeign + console.log("AMBContractProxyHomeAddress to call homeMediator's processMessageFromForeign function with the data:", data); + + const requestGasLimit = "2000000"; + const timelockPayload = await AMBProxy.interface.encodeFunctionData("requireToPassMessage", [homeMediatorAddress, + mediatorPayload, requestGasLimit]); + + const targets = [AMBProxyAddress]; + const values = [0]; + const callDatas = [timelockPayload]; + const description = "Change Drainer in ServiceRegistryL2 on gnosis"; + + // Proposal details + console.log("targets:", targets); + console.log("values:", values); + console.log("call datas:", callDatas); + console.log("description:", description); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); From bafd58a22756a65fab6a1a94a2ef79453277f585 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuperman Date: Tue, 19 Sep 2023 22:13:50 +0100 Subject: [PATCH 3/7] chore: updating chain audit scripts --- .gitleaksignore | 3 ++- scripts/audit_chains/audit_contracts_setup.js | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitleaksignore b/.gitleaksignore index 0267a129..7ac0441e 100644 --- a/.gitleaksignore +++ b/.gitleaksignore @@ -34,4 +34,5 @@ d818b0e6ed7f4ebd557b2502cdedea2cadeb22d5:scripts/audit_3_chains/audit.sh:generic d818b0e6ed7f4ebd557b2502cdedea2cadeb22d5:scripts/audit_3_chains/audit.sh:generic-api-key:43 d818b0e6ed7f4ebd557b2502cdedea2cadeb22d5:scripts/audit_3_chains/audit.sh:generic-api-key:85 d818b0e6ed7f4ebd557b2502cdedea2cadeb22d5:scripts/audit_3_chains/audit.sh:generic-api-key:86 -d780294b8dccf047391a71b75dade50a6b89003b:scripts/deployment/l2/globals_gnosis_chiado.json:generic-api-key:1 \ No newline at end of file +d780294b8dccf047391a71b75dade50a6b89003b:scripts/deployment/l2/globals_gnosis_chiado.json:generic-api-key:1 +f7b0036b3b5d2be2b81a56d2a4f10a91653fa872:scripts/deployment/l2/bridges/gnosis/test_service_registry_token_utility_change_drainer.js:generic-api-key:44 diff --git a/scripts/audit_chains/audit_contracts_setup.js b/scripts/audit_chains/audit_contracts_setup.js index 679bc0c4..3eed5686 100644 --- a/scripts/audit_chains/audit_contracts_setup.js +++ b/scripts/audit_chains/audit_contracts_setup.js @@ -253,7 +253,11 @@ async function checkServiceRegistryTokenUtility(chainId, provider, globalsInstan // Check drainer const drainer = await serviceRegistryTokenUtility.drainer(); - customExpect(drainer, globalsInstance["treasuryAddress"], log + ", function: drainer()"); + if (chainId === "1" || chainId === "5") { + customExpect(drainer, globalsInstance["timelockAddress"], log + ", function: drainer()"); + } else { + customExpect(drainer, globalsInstance["bridgeMediatorAddress"], log + ", function: drainer()"); + } // Check service registry const serviceRegistry = await serviceRegistryTokenUtility.serviceRegistry(); From 77f4af0e54d4a58b4c73f9e2b523bc7d76d8f40a Mon Sep 17 00:00:00 2001 From: Aleksandr Kuperman Date: Tue, 19 Sep 2023 22:15:49 +0100 Subject: [PATCH 4/7] chore: gitleaks ignore --- .gitleaksignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitleaksignore b/.gitleaksignore index 7ac0441e..0527a475 100644 --- a/.gitleaksignore +++ b/.gitleaksignore @@ -36,3 +36,6 @@ d818b0e6ed7f4ebd557b2502cdedea2cadeb22d5:scripts/audit_3_chains/audit.sh:generic d818b0e6ed7f4ebd557b2502cdedea2cadeb22d5:scripts/audit_3_chains/audit.sh:generic-api-key:86 d780294b8dccf047391a71b75dade50a6b89003b:scripts/deployment/l2/globals_gnosis_chiado.json:generic-api-key:1 f7b0036b3b5d2be2b81a56d2a4f10a91653fa872:scripts/deployment/l2/bridges/gnosis/test_service_registry_token_utility_change_drainer.js:generic-api-key:44 +71afff8bddeb852787f7294d7838832d5bb26a34:scripts/deployment/l2/bridges/gnosis/test_service_registry_token_utility_change_drainer.js:generic-api-key:44 +d780294b8dccf047391a71b75dade50a6b89003b:scripts/deployment/l2/globals_gnosis_chiado.json:generic-api-key:2 + From 72cf89f4c295f6725b4da0ad6e9dac3433ef6ac7 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuperman Date: Tue, 19 Sep 2023 22:17:14 +0100 Subject: [PATCH 5/7] chore: gitleaks ignore --- .gitleaksignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitleaksignore b/.gitleaksignore index 0527a475..087784af 100644 --- a/.gitleaksignore +++ b/.gitleaksignore @@ -38,4 +38,4 @@ d780294b8dccf047391a71b75dade50a6b89003b:scripts/deployment/l2/globals_gnosis_ch f7b0036b3b5d2be2b81a56d2a4f10a91653fa872:scripts/deployment/l2/bridges/gnosis/test_service_registry_token_utility_change_drainer.js:generic-api-key:44 71afff8bddeb852787f7294d7838832d5bb26a34:scripts/deployment/l2/bridges/gnosis/test_service_registry_token_utility_change_drainer.js:generic-api-key:44 d780294b8dccf047391a71b75dade50a6b89003b:scripts/deployment/l2/globals_gnosis_chiado.json:generic-api-key:2 - +7d2fbdb1556dc3ed289edf7e116b378b6f6a9d83:scripts/deployment/l2/bridges/gnosis/test_service_registry_token_utility_change_drainer.js:generic-api-key:44 From 1bfaf6a7f793897dac292b480d5cf787c46c0c0f Mon Sep 17 00:00:00 2001 From: Aleksandr Kuperman Date: Thu, 21 Sep 2023 09:54:18 +0100 Subject: [PATCH 6/7] chore: changing the proposal script name --- ...s => proposal_04_service_registry_l2_change_manager_gnosis.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts/proposals/{proposal_04_service_registry_token_utility_change_drainer_gnosis.js => proposal_04_service_registry_l2_change_manager_gnosis.js} (100%) diff --git a/scripts/proposals/proposal_04_service_registry_token_utility_change_drainer_gnosis.js b/scripts/proposals/proposal_04_service_registry_l2_change_manager_gnosis.js similarity index 100% rename from scripts/proposals/proposal_04_service_registry_token_utility_change_drainer_gnosis.js rename to scripts/proposals/proposal_04_service_registry_l2_change_manager_gnosis.js From cb5dcd25af46e2724c0fc645e2faf09b048efd04 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuperman <95347597+kupermind@users.noreply.github.com> Date: Thu, 21 Sep 2023 10:05:46 +0100 Subject: [PATCH 7/7] Update scripts/proposals/proposal_04_service_registry_l2_change_manager_gnosis.js Co-authored-by: mariapiamo <93650028+mariapiamo@users.noreply.github.com> --- .../proposal_04_service_registry_l2_change_manager_gnosis.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/proposals/proposal_04_service_registry_l2_change_manager_gnosis.js b/scripts/proposals/proposal_04_service_registry_l2_change_manager_gnosis.js index eb18637d..f9f6020e 100644 --- a/scripts/proposals/proposal_04_service_registry_l2_change_manager_gnosis.js +++ b/scripts/proposals/proposal_04_service_registry_l2_change_manager_gnosis.js @@ -72,7 +72,7 @@ async function main() { const targets = [AMBProxyAddress]; const values = [0]; const callDatas = [timelockPayload]; - const description = "Change Drainer in ServiceRegistryL2 on gnosis"; + const description = "Change Manager in ServiceRegistryL2 on gnosis"; // Proposal details console.log("targets:", targets);