From 54a6f32c383334c75cdb2936cae88d1f722505af Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 11 Sep 2024 16:41:34 +0100 Subject: [PATCH 01/72] Allow funds to be sent directly to domains --- docs/interfaces/icolony.md | 12 -- docs/interfaces/icolonynetwork.md | 180 ----------------------- docs/interfaces/imetacolony.md | 12 -- test/contracts-network/colony-funding.js | 91 ++++++++++++ 4 files changed, 91 insertions(+), 204 deletions(-) diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index 033da0286e..a94bdf69af 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -1584,18 +1584,6 @@ Set new colony funding role. Can be called by root role or architecture role. |_setTo|bool|The state of the role permission (true assign the permission, false revokes it) -### ▸ `setOwner(address owner_)` - -Set the owner of the contract - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|owner_|address|The new owner of the contract - - ### ▸ `setRecoveryRole(address _user)` Set new colony recovery role. Can be called by root. diff --git a/docs/interfaces/icolonynetwork.md b/docs/interfaces/icolonynetwork.md index 18dcd8f0e8..2e230638b5 100644 --- a/docs/interfaces/icolonynetwork.md +++ b/docs/interfaces/icolonynetwork.md @@ -36,48 +36,6 @@ Add a new extension resolver to the Extensions repository. |_resolver|address|The deployed resolver containing the extension contract logic -### ▸ `addPendingReputationUpdate(uint256 _chainId, address _colony)` - -Try to emit the next reputation update that was bridged but previously failed, if any - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_chainId|uint256|The chainId the update was bridged from -|_colony|address|The colony being queried - - -### ▸ `addPendingSkill(uint256 _skillId)` - -Called to add a bridged skill that wasn't next when it was bridged, but now is - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_skillId|uint256|The skillId of the skill being bridged - - -### ▸ `addReputationUpdateLogFromBridge(address _colony, address _user, int _amount, uint _skillId, uint256 _updateNumber)` - -Adds a reputation update entry to log. - -*Note: Errors if it is called by anyone but a known bridge* - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_colony|address|The colony the reputation is being awarded in -|_user|address|The address of the user for the reputation update -|_amount|int|The amount of reputation change for the update, this can be a negative as well as a positive value -|_skillId|uint|The skill for the reputation update -|_updateNumber|uint256|The counter used for ordering bridged updates - - ### ▸ `addSkill(uint256 _parentSkillId):uint256 _skillId` Adds a new skill to the domain or local skills tree, under skill `_parentSkillId`. Any colony is allowed to add a local skill and which is associated with a new domain via `IColony.addDomain`. @@ -96,19 +54,6 @@ Adds a new skill to the domain or local skills tree, under skill `_parentSkillId |---|---|---| |_skillId|uint256|Id of the added skill -### ▸ `addSkillFromBridge(uint256 _parentSkillId, uint256 _skillCount)` - -Function called by bridge transactions to add a new skill - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_parentSkillId|uint256|The parent id of the new skill -|_skillCount|uint256|The number of the new skill being created - - ### ▸ `addr(bytes32 _node):address _address` Returns the address the supplied node resolves do, if we are the resolver. @@ -148,43 +93,6 @@ Indicate approval to exit recovery mode. Can only be called by user with recover -### ▸ `bridgeCurrentRootHash(uint256 chainId)` - -Initiate a cross-chain update of the current reputation state - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|chainId|uint256|The chainid we want to bridge to - - -### ▸ `bridgePendingReputationUpdate(address _colony, uint256 _updateNumber)` - -Try to bridge a reputation update that (previously) failed - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_colony|address|The colony being queried -|_updateNumber|uint256|the emission index to bridge - - -### ▸ `bridgeSkillIfNotMiningChain(uint256 skillId)` - -Called to re-send the bridging transaction for a skill to the - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|skillId|uint256|The skillId we're bridging the creation of - - ### ▸ `burnUnneededRewards(uint256 _amount)` Used to burn tokens that are not needed to pay out rewards (because not every possible defence was made for all submissions) @@ -467,42 +375,6 @@ Exit recovery mode, can be called by anyone if enough whitelist approvals are gi -### ▸ `getBridgedReputationUpdateCount(uint256 _chainId, address _colony):uint256 bridgedReputationCount` - -Get the (currently bridged) reputation update count of a chain - -*Note: On the non-mining chain, this tracks the number of reputation updates that have either been bridged, or attempted to be bridged (and failed, and are now pending bridging). On the mining chain, it tracks how many have been successfully bridged and added to the log.* - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_chainId|uint256|The chainid of the chain -|_colony|address|The colony being queried - -**Return Parameters** - -|Name|Type|Description| -|---|---|---| -|bridgedReputationCount|uint256|The bridge reputation count of the corresponding chain - -### ▸ `getBridgedSkillCounts(uint256 _chainId):uint256 skillCount` - -Get the (currently bridged) skill count of another chain - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_chainId|uint256|The chainid of foreign chain - -**Return Parameters** - -|Name|Type|Description| -|---|---|---| -|skillCount|uint256|The skillCount of the corresponding chain - ### ▸ `getChildSkillId(uint256 _skillId, uint256 _childSkillIndex):uint256 _childSkillId` Get the id of the child skill at index `_childSkillIndex` for skill with Id `_skillId`. @@ -816,43 +688,6 @@ Get a token's status in the payout whitelist |---|---|---| |_status|bool|Will be `true` if token is whitelisted -### ▸ `getPendingReputationUpdate(uint256 _chainId, address _colony, uint256 _updateNumber):PendingReputationUpdate update` - -Get the details of a reputation update that was bridged but was not added to the log because it was bridged out of order - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_chainId|uint256|The chainId the update was bridged from -|_colony|address|The colony being queried -|_updateNumber|uint256|the updatenumber being queries - -**Return Parameters** - -|Name|Type|Description| -|---|---|---| -|update|PendingReputationUpdate|The update stored for that chain/colony/updateNumber - -### ▸ `getPendingSkillAddition(uint256 _chainId, uint256 _skillCount):uint256 parentId` - -Called to get the information about a skill that has been bridged out of order - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_chainId|uint256|The chainId we're bridging from -|_skillCount|uint256|The skill count - -**Return Parameters** - -|Name|Type|Description| -|---|---|---| -|parentId|uint256|The parent id of the skill being added - ### ▸ `getProfileDBAddress(bytes32 _node):string _orbitdb` Retrieve the orbitdb address corresponding to a registered account. @@ -1373,21 +1208,6 @@ Set a new Reputation root hash and starts a new mining cycle. Can only be called |_stakers|address[]|Array of users who submitted or backed the hash, being accepted here as the new reputation root hash -### ▸ `setReputationRootHashFromBridge(bytes32 newHash, uint256 newNLeaves, uint256 nonce)` - -Update the reputation on a foreign chain from the mining chain - -*Note: Should error if called by anyone other than the known bridge from the mining chain* - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|newHash|bytes32|The new root hash -|newNLeaves|uint256|The new nLeaves in the root hash -|nonce|uint256|The nonce to ensure these txs can't be replayed - - ### ▸ `setStorageSlotRecovery(uint256 _slot, bytes32 _value)` Update value of arbitrary storage variable. Can only be called by user with recovery role. diff --git a/docs/interfaces/imetacolony.md b/docs/interfaces/imetacolony.md index cab1e6ea16..bf86ee4989 100644 --- a/docs/interfaces/imetacolony.md +++ b/docs/interfaces/imetacolony.md @@ -1647,18 +1647,6 @@ Set the Colony Network fee inverse amount. |_feeInverse|uint256|Nonzero amount for the fee inverse -### ▸ `setOwner(address owner_)` - -Set the owner of the contract - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|owner_|address|The new owner of the contract - - ### ▸ `setPayoutWhitelist(address _token, bool _status)` Set a token's status in the payout whitelist on the Colony Network diff --git a/test/contracts-network/colony-funding.js b/test/contracts-network/colony-funding.js index 9dbc25f833..5d1880cdd7 100755 --- a/test/contracts-network/colony-funding.js +++ b/test/contracts-network/colony-funding.js @@ -18,6 +18,7 @@ const { ADDRESS_ZERO, } = require("../../helpers/constants"); +<<<<<<< HEAD const { fundColonyWithTokens, setupRandomColony, @@ -27,6 +28,14 @@ const { } = require("../../helpers/test-data-generator"); const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit, expectEvent, rolesToBytes32 } = require("../../helpers/test-helper"); const { setupDomainTokenReceiverResolver } = require("../../helpers/upgradable-contracts"); +||||||| parent of d7aa9686f (Allow funds to be sent directly to domains) +const { fundColonyWithTokens, setupRandomColony, makeExpenditure, setupFundedExpenditure } = require("../../helpers/test-data-generator"); +const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit } = require("../../helpers/test-helper"); +======= +const { fundColonyWithTokens, setupRandomColony, makeExpenditure, setupFundedExpenditure } = require("../../helpers/test-data-generator"); +const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit } = require("../../helpers/test-helper"); +const { setupDomainTokenReceiverResolver } = require("../../helpers/upgradable-contracts"); +>>>>>>> d7aa9686f (Allow funds to be sent directly to domains) const { expect } = chai; chai.use(bnChai(web3.utils.BN)); @@ -35,9 +44,15 @@ const EtherRouter = artifacts.require("EtherRouter"); const IColonyNetwork = artifacts.require("IColonyNetwork"); const IMetaColony = artifacts.require("IMetaColony"); const Token = artifacts.require("Token"); +<<<<<<< HEAD const Resolver = artifacts.require("Resolver"); const DomainTokenReceiver = artifacts.require("DomainTokenReceiver"); const TokenAuthority = artifacts.require("contracts/common/TokenAuthority.sol:TokenAuthority"); +||||||| parent of d7aa9686f (Allow funds to be sent directly to domains) +======= +const Resolver = artifacts.require("Resolver"); +const DomainTokenReceiver = artifacts.require("DomainTokenReceiver"); +>>>>>>> d7aa9686f (Allow funds to be sent directly to domains) contract("Colony Funding", (accounts) => { const MANAGER = accounts[0]; @@ -563,6 +578,7 @@ contract("Colony Funding", (accounts) => { expect(colonyRewardPotBalance).to.eq.BN(3); expect(nonRewardPotsTotal).to.eq.BN(297); }); +<<<<<<< HEAD it("should allow native coins to be directly sent to a domain", async () => { // Get address for domain 2 @@ -862,5 +878,80 @@ contract("Colony Funding", (accounts) => { expect(resolverAfter).to.not.equal(resolver); expect(resolverAfter).to.equal(newResolver.address); }); +||||||| parent of d7aa9686f (Allow funds to be sent directly to domains) +======= + + it("should allow native coins to be directly sent to a domain", async () => { + // Get address for domain 2 + await colony.addDomain(1, UINT256_MAX, 1); + const receiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); + + // Send 100 wei + await web3.eth.sendTransaction({ from: MANAGER, to: receiverAddress, value: 100, gas: 1000000 }); + + const domain = await colony.getDomain(2); + const domainPotBalanceBefore = await colony.getFundingPotBalance(domain.fundingPotId, ethers.constants.AddressZero); + + // Claim the funds + await colony.claimDomainFunds(ethers.constants.AddressZero, 2); + + const domainPotBalanceAfter = await colony.getFundingPotBalance(domain.fundingPotId, ethers.constants.AddressZero); + + // Check the balance of the domain + expect(domainPotBalanceAfter.sub(domainPotBalanceBefore)).to.eq.BN(100); + }); + + it("should allow a token to be directly sent to a domain", async () => { + // Get address for domain 2 + await colony.addDomain(1, UINT256_MAX, 1); + const receiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); + + // Send 100 wei + await otherToken.mint(receiverAddress, 100); + + const domain = await colony.getDomain(2); + const domainPotBalanceBefore = await colony.getFundingPotBalance(domain.fundingPotId, otherToken.address); + + // Claim the funds + await colony.claimDomainFunds(otherToken.address, 2); + + const domainPotBalanceAfter = await colony.getFundingPotBalance(domain.fundingPotId, otherToken.address); + + // Check the balance of the domain + expect(domainPotBalanceAfter.sub(domainPotBalanceBefore)).to.eq.BN(100); + }); + + it("should not be able to claim funds for a domain that does not exist", async () => { + await checkErrorRevert(colony.claimDomainFunds(ethers.constants.AddressZero, 2), "colony-funding-domain-does-not-exist"); + }); + + it("only a colony can call idempotentDeployDomainTokenReceiver on Network", async () => { + await checkErrorRevert(colonyNetwork.idempotentDeployDomainTokenReceiver(2), "colony-caller-must-be-colony"); + }); + + it("If the receiver resolver is updated, then the resolver is updated at the next claim", async () => { + await colony.addDomain(1, UINT256_MAX, 1); + const receiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); + // Send 100 wei + await otherToken.mint(receiverAddress, 100); + await colony.claimDomainFunds(otherToken.address, 2); + + const receiverAsEtherRouter = await EtherRouter.at(receiverAddress); + const resolver = await receiverAsEtherRouter.resolver(); + + // Update the resolver + const newResolver = await Resolver.new(); + const domainTokenReceiver = await DomainTokenReceiver.new(); + + await setupDomainTokenReceiverResolver(colonyNetwork, domainTokenReceiver, newResolver); + + await otherToken.mint(receiverAddress, 50); + await colony.claimDomainFunds(otherToken.address, 2); + + const resolverAfter = await receiverAsEtherRouter.resolver(); + expect(resolverAfter).to.not.equal(resolver); + expect(resolverAfter).to.equal(newResolver.address); + }); +>>>>>>> d7aa9686f (Allow funds to be sent directly to domains) }); }); From 03ff9291fb005197f466fb96bbae64f9f17e922b Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Tue, 21 May 2024 10:41:02 +0100 Subject: [PATCH 02/72] Wormhole Relayer WIP --- package.json | 11 +- .../package-utils/ExtendedNonceManager.js | 8 +- pnpm-lock.yaml | 513 ++++-------------- 3 files changed, 130 insertions(+), 402 deletions(-) diff --git a/package.json b/package.json index 6a5b5e25b0..5833a5c976 100644 --- a/package.json +++ b/package.json @@ -166,5 +166,14 @@ "packages/metatransaction-broadcaster", "packages/package-utils", "packages/reputation-miner" - ] + ], + "dependencies": { + "@certusone/wormhole-sdk": "^0.10.15", + "@grpc/grpc-js": "^1.10.8", + "@grpc/proto-loader": "^0.7.13", + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@types/node": "^20.11.0", + "express-ws": "^5.0.2", + "ws": "^8.17.0" + } } diff --git a/packages/package-utils/ExtendedNonceManager.js b/packages/package-utils/ExtendedNonceManager.js index fb0e2c9ee5..cb5d8091c7 100644 --- a/packages/package-utils/ExtendedNonceManager.js +++ b/packages/package-utils/ExtendedNonceManager.js @@ -24,7 +24,7 @@ class ExtendedNonceManager extends NonceManager { async sendTransaction(transactionRequest) { // What nonce are we going to attach to this? // Definitely not any we've sent and are pending - const pendingNonces = Object.keys(this.signedTransactions).map((txhash) => this.signedTransactions[txhash].nonce); + // const pendingNonces = Object.keys(this.signedTransactions).map((txhash) => this.signedTransactions[txhash].nonce); // At least whatever the endpoint says, or whatever we've already sent if higher let nonce = await this.signer.getTransactionCount(); @@ -32,7 +32,11 @@ class ExtendedNonceManager extends NonceManager { // Note the order we did the above two lines in - if a tx is mined between these two lines, // and got removed by the `on block` handler above, by doing it in this order we won't be tripped up // And we'll skip any nonces we've already used - while (pendingNonces.includes(nonce)) { + while ( + Object.keys(this.signedTransactions) + .map((txhash) => this.signedTransactions[txhash].nonce) + .includes(nonce) + ) { nonce += 1; } transactionRequest.nonce = nonce; // eslint-disable-line no-param-reassign diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91166f8f80..e9b06092cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,29 +15,39 @@ patchedDependencies: importers: .: + dependencies: + '@certusone/wormhole-sdk': + specifier: ^0.10.15 + version: 0.10.18(fastestsmallesttextencoderdecoder@1.0.22)(google-protobuf@3.21.4) + '@grpc/grpc-js': + specifier: ^1.10.8 + version: 1.12.5 + '@grpc/proto-loader': + specifier: ^0.7.13 + version: 0.7.13 + '@nomicfoundation/hardhat-ethers': + specifier: ^3.0.5 + version: 3.0.8(ethers@5.7.2)(hardhat@2.22.18) + '@types/node': + specifier: ^20.11.0 + version: 20.17.16 + express-ws: + specifier: ^5.0.2 + version: 5.0.2(express@4.21.2) + ws: + specifier: ^8.17.0 + version: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: fsevents: specifier: ^2.3.3 version: 2.3.3 devDependencies: - '@certusone/wormhole-sdk': - specifier: ^0.10.18 - version: 0.10.18(fastestsmallesttextencoderdecoder@1.0.22)(google-protobuf@3.21.4)(typescript@5.7.3) '@codechecks/client': specifier: ^0.1.12 version: 0.1.12(typescript@5.7.3) '@colony/eslint-config-colony': specifier: 10.0.0 version: 10.0.0(eslint-config-airbnb-base@15.0.0)(eslint-config-prettier@8.10.0)(eslint-plugin-eslint-comments@3.2.0)(eslint-plugin-import@2.31.0)(eslint-plugin-prettier@4.2.1)(eslint@8.57.1)(prettier@2.8.8) - '@grpc/grpc-js': - specifier: ^1.12.5 - version: 1.12.5 - '@grpc/proto-loader': - specifier: ^0.7.13 - version: 0.7.13 - '@nomicfoundation/hardhat-ethers': - specifier: ^3.0.8 - version: 3.0.8(ethers@5.7.2)(hardhat@2.22.18) '@nomicfoundation/hardhat-network-helpers': specifier: ^1.0.12 version: 1.0.12(hardhat@2.22.18) @@ -59,9 +69,6 @@ importers: '@truffle/contract': specifier: ^4.6.31 version: 4.6.31 - '@types/node': - specifier: ^20.17.16 - version: 20.17.16 '@typescript-eslint/eslint-plugin': specifier: ^8.21.0 version: 8.21.0(@typescript-eslint/parser@8.21.0)(eslint@8.57.1)(typescript@5.7.3) @@ -149,9 +156,6 @@ importers: express: specifier: ^4.21.2 version: 4.21.2 - express-ws: - specifier: ^5.0.2 - version: 5.0.2(express@4.21.2) find-in-files: specifier: ^0.5.0 version: 0.5.0 @@ -227,9 +231,6 @@ importers: web3-utils: specifier: ^1.10.4 version: 1.10.4 - ws: - specifier: ^8.18.0 - version: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) lib/safe-contracts: devDependencies: @@ -514,7 +515,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/highlight': 7.24.7 - picocolors: 1.0.1 + picocolors: 1.1.1 dev: true /@babel/helper-validator-identifier@7.24.7: @@ -780,7 +781,7 @@ packages: - utf-8-validate dev: false - /@certusone/wormhole-sdk@0.10.18(fastestsmallesttextencoderdecoder@1.0.22)(google-protobuf@3.21.4)(typescript@5.7.3): + /@certusone/wormhole-sdk@0.10.18(fastestsmallesttextencoderdecoder@1.0.22)(google-protobuf@3.21.4): resolution: {integrity: sha512-VuN4AGB018ELkzTT/jN+yWgE6TWqXsHilxxCVWqGctzow2hKSFd8ADUhxhHigies436rS0vPvrgXi6m0J1+Ecw==} deprecated: 'Please use the new Wormhole TypeScript SDK instead: @wormhole-foundation/sdk' dependencies: @@ -789,7 +790,7 @@ packages: '@coral-xyz/borsh': 0.2.6(@solana/web3.js@1.98.0) '@mysten/sui.js': 0.32.2 '@project-serum/anchor': 0.25.0 - '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.98.0)(fastestsmallesttextencoderdecoder@1.0.22) '@solana/web3.js': 1.98.0 '@terra-money/terra.js': 3.1.9 '@xpla/xpla.js': 0.2.3 @@ -817,9 +818,7 @@ packages: - react - react-dom - subscriptions-transport-ws - - typescript - utf-8-validate - dev: true /@certusone/wormhole-spydk@0.0.1: resolution: {integrity: sha512-iBQoY3UnmGoWHcbn0FypA6hKsANhdHKi03UN0GPoDAeMY12j8ly+7r462TfLl5f4hOJVQd3UZ2qviohEmdicmg==} @@ -922,7 +921,6 @@ packages: '@solana/web3.js': 1.98.0 bn.js: 5.2.1 buffer-layout: 1.2.2 - dev: true /@cosmjs/amino@0.28.13: resolution: {integrity: sha512-IHnH2zGwaY69qT4mVAavr/pfzx6YE+ud1NHJbvVePlbGiz68CXTi5LHR+K0lrKB5mQ7E+ZErWz2mw5U/x+V1wQ==} @@ -977,7 +975,7 @@ packages: '@cosmjs/utils': 0.28.13 '@noble/hashes': 1.4.0 bn.js: 5.2.1 - elliptic: 6.5.5 + elliptic: 6.6.1 libsodium-wrappers: 0.7.14 /@cosmjs/crypto@0.29.5: @@ -988,7 +986,7 @@ packages: '@cosmjs/utils': 0.29.5 '@noble/hashes': 1.4.0 bn.js: 5.2.1 - elliptic: 6.5.5 + elliptic: 6.6.1 libsodium-wrappers: 0.7.14 /@cosmjs/crypto@0.30.1: @@ -1223,7 +1221,7 @@ packages: '@cosmjs/socket': 0.28.13 '@cosmjs/stream': 0.28.13 '@cosmjs/utils': 0.28.13 - axios: 0.21.4(debug@4.3.5) + axios: 0.21.4 readonly-date: 1.0.0 xstream: 11.14.0 transitivePeerDependencies: @@ -1241,7 +1239,7 @@ packages: '@cosmjs/socket': 0.29.5 '@cosmjs/stream': 0.29.5 '@cosmjs/utils': 0.29.5 - axios: 0.21.4(debug@4.3.5) + axios: 0.21.4 readonly-date: 1.0.0 xstream: 11.14.0 transitivePeerDependencies: @@ -1260,7 +1258,7 @@ packages: '@cosmjs/socket': 0.30.1 '@cosmjs/stream': 0.30.1 '@cosmjs/utils': 0.30.1 - axios: 0.21.4(debug@4.3.5) + axios: 0.21.4 readonly-date: 1.0.0 xstream: 11.14.0 transitivePeerDependencies: @@ -2068,7 +2066,6 @@ packages: /@fastify/busboy@2.1.1: resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} - dev: true /@gar/promisify@1.1.3: resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} @@ -2248,7 +2245,7 @@ packages: dependencies: '@injectivelabs/exceptions': 1.14.40(google-protobuf@3.21.4) '@injectivelabs/ts-types': 1.14.40 - '@injectivelabs/utils': 1.10.12(google-protobuf@3.21.4) + '@injectivelabs/utils': 1.14.40(google-protobuf@3.21.4) link-module-alias: 1.2.0 shx: 0.3.4 transitivePeerDependencies: @@ -2285,11 +2282,11 @@ packages: '@injectivelabs/grpc-web-react-native-transport': 0.0.2(@injectivelabs/grpc-web@0.0.1) '@injectivelabs/indexer-proto-ts': 1.10.8-rc.4 '@injectivelabs/mito-proto-ts': 1.0.9 - '@injectivelabs/networks': 1.10.12(google-protobuf@3.21.4) + '@injectivelabs/networks': 1.14.40(google-protobuf@3.21.4) '@injectivelabs/test-utils': 1.14.40(google-protobuf@3.21.4) '@injectivelabs/token-metadata': 1.14.11(google-protobuf@3.21.4) '@injectivelabs/ts-types': 1.14.40 - '@injectivelabs/utils': 1.10.12(google-protobuf@3.21.4) + '@injectivelabs/utils': 1.14.40(google-protobuf@3.21.4) '@metamask/eth-sig-util': 4.0.1 axios: 0.27.2 bech32: 2.0.0 @@ -2371,7 +2368,7 @@ packages: dependencies: '@injectivelabs/exceptions': 1.14.40(google-protobuf@3.21.4) '@injectivelabs/ts-types': 1.14.40 - axios: 0.21.4(debug@4.3.5) + axios: 0.21.4 bignumber.js: 9.1.2 http-status-codes: 2.3.0 link-module-alias: 1.2.0 @@ -2713,7 +2710,6 @@ packages: /@noble/hashes@1.2.0: resolution: {integrity: sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==} - dev: true /@noble/hashes@1.4.0: resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} @@ -2729,7 +2725,6 @@ packages: /@noble/secp256k1@1.7.1: resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} - dev: true /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -2765,7 +2760,6 @@ packages: /@nomicfoundation/edr-darwin-arm64@0.7.0: resolution: {integrity: sha512-vAH20oh4GaSB/iQFTRcoO8jLc0CLd9XuLY9I7vtcqZWAiM4U1J4Y8cu67PWmtxbvUQOqXR7S6FtAr8/AlWm14g==} engines: {node: '>= 18'} - dev: true /@nomicfoundation/edr-darwin-x64@0.4.1: resolution: {integrity: sha512-N1MfJqEX5ixaXlyyrHnaYxzwIT27Nc/jUgLI7ts4/9kRvPTvyZRYmXS1ciKhmUFr/WvFckTCix2RJbZoGGtX7g==} @@ -2775,7 +2769,6 @@ packages: /@nomicfoundation/edr-darwin-x64@0.7.0: resolution: {integrity: sha512-WHDdIrPvLlgXQr2eKypBM5xOZAwdxhDAEQIvEMQL8tEEm2qYW2bliUlssBPrs8E3bdivFbe1HizImslMAfU3+g==} engines: {node: '>= 18'} - dev: true /@nomicfoundation/edr-linux-arm64-gnu@0.4.1: resolution: {integrity: sha512-bSPOfmcFjJwDgWOV5kgZHeqg2OWu1cINrHSGjig0aVHehjcoX4Sgayrj6fyAxcOV5NQKA6WcyTFll6NrCxzWRA==} @@ -2785,7 +2778,6 @@ packages: /@nomicfoundation/edr-linux-arm64-gnu@0.7.0: resolution: {integrity: sha512-WXpJB54ukz1no7gxCPXVEw9pgl/9UZ/WO3l1ctyv/T7vOygjqA4SUd6kppTs6MNXAuTiisPtvJ/fmvHiMBLrsw==} engines: {node: '>= 18'} - dev: true /@nomicfoundation/edr-linux-arm64-musl@0.4.1: resolution: {integrity: sha512-F/+DgOdeBFQDrk+SX4aFffJFBgJfd75ZtE2mjcWNAh/qWiS7NfUxdQX/5OvNo/H6EY4a+3bZH6Bgzqg4mEWvMw==} @@ -2795,7 +2787,6 @@ packages: /@nomicfoundation/edr-linux-arm64-musl@0.7.0: resolution: {integrity: sha512-1iZYOcEgc+zJI7JQrlAFziuy9sBz1WgnIx3HIIu0J7lBRZ/AXeHHgATb+4InqxtEx9O3W8A0s7f11SyFqJL4Aw==} engines: {node: '>= 18'} - dev: true /@nomicfoundation/edr-linux-x64-gnu@0.4.1: resolution: {integrity: sha512-POHhTWczIXCPhzKtY0Vt/l+VCqqCx5gNR5ErwSrNnLz/arfQobZFAU+nc61BX3Jch82TW8b3AbfGI73Kh7gO0w==} @@ -2805,7 +2796,6 @@ packages: /@nomicfoundation/edr-linux-x64-gnu@0.7.0: resolution: {integrity: sha512-wSjC94WcR5MM8sg9w3OsAmT6+bbmChJw6uJKoXR3qscps/jdhjzJWzfgT0XGRq3XMUfimyafW2RWOyfX3ouhrQ==} engines: {node: '>= 18'} - dev: true /@nomicfoundation/edr-linux-x64-musl@0.4.1: resolution: {integrity: sha512-uu8oNp4Ozg3H1x1We0FF+rwXfFiAvsOm5GQ+OBx9YYOXnfDPWqguQfGIkhrti9GD0iYhfQ/WOG5wvp0IzzgGSg==} @@ -2815,7 +2805,6 @@ packages: /@nomicfoundation/edr-linux-x64-musl@0.7.0: resolution: {integrity: sha512-Us22+AZ7wkG1mZwxqE4S4ZcuwkEA5VrUiBOJSvKHGOgy6vFvB/Euh5Lkp4GovwjrtiXuvyGO2UmtkzymZKDxZw==} engines: {node: '>= 18'} - dev: true /@nomicfoundation/edr-win32-x64-msvc@0.4.1: resolution: {integrity: sha512-PaZHFw455z89ZiKYNTnKu+/TiVZVRI+mRJsbRTe2N0VlYfUBS1o2gdXBM12oP1t198HR7xQwEPPAslTFxGBqHA==} @@ -2825,7 +2814,6 @@ packages: /@nomicfoundation/edr-win32-x64-msvc@0.7.0: resolution: {integrity: sha512-HAry0heTsWkzReVtjHwoIq3BgFCvXpVhJ5qPmTnegZGsr/KxqvMmHyDMifzKao4bycU8yrpTSyOiAJt27RWjzQ==} engines: {node: '>= 18'} - dev: true /@nomicfoundation/edr@0.4.1: resolution: {integrity: sha512-NgrMo2rI9r28uidumvd+K2/AJLdxtXsUlJr3hj/pM6S1FCd/HiWaLeLa/cjCVPcE2u1rYAa3W6UFxLCB7S5Dhw==} @@ -2851,7 +2839,6 @@ packages: '@nomicfoundation/edr-linux-x64-gnu': 0.7.0 '@nomicfoundation/edr-linux-x64-musl': 0.7.0 '@nomicfoundation/edr-win32-x64-msvc': 0.7.0 - dev: true /@nomicfoundation/ethereumjs-common@4.0.4: resolution: {integrity: sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg==} @@ -2859,13 +2846,11 @@ packages: '@nomicfoundation/ethereumjs-util': 9.0.4 transitivePeerDependencies: - c-kzg - dev: true /@nomicfoundation/ethereumjs-rlp@5.0.4: resolution: {integrity: sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw==} engines: {node: '>=18'} hasBin: true - dev: true /@nomicfoundation/ethereumjs-tx@5.0.4: resolution: {integrity: sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw==} @@ -2880,7 +2865,6 @@ packages: '@nomicfoundation/ethereumjs-rlp': 5.0.4 '@nomicfoundation/ethereumjs-util': 9.0.4 ethereum-cryptography: 0.1.3 - dev: true /@nomicfoundation/ethereumjs-util@9.0.4: resolution: {integrity: sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q==} @@ -2893,7 +2877,6 @@ packages: dependencies: '@nomicfoundation/ethereumjs-rlp': 5.0.4 ethereum-cryptography: 0.1.3 - dev: true /@nomicfoundation/hardhat-ethers@3.0.8(ethers@5.7.2)(hardhat@2.22.18): resolution: {integrity: sha512-zhOZ4hdRORls31DTOqg+GmEZM0ujly8GGIuRY7t7szEk2zW/arY1qDug/py8AEktT00v5K+b6RvbVog+va51IA==} @@ -2907,7 +2890,7 @@ packages: lodash.isequal: 4.5.0 transitivePeerDependencies: - supports-color - dev: true + dev: false /@nomicfoundation/hardhat-network-helpers@1.0.12(hardhat@2.22.18): resolution: {integrity: sha512-xTNQNI/9xkHvjmCJnJOTyqDSl8uq1rKb2WOVmixQxFtRd7Oa3ecO8zM0cyC2YmOK+jHB9WPZ+F/ijkHg1CoORA==} @@ -2922,49 +2905,42 @@ packages: resolution: {integrity: sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw==} engines: {node: '>= 12'} requiresBuild: true - dev: true optional: true /@nomicfoundation/solidity-analyzer-darwin-x64@0.1.2: resolution: {integrity: sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw==} engines: {node: '>= 12'} requiresBuild: true - dev: true optional: true /@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.2: resolution: {integrity: sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA==} engines: {node: '>= 12'} requiresBuild: true - dev: true optional: true /@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.2: resolution: {integrity: sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA==} engines: {node: '>= 12'} requiresBuild: true - dev: true optional: true /@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.2: resolution: {integrity: sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g==} engines: {node: '>= 12'} requiresBuild: true - dev: true optional: true /@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.2: resolution: {integrity: sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg==} engines: {node: '>= 12'} requiresBuild: true - dev: true optional: true /@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.2: resolution: {integrity: sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA==} engines: {node: '>= 12'} requiresBuild: true - dev: true optional: true /@nomicfoundation/solidity-analyzer@0.1.2: @@ -2978,7 +2954,6 @@ packages: '@nomicfoundation/solidity-analyzer-linux-x64-gnu': 0.1.2 '@nomicfoundation/solidity-analyzer-linux-x64-musl': 0.1.2 '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.2 - dev: true /@nomiclabs/hardhat-ethers@2.2.3(ethers@5.7.2)(hardhat@2.22.18): resolution: {integrity: sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==} @@ -3288,7 +3263,6 @@ packages: '@noble/hashes': 1.2.0 '@noble/secp256k1': 1.7.1 '@scure/base': 1.1.7 - dev: true /@scure/bip32@1.4.0: resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} @@ -3315,7 +3289,6 @@ packages: dependencies: '@noble/hashes': 1.2.0 '@scure/base': 1.1.7 - dev: true /@scure/bip39@1.3.0: resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} @@ -3381,7 +3354,6 @@ packages: '@sentry/types': 5.30.0 '@sentry/utils': 5.30.0 tslib: 1.14.1 - dev: true /@sentry/hub@5.30.0: resolution: {integrity: sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==} @@ -3390,7 +3362,6 @@ packages: '@sentry/types': 5.30.0 '@sentry/utils': 5.30.0 tslib: 1.14.1 - dev: true /@sentry/minimal@5.30.0: resolution: {integrity: sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==} @@ -3399,7 +3370,6 @@ packages: '@sentry/hub': 5.30.0 '@sentry/types': 5.30.0 tslib: 1.14.1 - dev: true /@sentry/node@5.30.0: resolution: {integrity: sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==} @@ -3416,7 +3386,6 @@ packages: tslib: 1.14.1 transitivePeerDependencies: - supports-color - dev: true /@sentry/tracing@5.30.0: resolution: {integrity: sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==} @@ -3427,12 +3396,10 @@ packages: '@sentry/types': 5.30.0 '@sentry/utils': 5.30.0 tslib: 1.14.1 - dev: true /@sentry/types@5.30.0: resolution: {integrity: sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==} engines: {node: '>=6'} - dev: true /@sentry/utils@5.30.0: resolution: {integrity: sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==} @@ -3440,7 +3407,6 @@ packages: dependencies: '@sentry/types': 5.30.0 tslib: 1.14.1 - dev: true /@sindresorhus/is@0.14.0: resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==} @@ -3475,15 +3441,6 @@ packages: resolution: {integrity: sha512-gLhCJXieSCrAU7acUJjbXl+IbGnqovvxQLlimztPoGgfLQ1wFYu+XJswrEVQqknZYK1pgxpxH3rZ+OKFs0ndQg==} dependencies: '@solana/errors': 2.0.0-preview.2 - dev: false - - /@solana/codecs-core@2.0.0-rc.1(typescript@5.7.3): - resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} - peerDependencies: - typescript: '>=5' - dependencies: - '@solana/errors': 2.0.0-rc.1(typescript@5.7.3) - typescript: 5.7.3 /@solana/codecs-data-structures@2.0.0-preview.2: resolution: {integrity: sha512-Xf5vIfromOZo94Q8HbR04TbgTwzigqrKII0GjYr21K7rb3nba4hUW2ir8kguY7HWFBcjHGlU5x3MevKBOLp3Zg==} @@ -3491,33 +3448,12 @@ packages: '@solana/codecs-core': 2.0.0-preview.2 '@solana/codecs-numbers': 2.0.0-preview.2 '@solana/errors': 2.0.0-preview.2 - dev: false - - /@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.7.3): - resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} - peerDependencies: - typescript: '>=5' - dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.7.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.7.3) - typescript: 5.7.3 /@solana/codecs-numbers@2.0.0-preview.2: resolution: {integrity: sha512-aLZnDTf43z4qOnpTcDsUVy1Ci9im1Md8thWipSWbE+WM9ojZAx528oAql+Cv8M8N+6ALKwgVRhPZkto6E59ARw==} dependencies: '@solana/codecs-core': 2.0.0-preview.2 '@solana/errors': 2.0.0-preview.2 - dev: false - - /@solana/codecs-numbers@2.0.0-rc.1(typescript@5.7.3): - resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} - peerDependencies: - typescript: '>=5' - dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.7.3) - typescript: 5.7.3 /@solana/codecs-strings@2.0.0-preview.2(fastestsmallesttextencoderdecoder@1.0.22): resolution: {integrity: sha512-EgBwY+lIaHHgMJIqVOGHfIfpdmmUDNoNO/GAUGeFPf+q0dF+DtwhJPEMShhzh64X2MeCZcmSO6Kinx0Bvmmz2g==} @@ -3528,19 +3464,6 @@ packages: '@solana/codecs-numbers': 2.0.0-preview.2 '@solana/errors': 2.0.0-preview.2 fastestsmallesttextencoderdecoder: 1.0.22 - dev: false - - /@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3): - resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} - peerDependencies: - fastestsmallesttextencoderdecoder: ^1.0.22 - typescript: '>=5' - dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.7.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.7.3) - fastestsmallesttextencoderdecoder: 1.0.22 - typescript: 5.7.3 /@solana/codecs@2.0.0-preview.2(fastestsmallesttextencoderdecoder@1.0.22): resolution: {integrity: sha512-4HHzCD5+pOSmSB71X6w9ptweV48Zj1Vqhe732+pcAQ2cMNnN0gMPMdDq7j3YwaZDZ7yrILVV/3+HTnfT77t2yA==} @@ -3552,21 +3475,6 @@ packages: '@solana/options': 2.0.0-preview.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - dev: false - - /@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3): - resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} - peerDependencies: - typescript: '>=5' - dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.3) - '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.7.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.7.3) - '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) - '@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) - typescript: 5.7.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder /@solana/errors@2.0.0-preview.2: resolution: {integrity: sha512-H2DZ1l3iYF5Rp5pPbJpmmtCauWeQXRJapkDg8epQ8BJ7cA2Ut/QEtC3CMmw/iMTcuS6uemFNLcWvlOfoQhvQuA==} @@ -3574,38 +3482,12 @@ packages: dependencies: chalk: 5.3.0 commander: 12.1.0 - dev: false - - /@solana/errors@2.0.0-rc.1(typescript@5.7.3): - resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} - hasBin: true - peerDependencies: - typescript: '>=5' - dependencies: - chalk: 5.4.1 - commander: 12.1.0 - typescript: 5.7.3 /@solana/options@2.0.0-preview.2: resolution: {integrity: sha512-FAHqEeH0cVsUOTzjl5OfUBw2cyT8d5Oekx4xcn5hn+NyPAfQJgM3CEThzgRD6Q/4mM5pVUnND3oK/Mt1RzSE/w==} dependencies: '@solana/codecs-core': 2.0.0-preview.2 '@solana/codecs-numbers': 2.0.0-preview.2 - dev: false - - /@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3): - resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} - peerDependencies: - typescript: '>=5' - dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.3) - '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.7.3) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.7.3) - '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) - '@solana/errors': 2.0.0-rc.1(typescript@5.7.3) - typescript: 5.7.3 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder /@solana/spl-token-metadata@0.1.4(@solana/web3.js@1.95.0)(fastestsmallesttextencoderdecoder@1.0.22): resolution: {integrity: sha512-N3gZ8DlW6NWDV28+vCCDJoTqaCZiF/jDUnk3o8GRkAFzHObiR60Bs1gXHBa8zCPdvOwiG6Z3dg5pg7+RW6XNsQ==} @@ -3618,32 +3500,18 @@ packages: '@solana/web3.js': 1.95.0 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - dev: false - - /@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.95.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3): - resolution: {integrity: sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.95.3 - dependencies: - '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) - '@solana/web3.js': 1.95.0 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - typescript - /@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3): - resolution: {integrity: sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA==} + /@solana/spl-token-metadata@0.1.4(@solana/web3.js@1.98.0)(fastestsmallesttextencoderdecoder@1.0.22): + resolution: {integrity: sha512-N3gZ8DlW6NWDV28+vCCDJoTqaCZiF/jDUnk3o8GRkAFzHObiR60Bs1gXHBa8zCPdvOwiG6Z3dg5pg7+RW6XNsQ==} engines: {node: '>=16'} peerDependencies: - '@solana/web3.js': ^1.95.3 + '@solana/web3.js': ^1.91.6 dependencies: - '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) + '@solana/codecs': 2.0.0-preview.2(fastestsmallesttextencoderdecoder@1.0.22) + '@solana/spl-type-length-value': 0.1.0 '@solana/web3.js': 1.98.0 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - - typescript - dev: true /@solana/spl-token@0.3.11(@solana/web3.js@1.95.0)(fastestsmallesttextencoderdecoder@1.0.22): resolution: {integrity: sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==} @@ -3661,27 +3529,8 @@ packages: - encoding - fastestsmallesttextencoderdecoder - utf-8-validate - dev: false - - /@solana/spl-token@0.3.11(@solana/web3.js@1.95.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3): - resolution: {integrity: sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==} - engines: {node: '>=16'} - peerDependencies: - '@solana/web3.js': ^1.88.0 - dependencies: - '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0 - '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.95.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) - '@solana/web3.js': 1.95.0 - buffer: 6.0.3 - transitivePeerDependencies: - - bufferutil - - encoding - - fastestsmallesttextencoderdecoder - - typescript - - utf-8-validate - /@solana/spl-token@0.3.11(@solana/web3.js@1.98.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3): + /@solana/spl-token@0.3.11(@solana/web3.js@1.98.0)(fastestsmallesttextencoderdecoder@1.0.22): resolution: {integrity: sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==} engines: {node: '>=16'} peerDependencies: @@ -3689,23 +3538,20 @@ packages: dependencies: '@solana/buffer-layout': 4.0.1 '@solana/buffer-layout-utils': 0.2.0 - '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) + '@solana/spl-token-metadata': 0.1.4(@solana/web3.js@1.98.0)(fastestsmallesttextencoderdecoder@1.0.22) '@solana/web3.js': 1.98.0 buffer: 6.0.3 transitivePeerDependencies: - bufferutil - encoding - fastestsmallesttextencoderdecoder - - typescript - utf-8-validate - dev: true /@solana/spl-type-length-value@0.1.0: resolution: {integrity: sha512-JBMGB0oR4lPttOZ5XiUGyvylwLQjt1CPJa6qQ5oM+MBCndfjz2TKKkw0eATlLLcYmq1jBVsNlJ2cD6ns2GR7lA==} engines: {node: '>=16'} dependencies: buffer: 6.0.3 - dev: false /@solana/web3.js@1.95.0: resolution: {integrity: sha512-iHwJ/HcWrF9qbnI1ctwI1UXHJ0vZXRpnt+lI5UcQIk8WvJNuQ5gV06icxzM6B7ojUES85Q1/FM4jZ49UQ8yZZQ==} @@ -4049,20 +3895,20 @@ packages: /@types/bn.js@5.1.5: resolution: {integrity: sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==} dependencies: - '@types/node': 14.18.63 + '@types/node': 20.17.16 dev: true /@types/bn.js@5.1.6: resolution: {integrity: sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==} dependencies: - '@types/node': 14.18.63 + '@types/node': 20.17.16 /@types/cacheable-request@6.0.3: resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} dependencies: '@types/http-cache-semantics': 4.0.4 '@types/keyv': 3.1.4 - '@types/node': 14.18.63 + '@types/node': 20.17.16 '@types/responselike': 1.0.3 dev: true @@ -4095,7 +3941,7 @@ packages: resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} dependencies: '@types/minimatch': 5.1.2 - '@types/node': 14.18.63 + '@types/node': 20.17.16 dev: true /@types/http-cache-semantics@4.0.4: @@ -4113,7 +3959,7 @@ packages: /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: - '@types/node': 14.18.63 + '@types/node': 20.17.16 dev: true /@types/lodash.values@4.3.9: @@ -4141,7 +3987,7 @@ packages: /@types/mkdirp@0.5.2: resolution: {integrity: sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==} dependencies: - '@types/node': 14.18.63 + '@types/node': 20.17.16 dev: true /@types/mocha@8.2.3: @@ -4151,7 +3997,7 @@ packages: /@types/node-fetch@2.6.11: resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} dependencies: - '@types/node': 14.18.63 + '@types/node': 20.17.16 form-data: 4.0.0 dev: true @@ -4167,6 +4013,7 @@ packages: /@types/node@14.18.63: resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} + dev: true /@types/node@18.19.74: resolution: {integrity: sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==} @@ -4178,11 +4025,6 @@ packages: dependencies: undici-types: 6.19.8 - /@types/node@22.10.10: - resolution: {integrity: sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==} - dependencies: - undici-types: 6.20.0 - /@types/node@8.10.66: resolution: {integrity: sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==} dev: true @@ -4190,7 +4032,7 @@ packages: /@types/pbkdf2@3.1.2: resolution: {integrity: sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==} dependencies: - '@types/node': 14.18.63 + '@types/node': 20.17.16 /@types/prettier@2.7.3: resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} @@ -4207,19 +4049,19 @@ packages: /@types/resolve@0.0.8: resolution: {integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==} dependencies: - '@types/node': 14.18.63 + '@types/node': 20.17.16 dev: true /@types/responselike@1.0.3: resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} dependencies: - '@types/node': 14.18.63 + '@types/node': 20.17.16 dev: true /@types/secp256k1@4.0.6: resolution: {integrity: sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==} dependencies: - '@types/node': 14.18.63 + '@types/node': 20.17.16 /@types/seedrandom@3.0.1: resolution: {integrity: sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw==} @@ -4255,7 +4097,7 @@ packages: /@types/ws@8.5.10: resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} dependencies: - '@types/node': 22.10.10 + '@types/node': 20.17.16 /@types/ws@8.5.14: resolution: {integrity: sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==} @@ -4664,7 +4506,7 @@ packages: dependencies: '@bull-board/api': 5.23.0(@bull-board/ui@5.23.0) '@bull-board/koa': 5.23.0 - '@certusone/wormhole-sdk': 0.10.18(fastestsmallesttextencoderdecoder@1.0.22)(google-protobuf@3.21.4)(typescript@5.7.3) + '@certusone/wormhole-sdk': 0.10.18(fastestsmallesttextencoderdecoder@1.0.22)(google-protobuf@3.21.4) '@certusone/wormhole-spydk': 0.0.1 '@datastructures-js/queue': 4.2.3 '@improbable-eng/grpc-web-node-http-transport': 0.15.0(@improbable-eng/grpc-web@0.15.0) @@ -4757,7 +4599,7 @@ packages: engines: {node: '>=8'} requiresBuild: true dependencies: - tslib: 2.6.3 + tslib: 2.8.1 optional: true /@wry/context@0.7.4: @@ -4765,7 +4607,7 @@ packages: engines: {node: '>=8'} requiresBuild: true dependencies: - tslib: 2.6.3 + tslib: 2.8.1 optional: true /@wry/equality@0.5.7: @@ -4773,7 +4615,7 @@ packages: engines: {node: '>=8'} requiresBuild: true dependencies: - tslib: 2.6.3 + tslib: 2.8.1 optional: true /@wry/trie@0.5.0: @@ -4781,7 +4623,7 @@ packages: engines: {node: '>=8'} requiresBuild: true dependencies: - tslib: 2.6.3 + tslib: 2.8.1 optional: true /@xlabs-xyz/wallet-monitor@0.2.16(@types/node@20.17.16)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3): @@ -4789,7 +4631,7 @@ packages: dependencies: '@ethersproject/bignumber': 5.7.0 '@mysten/sui.js': 0.32.2 - '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0)(fastestsmallesttextencoderdecoder@1.0.22) '@solana/web3.js': 1.95.0 bluebird: 3.7.2 bs58: 5.0.0 @@ -4996,7 +4838,6 @@ packages: /adm-zip@0.4.16: resolution: {integrity: sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==} engines: {node: '>=0.3.0'} - dev: true /aes-js@3.0.0: resolution: {integrity: sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==} @@ -5065,15 +4906,6 @@ packages: uri-js: 4.4.1 dev: true - /ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.0.6 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - dev: true - /algo-msgpack-with-bigint@2.1.1: resolution: {integrity: sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ==} engines: {node: '>= 10'} @@ -5091,7 +4923,6 @@ packages: json-bigint: 1.0.0 tweetnacl: 1.0.3 vlq: 2.0.4 - dev: true /algosdk@2.8.0: resolution: {integrity: sha512-yjDH/VbQ689hxmn4PFbfXQrn4VZbYCGGzI/RD4ccA0yr55qT/TyAtR/dnq4XXX2pwCKNHbxOfantaJ5kTiwuMQ==} @@ -5119,12 +4950,10 @@ packages: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} dependencies: string-width: 4.2.3 - dev: true /ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} - dev: true /ansi-escapes@3.2.0: resolution: {integrity: sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==} @@ -5136,7 +4965,6 @@ packages: engines: {node: '>=8'} dependencies: type-fest: 0.21.3 - dev: true /ansi-regex@2.1.1: resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} @@ -5187,11 +5015,6 @@ packages: engines: {node: '>=16'} dev: true - /antlr4@4.13.2: - resolution: {integrity: sha512-QiVbZhyy4xAZ17UPEuG3YTOt8ZaoeOR1CvEAqrEsDBsOqINslaB147i9xqljZqoyf5S+EUlGStaj+t22LT9MOg==} - engines: {node: '>=16'} - dev: true - /antlr4ts@0.5.0-alpha.4: resolution: {integrity: sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==} dev: true @@ -5212,7 +5035,6 @@ packages: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true /apicache@1.6.3: resolution: {integrity: sha512-jS3VfUFpQ9BesFQZcdd1vVYg3ZsO2kGPmTJHqycIYPAQs54r74CRiyj8DuzJpwzLwIfCBYzh4dy9Jt8xYbo27w==} @@ -5275,7 +5097,6 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true /argv@0.0.2: resolution: {integrity: sha512-dEamhpPEwRUBpLNHeuCm/v+g0anFByHahxodVO/BbAarHVBBg2MccCwf9K+o1Pof+2btdnkJelYVUWjW/VrATw==} @@ -5464,7 +5285,7 @@ packages: /asn1.js@4.10.1: resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} dependencies: - bn.js: 4.12.0 + bn.js: 4.12.1 inherits: 2.0.4 minimalistic-assert: 1.0.1 dev: true @@ -5575,31 +5396,39 @@ packages: resolution: {integrity: sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==} dev: true + /axios@0.21.4: + resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} + dependencies: + follow-redirects: 1.15.6(debug@4.4.0) + transitivePeerDependencies: + - debug + /axios@0.21.4(debug@4.3.5): resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} dependencies: follow-redirects: 1.15.6(debug@4.3.5) transitivePeerDependencies: - debug + dev: true /axios@0.24.0: resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==} dependencies: - follow-redirects: 1.15.9(debug@4.4.0) + follow-redirects: 1.15.9 transitivePeerDependencies: - debug /axios@0.26.1: resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} dependencies: - follow-redirects: 1.15.9(debug@4.4.0) + follow-redirects: 1.15.9 transitivePeerDependencies: - debug /axios@0.27.2: resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} dependencies: - follow-redirects: 1.15.9(debug@4.4.0) + follow-redirects: 1.15.9 form-data: 4.0.0 transitivePeerDependencies: - debug @@ -5607,7 +5436,7 @@ packages: /axios@0.28.1: resolution: {integrity: sha512-iUcGA5a7p0mVb4Gm/sy+FSECNkPFT4y7wt6OM/CDpO/OnNCvSs3PoMG8ibrC9jRoGYU0gUK5pXVC4NPXq6lHRQ==} dependencies: - follow-redirects: 1.15.9(debug@4.4.0) + follow-redirects: 1.15.9 form-data: 4.0.1 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -5616,7 +5445,7 @@ packages: /axios@1.7.9: resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==} dependencies: - follow-redirects: 1.15.9(debug@4.4.0) + follow-redirects: 1.15.9 form-data: 4.0.1 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -6280,7 +6109,6 @@ packages: /binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - dev: true /binary-parser@2.2.1: resolution: {integrity: sha512-5ATpz/uPDgq5GgEDxTB4ouXCde7q2lqAQlSdBRQVl/AJnxmQmhIfyxJx+0MGu//D5rHQifkfGbWWlaysG0o9NA==} @@ -6399,7 +6227,6 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color - dev: true /boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -6424,7 +6251,6 @@ packages: type-fest: 0.20.2 widest-line: 3.1.0 wrap-ansi: 7.0.0 - dev: true /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -6470,7 +6296,6 @@ packages: engines: {node: '>=8'} dependencies: fill-range: 7.1.1 - dev: true /brorand@1.1.0: resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} @@ -6484,7 +6309,6 @@ packages: /browser-stdout@1.3.1: resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} - dev: true /browserify-aes@1.2.0: resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} @@ -6564,7 +6388,6 @@ packages: /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: true /buffer-layout@1.2.2: resolution: {integrity: sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==} @@ -6763,7 +6586,6 @@ packages: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - dev: true /call-bind@1.0.7: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} @@ -6791,7 +6613,6 @@ packages: dependencies: call-bind-apply-helpers: 1.0.1 get-intrinsic: 1.2.7 - dev: true /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} @@ -6822,7 +6643,6 @@ packages: /camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - dev: true /caniuse-lite@1.0.30001695: resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==} @@ -6916,11 +6736,6 @@ packages: /chalk@5.3.0: resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - dev: false - - /chalk@5.4.1: - resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} /change-case@3.0.2: resolution: {integrity: sha512-Mww+SLF6MZ0U6kdg11algyKd5BARbyM4TbFBepwowYSR5ClfQGCGtxNXgykpN0uF/bstWeaGDT4JWaDh8zWAHA==} @@ -7021,14 +6836,12 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 - dev: true /chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} dependencies: readdirp: 4.1.1 - dev: true /chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -7040,7 +6853,6 @@ packages: /ci-info@2.0.0: resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} - dev: true /cids@0.7.5: resolution: {integrity: sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==} @@ -7082,7 +6894,6 @@ packages: /cli-boxes@2.2.1: resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} engines: {node: '>=6'} - dev: true /cli-table3@0.5.1: resolution: {integrity: sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==} @@ -7234,7 +7045,6 @@ packages: /command-exists@1.2.9: resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} - dev: true /command-line-args@4.0.7: resolution: {integrity: sha512-aUdPvQRAyBvQd2n7jXcsMDz68ckBJELXNzBybCHOibUWEg0mWTnaYCSRU8h9R+aNRSvDihJtssSRCiDRpLaezA==} @@ -7271,7 +7081,6 @@ packages: /commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} - dev: true /component-emitter@1.3.1: resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} @@ -7519,7 +7328,6 @@ packages: /cookie@0.4.2: resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} engines: {node: '>= 0.6'} - dev: true /cookie@0.6.0: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} @@ -7529,7 +7337,6 @@ packages: /cookie@0.7.1: resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} - dev: true /cookiejar@2.1.4: resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} @@ -7748,6 +7555,7 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + dev: true /cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} @@ -7756,7 +7564,6 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true /crypt@0.0.2: resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} @@ -7971,7 +7778,6 @@ packages: /decamelize@4.0.0: resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} engines: {node: '>=10'} - dev: true /decimal.js@10.5.0: resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} @@ -8186,7 +7992,6 @@ packages: /diff@5.2.0: resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} engines: {node: '>=0.3.1'} - dev: true /diffie-hellman@5.0.3: resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} @@ -8325,7 +8130,6 @@ packages: call-bind-apply-helpers: 1.0.1 es-errors: 1.3.0 gopd: 1.2.0 - dev: true /duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} @@ -8361,7 +8165,7 @@ packages: '@one-ini/wasm': 0.1.1 commander: 10.0.1 minimatch: 9.0.1 - semver: 7.6.2 + semver: 7.6.3 /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -8435,7 +8239,6 @@ packages: /encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - dev: true /encoding-down@5.0.4: resolution: {integrity: sha512-8CIZLDcSKxgzT+zX8ZVfgNbu8Md2wq/iqa1Y7zyVR18QBEAc0Nmzuvj/N5ykSKpfGzjM8qxbaFntLPwnVoUhZw==} @@ -8480,7 +8283,6 @@ packages: dependencies: ansi-colors: 4.1.3 strip-ansi: 6.0.1 - dev: true /entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} @@ -8645,7 +8447,6 @@ packages: /es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} - dev: true /es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} @@ -8663,7 +8464,6 @@ packages: engines: {node: '>= 0.4'} dependencies: es-errors: 1.3.0 - dev: true /es-set-tostringtag@2.0.3: resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} @@ -8789,7 +8589,6 @@ packages: /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - dev: true /escodegen@1.7.1: resolution: {integrity: sha512-2cd7+JUtUEmZVpGmfF9r+uRYXswJAkf85Ce8GvdBa7hSvdjY8hGo+rwC5syAgYzqHpfxNJzLntFjw6879yPbgQ==} @@ -9477,7 +9276,7 @@ packages: resolution: {integrity: sha512-NtlDnaVZah146Rm8HMRUNMgIwG/ED4jiqk0TME9zFheMl1jOp6jL1m0NKGjJwehXQ6ZKCPr16MTr+qspKpEXNg==} requiresBuild: true dependencies: - async: 2.6.2 + async: 2.6.4 clone: 2.1.2 concat-stream: 1.6.2 end-of-stream: 1.4.4 @@ -9493,7 +9292,7 @@ packages: resolution: {integrity: sha512-/MSbf/r2/Ld8o0l15AymjOTlPqpN8Cr4ByUEA9GtR4x0yAh3TdtDzEg29zMjXCNPI7u6E5fOQdj/Cf9Tc7oVNw==} deprecated: 'New package name format for new versions: @ethereumjs/ethash. Please update.' dependencies: - async: 2.6.2 + async: 2.6.4 buffer-xor: 2.0.2 ethereumjs-util: 7.1.5 miller-rabin: 4.0.1 @@ -9546,7 +9345,6 @@ packages: '@noble/secp256k1': 1.7.1 '@scure/bip32': 1.1.5 '@scure/bip39': 1.1.1 - dev: true /ethereum-cryptography@2.2.1: resolution: {integrity: sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==} @@ -9592,6 +9390,7 @@ packages: /ethereumjs-abi@0.6.5: resolution: {integrity: sha512-rCjJZ/AE96c/AAZc6O3kaog4FhOsAViaysBxqJNy2+LHP0ttH0zkZ7nXdVHOAyt6lFwLO0nlCwWszysG/ao1+g==} + deprecated: This library has been deprecated and usage is discouraged. dependencies: bn.js: 4.12.1 ethereumjs-util: 4.5.1 @@ -9649,7 +9448,7 @@ packages: resolution: {integrity: sha512-zCxaRMUOzzjvX78DTGiKjA+4h2/sF0OYL1QuPux0DHpyq8XiNoF5GYHtb++GUxVlMsMfZV7AVyzbtgcRdIcEPQ==} deprecated: 'New package name format for new versions: @ethereumjs/blockchain. Please update.' dependencies: - async: 2.6.2 + async: 2.6.4 ethashjs: 0.0.8 ethereumjs-block: 2.2.2 ethereumjs-common: 1.5.2 @@ -10010,7 +9809,7 @@ packages: transitivePeerDependencies: - bufferutil - utf-8-validate - dev: true + dev: false /express@4.19.2: resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} @@ -10088,7 +9887,6 @@ packages: vary: 1.1.2 transitivePeerDependencies: - supports-color - dev: true /ext@1.7.0: resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} @@ -10209,10 +10007,6 @@ packages: /fast-stable-stringify@1.0.0: resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} - /fast-uri@3.0.6: - resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} - dev: true - /fastestsmallesttextencoderdecoder@1.0.22: resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} @@ -10231,7 +10025,6 @@ packages: optional: true dependencies: picomatch: 4.0.2 - dev: true /fecha@4.2.3: resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} @@ -10296,7 +10089,6 @@ packages: engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 - dev: true /finalhandler@1.2.0: resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} @@ -10326,7 +10118,6 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color - dev: true /find-in-files@0.5.0: resolution: {integrity: sha512-VraTc6HdtdSHmAp0yJpAy20yPttGKzyBWc7b7FPnnsX9TOgmKx0g9xajizpF/iuu4IvNK4TP0SpyBT9zAlwG+g==} @@ -10364,7 +10155,6 @@ packages: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true /find-yarn-workspace-root@1.2.1: resolution: {integrity: sha512-dVtfb0WuQG+8Ag2uWkbG79hOUzEsRrhBzgfn86g2sJPkzmcpGdghbNTfUKGTxymFrY/tLIodDzLoW9nOJ4FY8Q==} @@ -10399,7 +10189,6 @@ packages: /flat@5.0.2: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true - dev: true /flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} @@ -10428,9 +10217,10 @@ packages: optional: true dependencies: debug: 4.3.5 + dev: true - /follow-redirects@1.15.9(debug@4.4.0): - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + /follow-redirects@1.15.6(debug@4.4.0): + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -10440,6 +10230,15 @@ packages: dependencies: debug: 4.4.0(supports-color@8.1.1) + /follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} dependencies: @@ -10462,7 +10261,7 @@ packages: resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} engines: {node: '>=14'} dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 signal-exit: 4.1.0 /forever-agent@0.6.1: @@ -10514,7 +10313,6 @@ packages: /fp-ts@1.19.3: resolution: {integrity: sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==} - dev: true /fragment-cache@0.2.1: resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} @@ -10564,7 +10362,6 @@ packages: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: true /fs-extra@8.1.0: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} @@ -10797,7 +10594,6 @@ packages: has-symbols: 1.1.0 hasown: 2.0.2 math-intrinsics: 1.1.0 - dev: true /get-paths@0.0.7: resolution: {integrity: sha512-0wdJt7C1XKQxuCgouqd+ZvLJ56FQixKoki9MrFaO4EriqzXOiH9gbukaDE1ou08S8Ns3/yDzoBAISNPqj6e6tA==} @@ -10816,7 +10612,6 @@ packages: dependencies: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - dev: true /get-stdin@6.0.0: resolution: {integrity: sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==} @@ -10912,7 +10707,6 @@ packages: engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 - dev: true /glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} @@ -10950,7 +10744,7 @@ packages: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 3.0.4 + minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 dev: true @@ -11074,7 +10868,6 @@ packages: /gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - dev: true /got@11.8.6: resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} @@ -11147,7 +10940,7 @@ packages: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: graphql: 16.10.0 - tslib: 2.6.3 + tslib: 2.8.1 optional: true /graphql@16.10.0: @@ -11327,7 +11120,6 @@ packages: - c-kzg - supports-color - utf-8-validate - dev: true /hardhat@2.22.6(ts-node@9.1.1)(typescript@4.9.5): resolution: {integrity: sha512-abFEnd9QACwEtSvZZGSmzvw7N3zhQN1cDKz5SLHAupfG24qTHofCjqvD5kT5Wwsq5XOL0ON1Mq5rr4v0XX5ciw==} @@ -11451,7 +11243,6 @@ packages: /has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} - dev: true /has-tostringtag@1.0.2: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} @@ -11543,7 +11334,6 @@ packages: /he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true - dev: true /header-case@1.0.1: resolution: {integrity: sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==} @@ -11791,7 +11581,6 @@ packages: /immutable@4.3.7: resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} - dev: true /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -11877,7 +11666,6 @@ packages: resolution: {integrity: sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==} dependencies: fp-ts: 1.19.3 - dev: true /ioredis@5.4.1: resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==} @@ -11945,6 +11733,7 @@ packages: /is-arguments@1.2.0: resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} engines: {node: '>= 0.4'} + requiresBuild: true dependencies: call-bound: 1.0.3 has-tostringtag: 1.0.2 @@ -12010,7 +11799,6 @@ packages: engines: {node: '>=8'} dependencies: binary-extensions: 2.3.0 - dev: true /is-boolean-object@1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} @@ -12157,7 +11945,6 @@ packages: /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - dev: true /is-finalizationregistry@1.1.1: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} @@ -12227,7 +12014,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 - dev: true /is-hex-prefixed@1.0.0: resolution: {integrity: sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==} @@ -12292,7 +12078,6 @@ packages: /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: true /is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} @@ -12302,7 +12087,6 @@ packages: /is-plain-obj@2.1.0: resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} engines: {node: '>=8'} - dev: true /is-plain-object@2.0.4: resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} @@ -12419,7 +12203,6 @@ packages: /is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - dev: true /is-upper-case@1.1.2: resolution: {integrity: sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==} @@ -12688,7 +12471,6 @@ packages: hasBin: true dependencies: argparse: 2.0.1 - dev: true /jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} @@ -12794,7 +12576,6 @@ packages: /json-stream-stringify@3.1.6: resolution: {integrity: sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog==} engines: {node: '>=7.10.1'} - dev: true /json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} @@ -12828,7 +12609,6 @@ packages: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: graceful-fs: 4.2.11 - dev: true /jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} @@ -12943,7 +12723,7 @@ packages: resolution: {integrity: sha512-rm71jaA/P+6HeCpoRhmCv8KVBIi0tfGuO/dMKicbQnQW/YJntJ6MnnspkodoA4QstMVEZArsCphmd0bJEtoMjQ==} engines: {node: '>= 7.6.0'} dependencies: - debug: 4.3.5 + debug: 4.4.0(supports-color@8.1.1) koa-compose: 4.1.0 transitivePeerDependencies: - supports-color @@ -12953,7 +12733,7 @@ packages: engines: {node: '>= 8.0.0'} deprecated: '**IMPORTANT 10x+ PERFORMANCE UPGRADE**: Please upgrade to v12.0.1+ as we have fixed an issue with debuglog causing 10x slower router benchmark performance, see https://github.com/koajs/router/pull/173' dependencies: - debug: 4.3.5 + debug: 4.4.0(supports-color@8.1.1) http-errors: 1.8.1 koa-compose: 4.1.0 methods: 1.1.2 @@ -12965,7 +12745,7 @@ packages: resolution: {integrity: sha512-gaDdj3GtzoLoeosacd50kBBTnnh3B9AYxDThQUo4sfUyXdOhY6ku1qyZKW88tQCRgc3Sw6ChXYXWZwwgjOxE0w==} engines: {node: '>= 12'} dependencies: - debug: 4.3.5 + debug: 4.4.0(supports-color@8.1.1) http-errors: 2.0.0 koa-compose: 4.1.0 methods: 1.1.2 @@ -12977,7 +12757,7 @@ packages: resolution: {integrity: sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==} engines: {node: '>= 8'} dependencies: - debug: 4.3.5 + debug: 4.4.0(supports-color@8.1.1) http-errors: 1.8.1 resolve-path: 1.4.0 transitivePeerDependencies: @@ -13073,7 +12853,7 @@ packages: content-disposition: 0.5.4 content-type: 1.0.5 cookies: 0.9.1 - debug: 4.3.5 + debug: 4.4.0(supports-color@8.1.1) delegates: 1.0.0 depd: 2.0.0 destroy: 1.2.0 @@ -13337,7 +13117,6 @@ packages: engines: {node: '>=10'} dependencies: p-locate: 5.0.0 - dev: true /lodash.assign@4.2.0: resolution: {integrity: sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw==} @@ -13355,7 +13134,7 @@ packages: /lodash.isequal@4.5.0: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. - dev: true + dev: false /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -13387,7 +13166,6 @@ packages: dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 - dev: true /logform@2.6.1: resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==} @@ -13509,7 +13287,6 @@ packages: /lru_map@0.3.3: resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==} - dev: true /ltgt@2.1.3: resolution: {integrity: sha512-5VjHC5GsENtIi5rbJd+feEpDKhfr7j0odoUR2Uh978g+2p93nd5o34cTjQWohXsPsCZeqoDnIqEf88mPCe0Pfw==} @@ -13603,7 +13380,6 @@ packages: /math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - dev: true /math-random@1.0.4: resolution: {integrity: sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==} @@ -13662,7 +13438,6 @@ packages: /memorystream@0.3.1: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} engines: {node: '>= 0.10.0'} - dev: true /merge-descriptors@1.0.1: resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} @@ -13670,7 +13445,6 @@ packages: /merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - dev: true /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} @@ -14012,7 +13786,6 @@ packages: resolution: {integrity: sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==} dependencies: obliterator: 2.0.5 - dev: true /mocha-circleci-reporter@0.0.3: resolution: {integrity: sha512-sZHmd+HH0pgQjTdk0itV2+RSOikqLo7kravRLKTmzcvyu9lA69pXd4KLAv5NMcF4c3EnSatX0iBSM4gSXFYjEw==} @@ -14063,7 +13836,6 @@ packages: yargs: 16.2.0 yargs-parser: 20.2.9 yargs-unparser: 2.0.0 - dev: true /mocha@4.1.0: resolution: {integrity: sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==} @@ -14541,7 +14313,6 @@ packages: /normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - dev: true /normalize-url@4.5.1: resolution: {integrity: sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==} @@ -14642,7 +14413,6 @@ packages: /object-inspect@1.13.3: resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} engines: {node: '>= 0.4'} - dev: true /object-is@1.1.6: resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} @@ -14768,7 +14538,6 @@ packages: /obliterator@2.0.5: resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==} - dev: true /oboe@2.1.4: resolution: {integrity: sha512-ymBJ4xSC6GBXLT9Y7lirj+xbqBLa+jADGJldGEYG7u8sZbS9GyG+u1Xk9c5cbriKwSpCg41qUhPjvU5xOpvIyQ==} @@ -14887,7 +14656,6 @@ packages: /os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} - dev: true /own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} @@ -14930,7 +14698,6 @@ packages: engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 - dev: true /p-locate@2.0.0: resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} @@ -14944,7 +14711,6 @@ packages: engines: {node: '>=10'} dependencies: p-limit: 3.1.0 - dev: true /p-map@4.0.0: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} @@ -15131,7 +14897,6 @@ packages: /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - dev: true /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} @@ -15159,7 +14924,6 @@ packages: /path-to-regexp@0.1.12: resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} - dev: true /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} @@ -15212,17 +14976,14 @@ packages: /picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - dev: true /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - dev: true /picomatch@4.0.2: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} - dev: true /pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} @@ -15539,7 +15300,7 @@ packages: '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 '@types/long': 4.0.2 - '@types/node': 22.10.10 + '@types/node': 20.17.16 long: 4.0.0 /protobufjs@7.3.2: @@ -15557,7 +15318,7 @@ packages: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 22.10.10 + '@types/node': 20.17.16 long: 5.2.3 /protobufjs@7.4.0: @@ -15713,7 +15474,6 @@ packages: engines: {node: '>=0.6'} dependencies: side-channel: 1.1.0 - dev: true /qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} @@ -15863,12 +15623,10 @@ packages: engines: {node: '>=8.10.0'} dependencies: picomatch: 2.3.1 - dev: true /readdirp@4.1.1: resolution: {integrity: sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==} engines: {node: '>= 14.18.0'} - dev: true /readonly-date@1.0.0: resolution: {integrity: sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==} @@ -16169,7 +15927,6 @@ packages: resolution: {integrity: sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==} dependencies: path-parse: 1.0.7 - dev: true /resolve@1.22.10: resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} @@ -16496,12 +16253,10 @@ packages: /semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true - dev: true /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - dev: true /semver@7.6.2: resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} @@ -16553,7 +16308,6 @@ packages: statuses: 2.0.1 transitivePeerDependencies: - supports-color - dev: true /sentence-case@2.1.1: resolution: {integrity: sha512-ENl7cYHaK/Ktwk5OTD+aDbQ3uC8IByu/6Bkg+HDv8Mm+XnBnppVNalcfJTNsp1ibstKh030/JKQQWglDvtKwEQ==} @@ -16566,7 +16320,6 @@ packages: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} dependencies: randombytes: 2.1.0 - dev: true /serve-static@1.15.0: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} @@ -16590,7 +16343,6 @@ packages: send: 0.19.0 transitivePeerDependencies: - supports-color - dev: true /servify@0.1.12: resolution: {integrity: sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==} @@ -16739,7 +16491,6 @@ packages: dependencies: es-errors: 1.3.0 object-inspect: 1.13.3 - dev: true /side-channel-map@1.0.1: resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} @@ -16749,7 +16500,6 @@ packages: es-errors: 1.3.0 get-intrinsic: 1.2.7 object-inspect: 1.13.3 - dev: true /side-channel-weakmap@1.0.2: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} @@ -16760,7 +16510,6 @@ packages: get-intrinsic: 1.2.7 object-inspect: 1.13.3 side-channel-map: 1.0.1 - dev: true /side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} @@ -16780,7 +16529,6 @@ packages: side-channel-list: 1.0.0 side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - dev: true /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -17014,14 +16762,13 @@ packages: dependencies: command-exists: 1.2.9 commander: 8.3.0 - follow-redirects: 1.15.9(debug@4.4.0) + follow-redirects: 1.15.6(debug@4.4.0) js-sha3: 0.8.0 memorystream: 0.3.1 semver: 5.7.2 tmp: 0.0.33 transitivePeerDependencies: - debug - dev: true /solhint-plugin-prettier@0.0.5(prettier-plugin-solidity@1.3.1)(prettier@2.8.8): resolution: {integrity: sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA==} @@ -17067,20 +16814,20 @@ packages: dependencies: '@solidity-parser/parser': 0.16.2 ajv: 6.12.6 - antlr4: 4.13.2 + antlr4: 4.13.1 ast-parents: 0.0.1 chalk: 4.1.2 commander: 10.0.1 cosmiconfig: 8.3.6(typescript@5.7.3) fast-diff: 1.3.0 glob: 8.1.0 - ignore: 5.3.2 + ignore: 5.3.1 js-yaml: 4.1.0 lodash: 4.17.21 pluralize: 8.0.0 - semver: 7.6.3 + semver: 7.6.2 strip-ansi: 6.0.1 - table: 6.9.0 + table: 6.8.2 text-table: 0.2.0 optionalDependencies: prettier: 2.8.8 @@ -17229,7 +16976,6 @@ packages: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - dev: true /source-map-url@0.4.1: resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} @@ -17253,7 +16999,6 @@ packages: /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - dev: true /spawn-sync@1.0.15: resolution: {integrity: sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==} @@ -17361,7 +17106,6 @@ packages: engines: {node: '>=6'} dependencies: type-fest: 0.7.1 - dev: true /standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} @@ -17563,7 +17307,6 @@ packages: /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - dev: true /superstruct@0.15.5: resolution: {integrity: sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==} @@ -17696,17 +17439,6 @@ packages: strip-ansi: 6.0.1 dev: true - /table@6.9.0: - resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} - engines: {node: '>=10.0.0'} - dependencies: - ajv: 8.17.1 - lodash.truncate: 4.4.2 - slice-ansi: 4.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: true - /tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} @@ -17880,7 +17612,6 @@ packages: dependencies: fdir: 6.4.3(picomatch@4.0.2) picomatch: 4.0.2 - dev: true /title-case@2.1.1: resolution: {integrity: sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==} @@ -17894,7 +17625,6 @@ packages: engines: {node: '>=0.6.0'} dependencies: os-tmpdir: 1.0.2 - dev: true /tmp@0.1.0: resolution: {integrity: sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==} @@ -17938,7 +17668,6 @@ packages: engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 - dev: true /to-regex@3.0.2: resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==} @@ -18034,7 +17763,7 @@ packages: engines: {node: '>=8'} requiresBuild: true dependencies: - tslib: 2.6.3 + tslib: 2.8.1 optional: true /ts-node@10.9.2(@types/node@20.17.16)(typescript@5.7.3): @@ -18129,7 +17858,6 @@ packages: /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - dev: true /tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} @@ -18143,7 +17871,6 @@ packages: /tsort@0.0.1: resolution: {integrity: sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==} - dev: true /tsscmp@1.0.6: resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} @@ -18212,17 +17939,14 @@ packages: /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} - dev: true /type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - dev: true /type-fest@0.7.1: resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} engines: {node: '>=8'} - dev: true /type-fest@3.13.1: resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} @@ -18246,7 +17970,7 @@ packages: hasBin: true dependencies: command-line-args: 4.0.7 - debug: 4.3.5 + debug: 4.4.0(supports-color@8.1.1) fs-extra: 7.0.1 js-sha3: 0.8.0 lodash: 4.17.21 @@ -18459,9 +18183,6 @@ packages: /undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - /undici-types@6.20.0: - resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} - /undici@5.28.4: resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} engines: {node: '>=14.0'} @@ -18474,7 +18195,6 @@ packages: engines: {node: '>=14.0'} dependencies: '@fastify/busboy': 2.1.1 - dev: true /undici@6.21.1: resolution: {integrity: sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==} @@ -18510,7 +18230,6 @@ packages: /universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} - dev: true /universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} @@ -18579,7 +18298,7 @@ packages: resolution: {integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==} dependencies: punycode: 1.4.1 - qs: 6.12.3 + qs: 6.14.0 dev: true /usb@1.9.2: @@ -20065,7 +19784,6 @@ packages: engines: {node: '>=8'} dependencies: string-width: 4.2.3 - dev: true /wif@2.0.6: resolution: {integrity: sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==} @@ -20144,7 +19862,6 @@ packages: /workerpool@6.5.1: resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} - dev: true /wrap-ansi@2.1.0: resolution: {integrity: sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==} @@ -20380,7 +20097,6 @@ packages: decamelize: 4.0.0 flat: 5.0.2 is-plain-obj: 2.1.0 - dev: true /yargs@10.1.2: resolution: {integrity: sha512-ivSoxqBGYOqQVruxD35+EyCFDYNEFL/Uo6FcOnz+9xZdZzK0Zzw4r4KhbrME1Oo2gOggwJod2MnsdamSG7H9ig==} @@ -20453,7 +20169,6 @@ packages: /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - dev: true /zen-observable-ts@1.2.5: resolution: {integrity: sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==} From 22b7f36ccea08ff24c9a13b10904bffacca26466 Mon Sep 17 00:00:00 2001 From: Daniel Kronovet Date: Wed, 26 Jun 2024 11:31:43 +0100 Subject: [PATCH 03/72] Add Shell contracts WIP --- .soliumrc.json | 3 +- contracts/bridging/ColonyShell.sol | 65 +++++++++++++++++ .../colonyNetwork/ColonyNetworkDeployer.sol | 9 +++ .../colonyNetwork/ColonyNetworkShells.sol | 71 +++++++++++++++++++ .../colonyNetwork/ColonyNetworkSkills.sol | 19 +---- .../colonyNetwork/ColonyNetworkStorage.sol | 20 +++++- docs/interfaces/icolonynetwork.md | 13 ++++ helpers/upgradable-contracts.js | 3 +- test/truffle-fixture.js | 6 ++ 9 files changed, 188 insertions(+), 21 deletions(-) create mode 100644 contracts/bridging/ColonyShell.sol create mode 100644 contracts/colonyNetwork/ColonyNetworkShells.sol diff --git a/.soliumrc.json b/.soliumrc.json index 450acbc769..712f0e541b 100644 --- a/.soliumrc.json +++ b/.soliumrc.json @@ -12,6 +12,7 @@ "error-reason": "error", "max-len": "error", "no-trailing-whitespace": "error", - "blank-lines": "error" + "blank-lines": "error", + "custom-errors": "off" } } diff --git a/contracts/bridging/ColonyShell.sol b/contracts/bridging/ColonyShell.sol new file mode 100644 index 0000000000..1a19b863aa --- /dev/null +++ b/contracts/bridging/ColonyShell.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + This file is part of The Colony Network. + + The Colony Network is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The Colony Network is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with The Colony Network. If not, see . +*/ + +pragma solidity 0.8.27; +pragma experimental ABIEncoderV2; + +import { BasicMetaTransaction } from "./../common/BasicMetaTransaction.sol"; +import { CallWithGuards } from "../common/CallWithGuards.sol"; +import { DSAuth } from "./../../lib/dappsys/auth.sol"; +import { ERC20Extended } from "./../common/ERC20Extended.sol"; +import { Multicall } from "./../common/Multicall.sol"; +import { IColonyNetwork } from "./../colonyNetwork/IColonyNetwork.sol"; + +contract ColonyShell is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards { + // Address of the Resolver contract used by EtherRouter for lookups and routing + address resolver; // Storage slot 2 (from DSAuth there is authority and owner at storage slots 0 and 1 respectively) + + mapping(address => uint256) metatransactionNonces; + + function getMetatransactionNonce(address _user) public view override returns (uint256 _nonce) { + return metatransactionNonces[_user]; + } + + function incrementMetatransactionNonce(address _user) internal override { + metatransactionNonces[_user] += 1; + } + + // Events + + event ColonyFundsClaimed(address token, uint256 balance); + event TransferMade(address token, address user, uint256 amount); + + // Public functions + + function claimColonyShellFunds(address _token) public { + uint256 balance = (_token == address(0x0)) + ? address(this).balance + : ERC20Extended(_token).balanceOf(address(this)); + + IColonyNetwork(owner).sendClaimColonyShellFunds(_token, balance); + + emit ColonyFundsClaimed(_token, balance); + } + + function transfer(address _token, address _user, uint256 _amount) public auth { + require(ERC20Extended(_token).transfer(_user, _amount), "colony-shell-transfer-failed"); + + emit TransferMade(_token, _user, _amount); + } +} diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index 2051ce8da5..7b4a275619 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -132,6 +132,15 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage { return (address(token), colonyAddress); } + function createColonyShell(bytes32 _salt) public onlyColonyBridge { + ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( + _salt, + type(EtherRouterCreate3).creationCode, + abi.encodeWithSignature("setOwner(address)", (address(this))), + ICreateX.Values(0, 0) + ); + } + /** * @dev Generates pseudo-randomly a salt value using a diverse selection of block and * transaction properties. diff --git a/contracts/colonyNetwork/ColonyNetworkShells.sol b/contracts/colonyNetwork/ColonyNetworkShells.sol new file mode 100644 index 0000000000..4c98564c08 --- /dev/null +++ b/contracts/colonyNetwork/ColonyNetworkShells.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + This file is part of The Colony Network. + + The Colony Network is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The Colony Network is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with The Colony Network. If not, see . +*/ + +pragma solidity 0.8.27; +pragma experimental ABIEncoderV2; + +import { IColony } from "./../colony/IColony.sol"; +import { Multicall } from "./../common/Multicall.sol"; +import { ColonyNetworkStorage } from "./ColonyNetworkStorage.sol"; +import { ColonyShell } from "./../bridging/ColonyShell.sol"; + +contract ColonyNetworkShells is ColonyNetworkStorage, Multicall { + // To shells + + function sendDeployColonyShell(bytes32 _salt) public calledByColony{ + bytes memory payload = abi.encodeWithSignature( + "deployColonyShell(bytes32)", + _salt + ); + + require(callThroughBridgeWithGuards(payload), "colony-network-shell-deploy-failed"); + } + + function colonyShellTransfer(address _colony, address _token, address _user, uint256 _amount) public onlyColonyBridge { + ColonyShell(_colony).transfer(_token, _user, _amount); + } + + function sendColonyShellTransfer(address _token, address _user, uint256 _amount) public calledByColony{ + bytes memory payload = abi.encodeWithSignature( + "colonyShellTransfer(address,address,address,uint256)", + msgSender(), + _token, + _user, + _amount + ); + + require(callThroughBridgeWithGuards(payload), "colony-network-shell-transfer-failed"); + } + + // From shells + + function claimColonyShellFunds(address _colony, address _token, uint256 _balance) public onlyColonyBridge { + IColony(_colony).claimColonyShellFunds(_token, _balance); + } + + function sendClaimColonyShellFunds(address _token, uint256 _balance) public calledByColony{ + bytes memory payload = abi.encodeWithSignature( + "claimColonyShellFunds(address,address,uint256)", + msgSender(), + _token, + _balance + ); + + require(callThroughBridgeWithGuards(payload), "colony-network-shell-claim-failed"); + } +} diff --git a/contracts/colonyNetwork/ColonyNetworkSkills.sol b/contracts/colonyNetwork/ColonyNetworkSkills.sol index 4133da000f..cf6b68fd12 100644 --- a/contracts/colonyNetwork/ColonyNetworkSkills.sol +++ b/contracts/colonyNetwork/ColonyNetworkSkills.sol @@ -24,7 +24,7 @@ import "./ColonyNetworkStorage.sol"; import { IColonyBridge } from "./../bridging/IColonyBridge.sol"; import { CallWithGuards } from "../common/CallWithGuards.sol"; -contract ColonyNetworkSkills is ColonyNetworkStorage, Multicall, CallWithGuards { +contract ColonyNetworkSkills is ColonyNetworkStorage, Multicall { // Skills function addSkill( @@ -488,21 +488,4 @@ contract ColonyNetworkSkills is ColonyNetworkStorage, Multicall, CallWithGuards } return _reputation; } - - function callThroughBridgeWithGuards(bytes memory payload) internal returns (bool) { - bytes memory bridgePayload = abi.encodeWithSignature( - "sendMessage(uint256,bytes)", - getAndCacheReputationMiningChainId(), - payload - ); - - (bool success, bytes memory returnData) = callWithGuards(colonyBridgeAddress, bridgePayload); - - // If the function call was a success, and it returned true - if (success) { - bool res = abi.decode(returnData, (bool)); - return res; - } - return false; - } } diff --git a/contracts/colonyNetwork/ColonyNetworkStorage.sol b/contracts/colonyNetwork/ColonyNetworkStorage.sol index d544997828..48914a2ee5 100644 --- a/contracts/colonyNetwork/ColonyNetworkStorage.sol +++ b/contracts/colonyNetwork/ColonyNetworkStorage.sol @@ -24,11 +24,12 @@ import { CommonStorage } from "./../common/CommonStorage.sol"; import { MultiChain } from "./../common/MultiChain.sol"; import { ERC20Extended } from "./../common/ERC20Extended.sol"; import { ColonyNetworkDataTypes } from "./ColonyNetworkDataTypes.sol"; +import { CallWithGuards } from "../common/CallWithGuards.sol"; // ignore-file-swc-131 // ignore-file-swc-108 -contract ColonyNetworkStorage is ColonyNetworkDataTypes, DSMath, CommonStorage, MultiChain { +contract ColonyNetworkStorage is ColonyNetworkDataTypes, DSMath, CommonStorage, MultiChain, CallWithGuards { // Number of colonies in the network uint256 colonyCount; // Storage slot 6 // uint256 version number of the latest deployed Colony contract, used in creating new colonies @@ -217,4 +218,21 @@ contract ColonyNetworkStorage is ColonyNetworkDataTypes, DSMath, CommonStorage, } return reputationMiningChainId; } + + function callThroughBridgeWithGuards(bytes memory payload) internal returns (bool) { + bytes memory bridgePayload = abi.encodeWithSignature( + "sendMessage(uint256,bytes)", + getAndCacheReputationMiningChainId(), + payload + ); + + (bool success, bytes memory returnData) = callWithGuards(colonyBridgeAddress, bridgePayload); + + // If the function call was a success, and it returned true + if (success) { + bool res = abi.decode(returnData, (bool)); + return res; + } + return false; + } } diff --git a/docs/interfaces/icolonynetwork.md b/docs/interfaces/icolonynetwork.md index 2e230638b5..bd49525b72 100644 --- a/docs/interfaces/icolonynetwork.md +++ b/docs/interfaces/icolonynetwork.md @@ -1074,6 +1074,19 @@ Used to track that a user is eligible to claim a reward |_amount|uint256|The amount of CLNY to be awarded +### ▸ `sendClaimColonyShellFunds(address _token, uint256 _balance)` + +Send the claimFunds transaction from the shell to the colony + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_token|address|The token being held by the shell +|_balance|uint256|The shell's current balance of the token + + ### ▸ `setColonyBridgeAddress(address _bridgeAddress)` Called to set the address of the colony bridge contract diff --git a/helpers/upgradable-contracts.js b/helpers/upgradable-contracts.js index abf1a55ba1..f76e9cc1a8 100644 --- a/helpers/upgradable-contracts.js +++ b/helpers/upgradable-contracts.js @@ -131,6 +131,7 @@ exports.setupUpgradableColonyNetwork = async function setupUpgradableColonyNetwo colonyNetworkENS, colonyNetworkExtensions, colonyNetworkSkills, + colonyNetworkShells, contractRecovery, ) { const deployedImplementations = {}; @@ -139,9 +140,9 @@ exports.setupUpgradableColonyNetwork = async function setupUpgradableColonyNetwo deployedImplementations.ColonyNetworkMining = colonyNetworkMining.address; deployedImplementations.ColonyNetworkAuction = colonyNetworkAuction.address; deployedImplementations.ColonyNetworkENS = colonyNetworkENS.address; - deployedImplementations.ColonyNetworkSkills = colonyNetworkSkills.address; deployedImplementations.ColonyNetworkExtensions = colonyNetworkExtensions.address; deployedImplementations.ColonyNetworkSkills = colonyNetworkSkills.address; + deployedImplementations.ColonyNetworkShells = colonyNetworkShells.address; deployedImplementations.ContractRecovery = contractRecovery.address; await exports.setupEtherRouter("colonyNetwork", "IColonyNetwork", deployedImplementations, resolver); diff --git a/test/truffle-fixture.js b/test/truffle-fixture.js index bb22dafa57..49b786a54f 100644 --- a/test/truffle-fixture.js +++ b/test/truffle-fixture.js @@ -24,6 +24,7 @@ const ColonyNetworkAuction = artifacts.require("ColonyNetworkAuction"); const ColonyNetworkENS = artifacts.require("ColonyNetworkENS"); const ColonyNetworkExtensions = artifacts.require("ColonyNetworkExtensions"); const ColonyNetworkSkills = artifacts.require("ColonyNetworkSkills"); +const ColonyNetworkShells = artifacts.require("ColonyNetworkShells"); const IColonyNetwork = artifacts.require("IColonyNetwork"); const ENSRegistry = artifacts.require("ENSRegistry"); @@ -136,6 +137,9 @@ async function deployContracts() { const colonyNetworkSkills = await ColonyNetworkSkills.new(); ColonyNetworkSkills.setAsDeployed(colonyNetworkSkills); + const colonyNetworkShells = await ColonyNetworkShells.new(); + ColonyNetworkShells.setAsDeployed(colonyNetworkShells); + const reputationMiningCycle = await ReputationMiningCycle.new(); ReputationMiningCycle.setAsDeployed(reputationMiningCycle); @@ -156,6 +160,7 @@ async function setupColonyNetwork() { const colonyNetworkENS = await ColonyNetworkENS.deployed(); const colonyNetworkExtensions = await ColonyNetworkExtensions.deployed(); const colonyNetworkSkills = await ColonyNetworkSkills.deployed(); + const colonyNetworkShells = await ColonyNetworkShells.deployed(); // const etherRouter = await EtherRouter.deployed(); const resolver = await Resolver.deployed(); const contractRecovery = await ContractRecovery.deployed(); @@ -191,6 +196,7 @@ async function setupColonyNetwork() { colonyNetworkENS, colonyNetworkExtensions, colonyNetworkSkills, + colonyNetworkShells, contractRecovery, ); From 50a143fe19360f9f43c8b02ff89a41a51b8fbd6f Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Tue, 6 Aug 2024 13:17:03 +0100 Subject: [PATCH 04/72] Make bridging messages only valid on one chain --- .../bridging/WormholeBridgeForColony.sol | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/contracts/bridging/WormholeBridgeForColony.sol b/contracts/bridging/WormholeBridgeForColony.sol index 13674d4a8a..c73e0a1bd6 100644 --- a/contracts/bridging/WormholeBridgeForColony.sol +++ b/contracts/bridging/WormholeBridgeForColony.sol @@ -101,11 +101,17 @@ contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { // We ignore sequence numbers - bridging out of order is okay, because we have our own way of handling that + bytes memory payload = wormholeMessage.payload; + // Strip off the chain id prefix, and make sure we are on that chain Id + uint256 destinationChainId; + bytes memory payloadWithoutChainId; + + (destinationChainId, payloadWithoutChainId) = abi.decode(payload, (uint256, bytes)); + + require(destinationChainId == block.chainid, "colony-bridge-destination-chain-id-mismatch"); + // Make the call requested to the colony network - (bool success, bytes memory returndata) = callWithGuards( - colonyNetwork, - wormholeMessage.payload - ); + (bool success, bytes memory returndata) = callWithGuards(colonyNetwork, payloadWithoutChainId); // Note that this is not a require because returndata might not be a string, and if we try // to decode it we'll get a revert. @@ -122,7 +128,11 @@ contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { // This returns a sequence, but we don't care about it // The first sequence ID is, I believe 0, so all return values are potentially valid // slither-disable-next-line unused-return - try wormhole.publishMessage(0, _payload, 0) { + + // For wormhole, we prefix the supplied payload with the _evmChainId + // This is because wormhole is a generic bridge, and we need to tell it which chain to send to + + try wormhole.publishMessage(0, abi.encode(_evmChainId, _payload), 0) { return true; } catch { return false; From 6f84e3662c4cb2eb6c2971bbb519868de66160e0 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Thu, 8 Aug 2024 12:12:07 +0100 Subject: [PATCH 05/72] Deploy colony shells --- contracts/colony/Colony.sol | 4 ++ contracts/colony/IColony.sol | 2 + .../colonyNetwork/ColonyNetworkDeployer.sol | 8 ++- .../colonyNetwork/ColonyNetworkShells.sol | 28 +++++++--- .../colonyNetwork/ColonyNetworkStorage.sol | 8 ++- contracts/colonyNetwork/IColonyNetwork.sol | 7 +++ test/cross-chain/cross-chain.js | 56 +++++++++---------- 7 files changed, 74 insertions(+), 39 deletions(-) diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index 7aa19ac578..7d5d6840dc 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -324,6 +324,10 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); } + function createColonyShell(uint256 _destinationChainId, bytes32 _salt) public { + IColonyNetwork(colonyNetworkAddress).createColonyShell(_destinationChainId, _salt); + } + function getMetatransactionNonce(address _user) public view override returns (uint256 nonce) { return metatransactionNonces[_user]; } diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index d1af3f243f..7dfa2a40a8 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -858,6 +858,8 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, /// @return uint256 amount Amount of the token that the domain can receive function getAllowedDomainReputationReceipt(uint256 _domainId) external view returns (uint256); + function createColonyShell(uint256 _destinationChainId, bytes32 _salt) external; + /// @notice Get the total amount of tokens `_token` minus amount reserved to be paid to the reputation and token holders as rewards. /// @param _token Address of the token, `0x0` value indicates Ether /// @return amount Total amount of tokens in funding pots other than the rewards pot (id 0) diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index 7b4a275619..3e50875caf 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -132,7 +132,13 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage { return (address(token), colonyAddress); } - function createColonyShell(bytes32 _salt) public onlyColonyBridge { + function createColonyShell(uint256 _destinationChainId, bytes32 _salt) public calledByColony { + // TODO: Check if the colony is allowed to use the salt + bytes memory payload = abi.encodeWithSignature("createColonyShellFromBridge(bytes32)", _salt); + IColonyBridge(colonyBridgeAddress).sendMessage(_destinationChainId, payload); + } + + function createColonyShellFromBridge(bytes32 _salt) public onlyColonyBridge { ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( _salt, type(EtherRouterCreate3).creationCode, diff --git a/contracts/colonyNetwork/ColonyNetworkShells.sol b/contracts/colonyNetwork/ColonyNetworkShells.sol index 4c98564c08..abb3afa51d 100644 --- a/contracts/colonyNetwork/ColonyNetworkShells.sol +++ b/contracts/colonyNetwork/ColonyNetworkShells.sol @@ -27,20 +27,26 @@ import { ColonyShell } from "./../bridging/ColonyShell.sol"; contract ColonyNetworkShells is ColonyNetworkStorage, Multicall { // To shells - function sendDeployColonyShell(bytes32 _salt) public calledByColony{ - bytes memory payload = abi.encodeWithSignature( - "deployColonyShell(bytes32)", - _salt - ); + function sendDeployColonyShell(bytes32 _salt) public calledByColony { + bytes memory payload = abi.encodeWithSignature("deployColonyShell(bytes32)", _salt); require(callThroughBridgeWithGuards(payload), "colony-network-shell-deploy-failed"); } - function colonyShellTransfer(address _colony, address _token, address _user, uint256 _amount) public onlyColonyBridge { + function colonyShellTransfer( + address _colony, + address _token, + address _user, + uint256 _amount + ) public onlyColonyBridge { ColonyShell(_colony).transfer(_token, _user, _amount); } - function sendColonyShellTransfer(address _token, address _user, uint256 _amount) public calledByColony{ + function sendColonyShellTransfer( + address _token, + address _user, + uint256 _amount + ) public calledByColony { bytes memory payload = abi.encodeWithSignature( "colonyShellTransfer(address,address,address,uint256)", msgSender(), @@ -54,11 +60,15 @@ contract ColonyNetworkShells is ColonyNetworkStorage, Multicall { // From shells - function claimColonyShellFunds(address _colony, address _token, uint256 _balance) public onlyColonyBridge { + function claimColonyShellFunds( + address _colony, + address _token, + uint256 _balance + ) public onlyColonyBridge { IColony(_colony).claimColonyShellFunds(_token, _balance); } - function sendClaimColonyShellFunds(address _token, uint256 _balance) public calledByColony{ + function sendClaimColonyShellFunds(address _token, uint256 _balance) public calledByColony { bytes memory payload = abi.encodeWithSignature( "claimColonyShellFunds(address,address,uint256)", msgSender(), diff --git a/contracts/colonyNetwork/ColonyNetworkStorage.sol b/contracts/colonyNetwork/ColonyNetworkStorage.sol index 48914a2ee5..cd2f0b7c8f 100644 --- a/contracts/colonyNetwork/ColonyNetworkStorage.sol +++ b/contracts/colonyNetwork/ColonyNetworkStorage.sol @@ -29,7 +29,13 @@ import { CallWithGuards } from "../common/CallWithGuards.sol"; // ignore-file-swc-131 // ignore-file-swc-108 -contract ColonyNetworkStorage is ColonyNetworkDataTypes, DSMath, CommonStorage, MultiChain, CallWithGuards { +contract ColonyNetworkStorage is + ColonyNetworkDataTypes, + DSMath, + CommonStorage, + MultiChain, + CallWithGuards +{ // Number of colonies in the network uint256 colonyCount; // Storage slot 6 // uint256 version number of the latest deployed Colony contract, used in creating new colonies diff --git a/contracts/colonyNetwork/IColonyNetwork.sol b/contracts/colonyNetwork/IColonyNetwork.sol index 850840598e..dc01dab49e 100644 --- a/contracts/colonyNetwork/IColonyNetwork.sol +++ b/contracts/colonyNetwork/IColonyNetwork.sol @@ -648,4 +648,11 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery, IBasicMetaTransac address _colonyAddress, uint256 _domainId ) external view returns (address domainTokenReceiverAddress); + /// @notice Send the claimFunds transaction from the shell to the colony + /// @param _token The token being held by the shell + /// @param _balance The shell's current balance of the token + function sendClaimColonyShellFunds(address _token, uint256 _balance) external; + + function createColonyShell(uint256 _destinationChainId, bytes32 _salt) external; + function createColonyShellFromBridge(bytes32 _salt) external; } diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 60ee03649c..59ed14c65c 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -23,14 +23,15 @@ const { expect } = chai; chai.use(bnChai(web3.utils.BN)); const IColonyNetwork = artifacts.require("IColonyNetwork"); -const EtherRouterCreate3 = artifacts.require("EtherRouterCreate3"); +// const EtherRouterCreate3 = artifacts.require("EtherRouterCreate3"); const EtherRouter = artifacts.require("EtherRouter"); const IMetaColony = artifacts.require("IMetaColony"); const Token = artifacts.require("Token"); const IColony = artifacts.require("IColony"); -const ICreateX = artifacts.require("ICreateX"); +// const ICreateX = artifacts.require("ICreateX"); const IReputationMiningCycle = artifacts.require("IReputationMiningCycle"); const WormholeBridgeForColony = artifacts.require("WormholeBridgeForColony"); +// const { assert } = require("console"); const { setupBridging, deployBridge } = require("../../scripts/setup-bridging-contracts"); const { MINING_CYCLE_DURATION, CHALLENGE_RESPONSE_WINDOW_DURATION, ROOT_ROLE, CURR_VERSION, CREATEX_ADDRESS } = require("../../helpers/constants"); @@ -88,7 +89,9 @@ contract("Cross-chain", (accounts) => { const ethersForeignProvider = new ethers.providers.StaticJsonRpcProvider(foreignRpcUrl); const ethersForeignSigner = ethersForeignProvider.getSigner(); - const ethersHomeSigner = new ethers.providers.StaticJsonRpcProvider(homeRpcUrl).getSigner(); + const ethersHomeProvider = new ethers.providers.StaticJsonRpcProvider(homeRpcUrl); + const ethersHomeSigner = ethersHomeProvider.getSigner(); + // const ethersHomeSigner = new ethers.providers.StaticJsonRpcProvider(homeRpcUrl).getSigner(); const ethersForeignSigner2 = new ethers.providers.StaticJsonRpcProvider(foreignRpcUrl).getSigner(1); const ethersHomeSigner2 = new ethers.providers.StaticJsonRpcProvider(homeRpcUrl).getSigner(1); @@ -283,37 +286,34 @@ contract("Cross-chain", (accounts) => { const colonyCreationSalt = await homeColonyNetwork.getColonyCreationSalt({ blockTag: createXDeployEvent.blockNumber }); console.log("colony creation salt", colonyCreationSalt); - // Query CreateX on other network to prove this salt being used in the Create3 pattern - // from colonyNetwork would result in a colony at the same address + // Now have the colony request a deployment of a shell on the other chain. - // This test should be replaced by one that shows the colony on the home network can request - // a deployment on the other chain, and that the resulting address is the same. However, that - // functionality is not implemented yet, so we do a static call to CreateX, and get the address - // that would be created if colonyNetwork created a colony with that salt. - const createXForeign = new ethers.Contract(CREATEX_ADDRESS, ICreateX.abi, ethersForeignProvider); - const cnAsEr = await EtherRouterCreate3.at(homeColonyNetwork.address); + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); - const setOwnerData = cnAsEr.contract.methods.setOwner(foreignColonyNetwork.address).encodeABI(); + const deployedColony = new ethers.Contract(colonyAddress, IColony.abi, ethersHomeSigner); - const colonyAddressWithSalt = await createXForeign.callStatic["deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))"]( - colonyCreationSalt, - EtherRouterCreate3.bytecode, - setOwnerData, - [0, 0], - { from: foreignColonyNetwork.address }, - ); + tx = await deployedColony.createColonyShell(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); - expect(colonyAddressWithSalt).to.equal(colonyAddress); + await tx.wait(); + await p; - // Demonstrate that another address using that salt would not get the same address - const otherAddress = await createXForeign.callStatic["deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))"]( - colonyCreationSalt, - EtherRouterCreate3.bytecode, - setOwnerData, - [0, 0], - ); + // Did we deploy a shell colony on the foreign chain at the right address? Should have EtherRouter code... + const code = await ethersForeignProvider.getCode(deployedColony.address); + const codeExpected = await ethersHomeProvider.getCode(deployedColony.address); + expect(code).to.equal(codeExpected); + + // TODO: And the right resolver? - expect(otherAddress).to.not.equal(colonyAddress); + // TODO: Equivalent of this? + // Demonstrate that another address using that salt would not get the same address + // const otherAddress = await createXForeign.callStatic["deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))"]( + // colonyCreationSalt, + // EtherRouterCreate3.bytecode, + // setOwnerData, + // [0, 0], + // ); + + // expect(otherAddress).to.not.equal(colonyAddress); }); it("bridge data can be queried", async () => { From b44876bd3c1d0a7cf08c6e3f60a116dbb5cddf51 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Thu, 8 Aug 2024 14:38:49 +0100 Subject: [PATCH 06/72] Track tokens received on foreign chain --- contracts/bridging/IColonyBridge.sol | 3 +- .../{ColonyShell.sol => ShellColony.sol} | 18 +- contracts/bridging/ShellColonyNetwork.sol | 96 +++++++++ .../bridging/WormholeBridgeForColony.sol | 22 +- contracts/colony/Colony.sol | 4 +- contracts/colony/ColonyDataTypes.sol | 5 + contracts/colony/ColonyFunding.sol | 14 ++ contracts/colony/IColony.sol | 5 +- .../colonyNetwork/ColonyNetworkDeployer.sol | 15 +- .../colonyNetwork/ColonyNetworkMining.sol | 4 +- .../colonyNetwork/ColonyNetworkShells.sol | 26 +-- contracts/colonyNetwork/IColonyNetwork.sol | 5 +- docs/interfaces/icolony.md | 46 +++++ docs/interfaces/icolonynetwork.md | 15 +- hardhat.config.js | 6 + helpers/upgradable-contracts.js | 17 +- scripts/mockGuardianSpy.ts | 3 +- test/cross-chain/cross-chain.js | 193 ++++++++++++++---- 18 files changed, 410 insertions(+), 87 deletions(-) rename contracts/bridging/{ColonyShell.sol => ShellColony.sol} (77%) create mode 100644 contracts/bridging/ShellColonyNetwork.sol diff --git a/contracts/bridging/IColonyBridge.sol b/contracts/bridging/IColonyBridge.sol index 5a5d7eb470..3c59d8e70c 100644 --- a/contracts/bridging/IColonyBridge.sol +++ b/contracts/bridging/IColonyBridge.sol @@ -45,8 +45,9 @@ interface IColonyBridge { /// @notice Function to send a message to the colony bridge on another chain /// @param evmChainId The chain id to send the message to + /// @param destination The address of the contract on the other chain to send the message to /// @param payload The message payload /// @return bool Whether the message was sent successfully (to the best of the contract's knowledge, /// in terms of the underlying bridge implementation) - function sendMessage(uint256 evmChainId, bytes memory payload) external returns (bool); + function sendMessage(uint256 evmChainId, address destination, bytes memory payload) external returns (bool); } diff --git a/contracts/bridging/ColonyShell.sol b/contracts/bridging/ShellColony.sol similarity index 77% rename from contracts/bridging/ColonyShell.sol rename to contracts/bridging/ShellColony.sol index 1a19b863aa..acd93c5a0c 100644 --- a/contracts/bridging/ColonyShell.sol +++ b/contracts/bridging/ShellColony.sol @@ -25,13 +25,17 @@ import { DSAuth } from "./../../lib/dappsys/auth.sol"; import { ERC20Extended } from "./../common/ERC20Extended.sol"; import { Multicall } from "./../common/Multicall.sol"; import { IColonyNetwork } from "./../colonyNetwork/IColonyNetwork.sol"; +import { ShellColonyNetwork } from "./ShellColonyNetwork.sol"; -contract ColonyShell is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards { +contract ShellColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards { // Address of the Resolver contract used by EtherRouter for lookups and routing address resolver; // Storage slot 2 (from DSAuth there is authority and owner at storage slots 0 and 1 respectively) mapping(address => uint256) metatransactionNonces; + // Token address => known balance + mapping(address => uint256) tokenBalances; + function getMetatransactionNonce(address _user) public view override returns (uint256 _nonce) { return metatransactionNonces[_user]; } @@ -47,17 +51,25 @@ contract ColonyShell is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards // Public functions - function claimColonyShellFunds(address _token) public { + function claimTokens(address _token) public { uint256 balance = (_token == address(0x0)) ? address(this).balance : ERC20Extended(_token).balanceOf(address(this)); - IColonyNetwork(owner).sendClaimColonyShellFunds(_token, balance); + require(balance >= tokenBalances[_token], "colony-shell-token-bookkeeping-error"); + uint256 difference = balance - tokenBalances[_token]; + + tokenBalances[_token] = balance; + + bytes memory payload = abi.encodeWithSignature("recordClaimedFundsFromBridge(uint256,address,uint256)", block.chainid, _token, difference); + ShellColonyNetwork(owner).bridgeMessage(payload); emit ColonyFundsClaimed(_token, balance); } function transfer(address _token, address _user, uint256 _amount) public auth { + tokenBalances[_token] -= _amount; + require(ERC20Extended(_token).transfer(_user, _amount), "colony-shell-transfer-failed"); emit TransferMade(_token, _user, _amount); diff --git a/contracts/bridging/ShellColonyNetwork.sol b/contracts/bridging/ShellColonyNetwork.sol new file mode 100644 index 0000000000..4585d0b463 --- /dev/null +++ b/contracts/bridging/ShellColonyNetwork.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + This file is part of The Colony Network. + + The Colony Network is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The Colony Network is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with The Colony Network. If not, see . +*/ + +pragma solidity 0.8.25; +pragma experimental ABIEncoderV2; + +import { BasicMetaTransaction } from "./../common/BasicMetaTransaction.sol"; +import { CallWithGuards } from "../common/CallWithGuards.sol"; +import { DSAuth } from "./../../lib/dappsys/auth.sol"; +import { ERC20Extended } from "./../common/ERC20Extended.sol"; +import { Multicall } from "./../common/Multicall.sol"; +import { IColonyNetwork } from "./../colonyNetwork/IColonyNetwork.sol"; +import { EtherRouterCreate3 } from "./../common/EtherRouterCreate3.sol"; +import { ICreateX } from "./../../lib/createx/src/ICreateX.sol"; +import { EtherRouter } from "./../common/EtherRouter.sol"; +import { IColonyBridge } from "./IColonyBridge.sol"; + +contract ShellColonyNetwork is DSAuth, Multicall, CallWithGuards { + address resolver; // Storage slot 2 (from DSAuth there is authority and owner at storage slots 0 and 1 respectively) + + address constant CREATEX_ADDRESS = 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed; + + address public colonyBridgeAddress; + uint256 public homeChainId; + address public shellColonyResolverAddress; + mapping (address => bool) public shellColonies; + + /// @notice Event logged when the colony network has data about a bridge contract set. + /// @param bridgeAddress The address of the bridge contract that will be interacted with + event BridgeSet(address bridgeAddress); + + modifier self() { + require(address(this) == msgSender(), "colony-not-self"); + _; + } + + modifier onlyColony() { + require(shellColonies[msgSender()], "colony-network-caller-must-be-shell-colony"); + _; + } + + modifier onlyColonyBridge() { + require(msgSender() == colonyBridgeAddress, "colony-network-caller-must-be-colony-bridge"); + _; + } + + function setColonyBridgeAddress(address _bridgeAddress) public auth { + // TODO: Move this somewhere else to guard against unsupported chainids + // require(_chainId <= type(uint128).max, "colony-network-chainid-too-large"); + + colonyBridgeAddress = _bridgeAddress; + // TODO: Move this to where the first + + emit BridgeSet(_bridgeAddress); + } + + function setShellColonyResolverAddress(address _resolver) public auth { + shellColonyResolverAddress = _resolver; + } + + function setHomeChainId(uint256 _homeChainId) public auth { + homeChainId = _homeChainId; + } + + function createShellColonyFromBridge(bytes32 _salt) public onlyColonyBridge { + EtherRouter etherRouter = EtherRouter(payable(ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( + _salt, + type(EtherRouterCreate3).creationCode, + abi.encodeWithSignature("setOwner(address)", (address(this))), + ICreateX.Values(0, 0) + ))); + + shellColonies[address(etherRouter)] = true; + + etherRouter.setResolver(shellColonyResolverAddress); // ignore-swc-113 + } + + function bridgeMessage(bytes memory _payload) public onlyColony { + require(IColonyBridge(colonyBridgeAddress).sendMessage(homeChainId, msg.sender, _payload), "colony-network-bridge-message-failed"); + } +} \ No newline at end of file diff --git a/contracts/bridging/WormholeBridgeForColony.sol b/contracts/bridging/WormholeBridgeForColony.sol index c73e0a1bd6..55e34ee6bf 100644 --- a/contracts/bridging/WormholeBridgeForColony.sol +++ b/contracts/bridging/WormholeBridgeForColony.sol @@ -23,6 +23,7 @@ import { IColonyNetwork } from "../colonyNetwork/IColonyNetwork.sol"; import { IColonyBridge } from "./IColonyBridge.sol"; import { CallWithGuards } from "../common/CallWithGuards.sol"; import { DSAuth } from "../../lib/dappsys/auth.sol"; +import "hardhat/console.sol"; contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { address public colonyNetwork; @@ -93,6 +94,10 @@ contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { require(valid, reason); // Check came from a known colony bridge + console.log("emitterAddress", wormholeAddressToEVMAddress(wormholeMessage.emitterAddress)); + console.log("emitterChainId", wormholeMessage.emitterChainId); + console.log("colonyBridges[wormholeMessage.emitterChainId]", colonyBridges[wormholeMessage.emitterChainId]); + require( wormholeAddressToEVMAddress(wormholeMessage.emitterAddress) == colonyBridges[wormholeMessage.emitterChainId], @@ -104,24 +109,30 @@ contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { bytes memory payload = wormholeMessage.payload; // Strip off the chain id prefix, and make sure we are on that chain Id uint256 destinationChainId; + address destinationAddress; bytes memory payloadWithoutChainId; - (destinationChainId, payloadWithoutChainId) = abi.decode(payload, (uint256, bytes)); + (destinationChainId, destinationAddress, payloadWithoutChainId) = abi.decode(payload, (uint256, address, bytes)); + console.log("destinationChainId", destinationChainId); require(destinationChainId == block.chainid, "colony-bridge-destination-chain-id-mismatch"); - - // Make the call requested to the colony network - (bool success, bytes memory returndata) = callWithGuards(colonyNetwork, payloadWithoutChainId); + console.log("destinationAddress", destinationAddress); + console.log("payload:"); + console.logBytes(payloadWithoutChainId); + // Make the call requested to the destination address + (bool success, bytes memory returndata) = callWithGuards(destinationAddress, payloadWithoutChainId); // Note that this is not a require because returndata might not be a string, and if we try // to decode it we'll get a revert. if (!success) { revert(abi.decode(returndata, (string))); } + console.log("call successful"); } function sendMessage( uint256 _evmChainId, + address _destination, bytes memory _payload ) public onlyColonyNetwork returns (bool) { require(supportedEvmChainId(_evmChainId), "colony-bridge-not-known-chain"); @@ -132,7 +143,8 @@ contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { // For wormhole, we prefix the supplied payload with the _evmChainId // This is because wormhole is a generic bridge, and we need to tell it which chain to send to - try wormhole.publishMessage(0, abi.encode(_evmChainId, _payload), 0) { + // We also prefix with the destination chain Id, which we assume is the same as the sender. + try wormhole.publishMessage(0, abi.encode(_evmChainId, _destination, _payload), 0) { return true; } catch { return false; diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index 7d5d6840dc..6ded0057ad 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -324,8 +324,8 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); } - function createColonyShell(uint256 _destinationChainId, bytes32 _salt) public { - IColonyNetwork(colonyNetworkAddress).createColonyShell(_destinationChainId, _salt); + function createShellColony(uint256 _destinationChainId, bytes32 _salt) public { + IColonyNetwork(colonyNetworkAddress).createShellColony(_destinationChainId, _salt); } function getMetatransactionNonce(address _user) public view override returns (uint256 nonce) { diff --git a/contracts/colony/ColonyDataTypes.sol b/contracts/colony/ColonyDataTypes.sol index cf8267e228..f4961bfa5f 100755 --- a/contracts/colony/ColonyDataTypes.sol +++ b/contracts/colony/ColonyDataTypes.sol @@ -326,6 +326,9 @@ interface ColonyDataTypes { // Map any assigned payouts from this pot mapping (address => uint256) payouts; uint256 payoutsWeCannotMake; + + // Chainid => tokenAddress => balance + mapping (uint256 => mapping (address => uint256)) chainBalances; } struct Domain { @@ -436,6 +439,8 @@ interface ColonyDataTypes { /// @param paymentId Id of the payment event PaymentFinalized(address agent, uint256 indexed paymentId); + event ProxyColonyFundsClaimed(uint256 _chainId, address _token, uint256 _amount); + // Deprecated Task and Payment structs enum TaskRatings { None, Unsatisfactory, Satisfactory, Excellent } diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index fc2473f8e3..07e2e777d0 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -318,6 +318,20 @@ contract ColonyFunding is return fundingPots[_potId].balance[_token]; } + function getFundingPotProxyBalance(uint256 _potId, uint256 _chainId, address _token) + public + view + returns (uint256) + { + return fundingPots[_potId].chainBalances[_chainId][_token]; + } + + function recordClaimedFundsFromBridge(uint256 _chainId, address _token, uint256 _amount) public stoppable { + fundingPots[1].chainBalances[_chainId][_token] += _amount; + + emit ProxyColonyFundsClaimed(_chainId, _token, _amount); + } + function getFundingPotPayout(uint256 _potId, address _token) public view returns (uint256) { return fundingPots[_potId].payouts[_token]; } diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 7dfa2a40a8..79bc7e9e76 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -858,7 +858,10 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, /// @return uint256 amount Amount of the token that the domain can receive function getAllowedDomainReputationReceipt(uint256 _domainId) external view returns (uint256); - function createColonyShell(uint256 _destinationChainId, bytes32 _salt) external; + function recordClaimedFundsFromBridge(uint256 _chainId, address _token, uint256 _amount) external; + function getFundingPotProxyBalance(uint256 _potId, uint256 _chainId, address _token) external view returns (uint256); + + function createShellColony(uint256 _destinationChainId, bytes32 _salt) external; /// @notice Get the total amount of tokens `_token` minus amount reserved to be paid to the reputation and token holders as rewards. /// @param _token Address of the token, `0x0` value indicates Ether diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index 3e50875caf..aa13b77924 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -132,19 +132,10 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage { return (address(token), colonyAddress); } - function createColonyShell(uint256 _destinationChainId, bytes32 _salt) public calledByColony { + function createShellColony(uint256 _destinationChainId, bytes32 _salt) public calledByColony { // TODO: Check if the colony is allowed to use the salt - bytes memory payload = abi.encodeWithSignature("createColonyShellFromBridge(bytes32)", _salt); - IColonyBridge(colonyBridgeAddress).sendMessage(_destinationChainId, payload); - } - - function createColonyShellFromBridge(bytes32 _salt) public onlyColonyBridge { - ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( - _salt, - type(EtherRouterCreate3).creationCode, - abi.encodeWithSignature("setOwner(address)", (address(this))), - ICreateX.Values(0, 0) - ); + bytes memory payload = abi.encodeWithSignature("createShellColonyFromBridge(bytes32)", _salt); + IColonyBridge(colonyBridgeAddress).sendMessage(_destinationChainId, address(this), payload); } /** diff --git a/contracts/colonyNetwork/ColonyNetworkMining.sol b/contracts/colonyNetwork/ColonyNetworkMining.sol index cbb154903d..05fc167474 100644 --- a/contracts/colonyNetwork/ColonyNetworkMining.sol +++ b/contracts/colonyNetwork/ColonyNetworkMining.sol @@ -127,9 +127,9 @@ contract ColonyNetworkMining is ColonyNetworkStorage { ); // slither-disable-next-line unchecked-lowlevel - bool success = IColonyBridge(colonyBridgeAddress).sendMessage(_chainId, payload); + // bool success = IColonyBridge(colonyBridgeAddress).sendMessage(_chainId, payload); // We require success so estimation calls can tell us if bridging is going to work - require(success, "colony-mining-bridge-call-failed"); + // require(success, "colony-mining-bridge-call-failed"); } function setReputationRootHash( diff --git a/contracts/colonyNetwork/ColonyNetworkShells.sol b/contracts/colonyNetwork/ColonyNetworkShells.sol index abb3afa51d..432d7300b9 100644 --- a/contracts/colonyNetwork/ColonyNetworkShells.sol +++ b/contracts/colonyNetwork/ColonyNetworkShells.sol @@ -22,33 +22,33 @@ pragma experimental ABIEncoderV2; import { IColony } from "./../colony/IColony.sol"; import { Multicall } from "./../common/Multicall.sol"; import { ColonyNetworkStorage } from "./ColonyNetworkStorage.sol"; -import { ColonyShell } from "./../bridging/ColonyShell.sol"; +import { ShellColony } from "./../bridging/ShellColony.sol"; contract ColonyNetworkShells is ColonyNetworkStorage, Multicall { // To shells - function sendDeployColonyShell(bytes32 _salt) public calledByColony { - bytes memory payload = abi.encodeWithSignature("deployColonyShell(bytes32)", _salt); + function sendDeployShellColony(bytes32 _salt) public calledByColony { + bytes memory payload = abi.encodeWithSignature("deployShellColony(bytes32)", _salt); require(callThroughBridgeWithGuards(payload), "colony-network-shell-deploy-failed"); } - function colonyShellTransfer( + function ShellColonyTransfer( address _colony, address _token, address _user, uint256 _amount ) public onlyColonyBridge { - ColonyShell(_colony).transfer(_token, _user, _amount); + ShellColony(_colony).transfer(_token, _user, _amount); } - function sendColonyShellTransfer( + function sendShellColonyTransfer( address _token, address _user, uint256 _amount ) public calledByColony { bytes memory payload = abi.encodeWithSignature( - "colonyShellTransfer(address,address,address,uint256)", + "ShellColonyTransfer(address,address,address,uint256)", msgSender(), _token, _user, @@ -60,17 +60,9 @@ contract ColonyNetworkShells is ColonyNetworkStorage, Multicall { // From shells - function claimColonyShellFunds( - address _colony, - address _token, - uint256 _balance - ) public onlyColonyBridge { - IColony(_colony).claimColonyShellFunds(_token, _balance); - } - - function sendClaimColonyShellFunds(address _token, uint256 _balance) public calledByColony { + function sendClaimShellColonyFunds(address _token, uint256 _balance) public calledByColony { bytes memory payload = abi.encodeWithSignature( - "claimColonyShellFunds(address,address,uint256)", + "claimShellColonyFunds(address,address,uint256)", msgSender(), _token, _balance diff --git a/contracts/colonyNetwork/IColonyNetwork.sol b/contracts/colonyNetwork/IColonyNetwork.sol index dc01dab49e..a22af7bd9e 100644 --- a/contracts/colonyNetwork/IColonyNetwork.sol +++ b/contracts/colonyNetwork/IColonyNetwork.sol @@ -651,8 +651,7 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery, IBasicMetaTransac /// @notice Send the claimFunds transaction from the shell to the colony /// @param _token The token being held by the shell /// @param _balance The shell's current balance of the token - function sendClaimColonyShellFunds(address _token, uint256 _balance) external; + function sendClaimShellColonyFunds(address _token, uint256 _balance) external; - function createColonyShell(uint256 _destinationChainId, bytes32 _salt) external; - function createColonyShellFromBridge(bytes32 _salt) external; + function createShellColony(uint256 _destinationChainId, bytes32 _salt) external; } diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index a94bdf69af..6a5ca5605f 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -218,6 +218,19 @@ Claim the reward payout at `_payoutId`. User needs to provide their reputation a |siblings|bytes32[]|The siblings of the proof +### ▸ `createShellColony(uint256 _destinationChainId, bytes32 _salt)` + + + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_destinationChainId|uint256| +|_salt|bytes32| + + ### ▸ `deobligateStake(address _user, uint256 _domainId, uint256 _amount)` Deobligate the user some amount of tokens, releasing the stake. @@ -711,6 +724,25 @@ Get the assigned `_token` payouts of pot with id `_potId`. |---|---|---| |payout|uint256|Funding pot payout amount +### ▸ `getFundingPotProxyBalance(uint256 _potId, uint256 _chainId, address _token):uint256 uint256` + + + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_potId|uint256| +|_chainId|uint256| +|_token|address| + +**Return Parameters** + +|Name|Type|Description| +|---|---|---| +|uint256|uint256| + ### ▸ `getLocalSkill(uint256 localSkillId):LocalSkill localSkill` Get the local skill @@ -1287,6 +1319,20 @@ Get the owner of the contract |---|---|---| |owner|address|The owner of the contract +### ▸ `recordClaimedFundsFromBridge(uint256 _chainId, address _token, uint256 _amount)` + + + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_chainId|uint256| +|_token|address| +|_amount|uint256| + + ### ▸ `registerColonyLabel(string memory colonyName, string memory orbitdb)` Register colony's ENS label. diff --git a/docs/interfaces/icolonynetwork.md b/docs/interfaces/icolonynetwork.md index bd49525b72..1d976c2ece 100644 --- a/docs/interfaces/icolonynetwork.md +++ b/docs/interfaces/icolonynetwork.md @@ -268,6 +268,19 @@ Create the Meta Colony, same as a normal colony plus the root skill. |_tokenAddress|address|Address of the CLNY token +### ▸ `createShellColony(uint256 _destinationChainId, bytes32 _salt)` + + + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_destinationChainId|uint256| +|_salt|bytes32| + + ### ▸ `deployTokenAuthority(address _token, address _colony, address[] memory _allowedToTransfer):address _tokenAuthority` Called to deploy a token authority @@ -1074,7 +1087,7 @@ Used to track that a user is eligible to claim a reward |_amount|uint256|The amount of CLNY to be awarded -### ▸ `sendClaimColonyShellFunds(address _token, uint256 _balance)` +### ▸ `sendClaimShellColonyFunds(address _token, uint256 _balance)` Send the claimFunds transaction from the shell to the colony diff --git a/hardhat.config.js b/hardhat.config.js index dae5f37394..9a16594770 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -81,6 +81,12 @@ task("test", "Run tests").setAction(async () => { } }); +task("ensureCreateXDeployed", "Ensure CreateX is deployed").setAction(async () => { + const { deployCreateXIfNeeded } = require("./helpers/test-helper"); // eslint-disable-line global-require + + await deployCreateXIfNeeded(); +}); + task("node", "Run a node, and output ganache-accounts.json for backwards-compatability").setAction(async () => { const ganacheAccounts = { addresses: {}, private_keys: {} }; // eslint-disable-next-line no-restricted-syntax diff --git a/helpers/upgradable-contracts.js b/helpers/upgradable-contracts.js index f76e9cc1a8..9992304012 100644 --- a/helpers/upgradable-contracts.js +++ b/helpers/upgradable-contracts.js @@ -5,6 +5,7 @@ const fs = require("fs"); function readArtifact(contractDir, contractName) { const artifactPath = `./artifacts/contracts/${contractDir}/${contractName}.sol/${contractName}.json`; + console.log(artifactPath); try { return JSON.parse(fs.readFileSync(artifactPath, "utf8")); } catch (err) { @@ -85,7 +86,10 @@ exports.setupEtherRouter = async function setupEtherRouter(contractDir, interfac const sig = `${fName}(${iAbi[i].inputs.map((parameter) => parameter.type).join(",")})`; const address = functionsToResolve[fName].definedIn; try { - await resolver.register(sig, address); + const txOrReceipt = await resolver.register(sig, address); + if (txOrReceipt.wait) { + await txOrReceipt.wait(); + } } catch (err) { console.log(err); throw new Error(`${sig} could not be registered. Is it defined?`); @@ -197,3 +201,14 @@ exports.setupENSRegistrar = async function setupENSRegistrar(colonyNetwork, ensR await ensRegistry.setSubnodeOwner(rootNode, USER_HASH, colonyNetwork.address); await ensRegistry.setSubnodeOwner(rootNode, COLONY_HASH, colonyNetwork.address); }; + +exports.setupShellColonyNetwork = async function setupShellColonyNetwork(etherRouter, shellColonyNetwork, resolver) { + const deployedImplementations = {}; + deployedImplementations.ShellColonyNetwork = shellColonyNetwork.address; + + await exports.setupEtherRouter("bridging", "ShellColonyNetwork", deployedImplementations, resolver); + const txOrReceipt = await etherRouter.setResolver(resolver.address); + if (txOrReceipt.wait) { + await txOrReceipt.wait(); + } +}; diff --git a/scripts/mockGuardianSpy.ts b/scripts/mockGuardianSpy.ts index 2cd06daf50..140ddeb4b4 100644 --- a/scripts/mockGuardianSpy.ts +++ b/scripts/mockGuardianSpy.ts @@ -277,7 +277,8 @@ class MockGuardianSpy { this.bridgingPromiseCount -= 1; if (this.bridgingPromiseCount === 0) { - this.resolveBridgingPromise(tx); + const receipt = await bridge.provider.getTransactionReceipt(tx.hash); + this.resolveBridgingPromise(receipt); } if (this.locked) { this.locked = false; diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 59ed14c65c..3d04620746 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -23,14 +23,18 @@ const { expect } = chai; chai.use(bnChai(web3.utils.BN)); const IColonyNetwork = artifacts.require("IColonyNetwork"); -// const EtherRouterCreate3 = artifacts.require("EtherRouterCreate3"); +const EtherRouterCreate3 = artifacts.require("EtherRouterCreate3"); const EtherRouter = artifacts.require("EtherRouter"); const IMetaColony = artifacts.require("IMetaColony"); +const Resolver = artifacts.require("Resolver"); const Token = artifacts.require("Token"); const IColony = artifacts.require("IColony"); -// const ICreateX = artifacts.require("ICreateX"); +const ICreateX = artifacts.require("ICreateX"); const IReputationMiningCycle = artifacts.require("IReputationMiningCycle"); const WormholeBridgeForColony = artifacts.require("WormholeBridgeForColony"); +const ShellColonyNetwork = artifacts.require("ShellColonyNetwork"); +const ShellColony = artifacts.require("ShellColony"); +const MetaTxToken = artifacts.require("MetaTxToken"); // const { assert } = require("console"); const { setupBridging, deployBridge } = require("../../scripts/setup-bridging-contracts"); @@ -38,6 +42,7 @@ const { MINING_CYCLE_DURATION, CHALLENGE_RESPONSE_WINDOW_DURATION, ROOT_ROLE, CU const { forwardTime, checkErrorRevertEthers, revert, snapshot, evmChainIdToWormholeChainId } = require("../../helpers/test-helper"); const ReputationMinerTestWrapper = require("../../packages/reputation-miner/test/ReputationMinerTestWrapper"); const { TruffleLoader } = require("../../packages/package-utils"); +const { setupShellColonyNetwork, setupEtherRouter } = require("../../helpers/upgradable-contracts"); const UINT256_MAX_ETHERS = ethers.BigNumber.from(2).pow(256).sub(1); @@ -106,8 +111,13 @@ contract("Cross-chain", (accounts) => { tx = await bridge.setColonyNetworkAddress(foreignColonyNetwork.address); await tx.wait(); - tx = await foreignMetacolony.setColonyBridgeAddress(foreignColonyBridgeForColony); + // TODO: Figure out a better way of setting / controlling this? + console.log("setting foreign colony bridge address", foreignColonyBridgeForColony); + tx = await foreignColonyNetwork.setColonyBridgeAddress(foreignColonyBridgeForColony); await tx.wait(); + tx = await foreignColonyNetwork.setHomeChainId(homeChainId); + await tx.wait(); + console.log("done"); } async function setHomeBridgeData(homeColonyBridgeAddressForColony) { @@ -145,11 +155,49 @@ contract("Cross-chain", (accounts) => { foreignChainId = await ethersForeignSigner.provider.send("eth_chainId", []); wormholeForeignChainId = evmChainIdToWormholeChainId(foreignChainId); - // Deploy colonyNetwork to whichever chain truffle hasn't already deployed to. + // Deploy shell colonyNetwork to whichever chain truffle hasn't already deployed to. try { - const nonHardhatChainId = process.env.HARDHAT_FOREIGN === "true" ? homeChainId : foreignChainId; + await exec(`CHAIN_ID=${parseInt(foreignChainId, 16)} npx hardhat ensureCreateXDeployed --network development2`); + + const createX = await new ethers.Contract(CREATEX_ADDRESS, ICreateX.abi, ethersForeignSigner); + + // This is a fake instance of an etherRouter, just so we can call encodeABs + const fakeEtherRouter = await EtherRouterCreate3.at(CREATEX_ADDRESS); + const setOwnerData = fakeEtherRouter.contract.methods.setOwner(accounts[0]).encodeABI(); + + const tx = await createX["deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))"]( + `0xb77d57f4959eafa0339424b83fcfaf9c15407461005e95d52076387600e2c1e9`, + EtherRouterCreate3.bytecode, + setOwnerData, + [0, 0], + { from: accounts[0] }, + ); + + const receipt = await tx.wait(); + + const etherRouter = await new ethers.Contract( + receipt.events.filter((log) => log.event === "ContractCreation")[0].args.newContract, + EtherRouter.abi, + ethersForeignSigner, + ); + let resolver = await new ethers.ContractFactory(Resolver.abi, Resolver.bytecode, ethersForeignSigner).deploy(); + const shellColonyNetworkImplementation = await new ethers.ContractFactory( + ShellColonyNetwork.abi, + ShellColonyNetwork.bytecode, + ethersForeignSigner, + ).deploy(); + + await setupShellColonyNetwork(etherRouter, shellColonyNetworkImplementation, resolver); + console.log("**** shell colony network set up"); + + // Set up the resolver for shell colonies + resolver = await new ethers.ContractFactory(Resolver.abi, Resolver.bytecode, ethersForeignSigner).deploy(); + const shellColonyImplementation = await new ethers.ContractFactory(ShellColony.abi, ShellColony.bytecode, ethersForeignSigner).deploy(); + + await setupEtherRouter("bridging", "ShellColony", { ShellColony: shellColonyImplementation.address }, resolver); + const shellColonyNetwork = new ethers.Contract(etherRouter.address, ShellColonyNetwork.abi, ethersForeignSigner); - await exec(`CHAIN_ID=${parseInt(nonHardhatChainId, 16)} npx hardhat deploy --network development2`); + await shellColonyNetwork.setShellColonyResolverAddress(resolver.address); } catch (err) { console.log(err); process.exit(1); @@ -164,7 +212,7 @@ contract("Cross-chain", (accounts) => { homeColonyNetwork = await new ethers.Contract(homeEtherRouterAddress, IColonyNetwork.abi, ethersHomeSigner); const foreignEtherRouterAddress = homeEtherRouterAddress; - foreignColonyNetwork = await new ethers.Contract(foreignEtherRouterAddress, IColonyNetwork.abi, ethersForeignSigner); + foreignColonyNetwork = await new ethers.Contract(foreignEtherRouterAddress, ShellColonyNetwork.abi, ethersForeignSigner); }); beforeEach(async () => { @@ -180,9 +228,11 @@ contract("Cross-chain", (accounts) => { tx = await homeBridge.setBridgeEnabled(true); await tx.wait(); - const foreignMCAddress = await foreignColonyNetwork.getMetaColony(); - foreignMetacolony = await new ethers.Contract(foreignMCAddress, IMetaColony.abi, ethersForeignSigner); + // const foreignMCAddress = await foreignColonyNetwork.getMetaColony(); + // foreignMetacolony = await new ethers.Contract(foreignMCAddress, IMetaColony.abi, ethersForeignSigner); + console.log("get mc"); const homeMCAddress = await homeColonyNetwork.getMetaColony(); + console.log("got mc"); homeMetacolony = await new ethers.Contract(homeMCAddress, IMetaColony.abi, ethersHomeSigner); await setForeignBridgeData(foreignColonyBridge.address); @@ -190,15 +240,15 @@ contract("Cross-chain", (accounts) => { // Bridge over skills that have been created on the foreign chain - const latestSkillId = await foreignColonyNetwork.getSkillCount(); - const alreadyBridged = await homeColonyNetwork.getBridgedSkillCounts(foreignChainId); - for (let i = alreadyBridged.add(1); i <= latestSkillId; i = i.add(1)) { - const p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await foreignColonyNetwork.bridgeSkillIfNotMiningChain(i); - await tx.wait(); - await p; - } - + // const latestSkillId = await foreignColonyNetwork.getSkillCount(); + // const alreadyBridged = await homeColonyNetwork.getBridgedSkillCounts(foreignChainId); + // for (let i = alreadyBridged.add(1); i <= latestSkillId; i = i.add(1)) { + // const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + // tx = await foreignColonyNetwork.bridgeSkillIfNotMiningChain(i); + // await tx.wait(); + // await p; + // } + console.log("setting up mining client "); // Set up mining client client = new ReputationMinerTestWrapper({ loader: contractLoader, @@ -209,6 +259,8 @@ contract("Cross-chain", (accounts) => { await client.initialise(homeColonyNetwork.address); + console.log("initialised"); + await forwardTime(MINING_CYCLE_DURATION + CHALLENGE_RESPONSE_WINDOW_DURATION, undefined, web3HomeProvider); await client.addLogContentsToReputationTree(); await client.submitRootHash(); @@ -222,9 +274,9 @@ contract("Cross-chain", (accounts) => { // Set up a colony on the home chain. That may or may not be the truffle chain... homeColony = await setupColony(homeColonyNetwork); - const p = guardianSpy.getPromiseForNextBridgedTransaction(2); - foreignColony = await setupColony(foreignColonyNetwork); - await p; + // const p = bridgeMonitor.getPromiseForNextBridgedTransaction(2); + // foreignColony = await setupColony(foreignColonyNetwork); + // await p; }); async function setupColony(colonyNetworkEthers) { @@ -254,16 +306,18 @@ contract("Cross-chain", (accounts) => { }); describe("administrating cross-network bridges", async () => { - it("colonyNetwork should have the same address on each chain", async () => { + it.only("colonyNetwork should have the same address on each chain", async () => { expect(homeColonyNetwork.address).to.equal(foreignColonyNetwork.address); // Check we have colony Network there - this equality is expected because of how we set up the addresses const homeVersionResolver = await homeColonyNetwork.getColonyVersionResolver(CURR_VERSION); - const foreignVersionResolver = await foreignColonyNetwork.getColonyVersionResolver(CURR_VERSION); + console.log(foreignColonyNetwork.address); + console.log(await ethersForeignProvider.getBlockNumber()); + const shellColonyResolver = await foreignColonyNetwork.shellColonyResolverAddress(); expect(homeVersionResolver).to.not.equal(ADDRESS_ZERO); - expect(foreignVersionResolver).to.not.equal(ADDRESS_ZERO); + expect(shellColonyResolver).to.not.equal(ADDRESS_ZERO); }); - it("colonies deployed on different chains can have same address", async () => { + it.only("colonies deployed on different chains can have same address", async () => { // Deploy a colony only on one chain, so that normal contract creations wouldn't have the same address await setupColony(homeColonyNetwork); @@ -292,7 +346,7 @@ contract("Cross-chain", (accounts) => { const deployedColony = new ethers.Contract(colonyAddress, IColony.abi, ethersHomeSigner); - tx = await deployedColony.createColonyShell(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); + tx = await deployedColony.createShellColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); await tx.wait(); await p; @@ -302,18 +356,12 @@ contract("Cross-chain", (accounts) => { const codeExpected = await ethersHomeProvider.getCode(deployedColony.address); expect(code).to.equal(codeExpected); - // TODO: And the right resolver? + const colonyAsEtherRouter = new ethers.Contract(deployedColony.address, EtherRouter.abi, ethersForeignSigner); + const resolverAddress = await colonyAsEtherRouter.resolver(); - // TODO: Equivalent of this? - // Demonstrate that another address using that salt would not get the same address - // const otherAddress = await createXForeign.callStatic["deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))"]( - // colonyCreationSalt, - // EtherRouterCreate3.bytecode, - // setOwnerData, - // [0, 0], - // ); + const expectedResolver = await foreignColonyNetwork.shellColonyResolverAddress(); - // expect(otherAddress).to.not.equal(colonyAddress); + expect(resolverAddress).to.equal(expectedResolver); }); it("bridge data can be queried", async () => { @@ -450,7 +498,7 @@ contract("Cross-chain", (accounts) => { }); }); - describe("when adding skills on another chain", async () => { + describe.skip("when adding skills on another chain", async () => { it("can create a skill on another chain and it's reflected on the home chain", async () => { // See skills on home chain const beforeCount = await homeColonyNetwork.getBridgedSkillCounts("0x0fd5c9ed"); @@ -674,7 +722,7 @@ contract("Cross-chain", (accounts) => { }); }); - describe("while earning reputation on another chain", async () => { + describe.skip("while earning reputation on another chain", async () => { it("reputation awards are ultimately reflected", async () => { let p = guardianSpy.getPromiseForNextBridgedTransaction(); // Emit reputation @@ -1118,6 +1166,75 @@ contract("Cross-chain", (accounts) => { }); }); + describe("collecting and paying out tokens on another chain", async () => { + let foreignToken; + let colony; + let shellColony; + beforeEach(async () => { + colony = await setupColony(homeColonyNetwork); + + const events = await homeColonyNetwork.queryFilter(homeColonyNetwork.filters.ColonyAdded()); + // homeColonyNetwork.fil + // Deploy a proxy colony on the foreign network + + const colonyCreationSalt = await homeColonyNetwork.getColonyCreationSalt({ blockTag: events[events.length - 1].blockNumber }); + + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + + const tx = await colony.createShellColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); + await tx.wait(); + + await p; + shellColony = new ethers.Contract(colony.address, ShellColony.abi, ethersForeignSigner); + // Deploy a token on the foreign network + + const tokenFactory = new ethers.ContractFactory(MetaTxToken.abi, MetaTxToken.bytecode, ethersForeignSigner); + foreignToken = await tokenFactory.deploy("Test Token", "TT", 18); + }); + + it.only("Can track tokens received on the foreign chain", async () => { + const tokenAmount = ethers.utils.parseEther("100"); + + let tx = await foreignToken["mint(address,uint256)"](shellColony.address, tokenAmount); + await tx.wait(); + + // Claim on the foreign chain + + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + + tx = await shellColony.claimTokens(foreignToken.address); + await tx.wait(); + + let receipt = await p; + expect(receipt.status).to.equal(1); + + // Check bookkeeping on the home chain + + const balance = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); + }); + + it.skip("Can track tokens sent on the foreign chain", async () => { + const tokenAmount = ethers.utils.parseEther("100"); + + let tx = await foreignToken["mint(address,uint256)"](shellColony.address, tokenAmount); + await tx.wait(); + + // Claim on the foreign chain + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + tx = await shellColony.claimTokens(foreignToken.address); + await tx.wait(); + await p; + + + // Make a payment that pays out 30 + + // Check bookkeeping on the home chain + + // Check actually paid on foreign chain + }); + }); + describe("bridge functions are secure", async () => { it("only the configured colonyNetwork can call `sendMessage`", async () => { const tx = await foreignColonyBridge.sendMessage(1, "0x00000000", { gasLimit: 1000000 }); From a67c1fccfad30547c607d9009d476ae7cc4ef589 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Thu, 8 Aug 2024 17:10:02 +0100 Subject: [PATCH 07/72] Give pots ability to track tokens on other chains --- contracts/bridging/IColonyBridge.sol | 6 +- contracts/bridging/ShellColony.sol | 7 +- contracts/bridging/ShellColonyNetwork.sol | 25 +- .../bridging/WormholeBridgeForColony.sol | 19 +- contracts/colony/ColonyDataTypes.sol | 3 + contracts/colony/ColonyExpenditure.sol | 8 - contracts/colony/ColonyFunding.sol | 300 +++++++++++++----- contracts/colony/ColonyStorage.sol | 3 + contracts/colony/IColony.sol | 6 +- 9 files changed, 274 insertions(+), 103 deletions(-) diff --git a/contracts/bridging/IColonyBridge.sol b/contracts/bridging/IColonyBridge.sol index 3c59d8e70c..7067e719e7 100644 --- a/contracts/bridging/IColonyBridge.sol +++ b/contracts/bridging/IColonyBridge.sol @@ -49,5 +49,9 @@ interface IColonyBridge { /// @param payload The message payload /// @return bool Whether the message was sent successfully (to the best of the contract's knowledge, /// in terms of the underlying bridge implementation) - function sendMessage(uint256 evmChainId, address destination, bytes memory payload) external returns (bool); + function sendMessage( + uint256 evmChainId, + address destination, + bytes memory payload + ) external returns (bool); } diff --git a/contracts/bridging/ShellColony.sol b/contracts/bridging/ShellColony.sol index acd93c5a0c..23e6c5c925 100644 --- a/contracts/bridging/ShellColony.sol +++ b/contracts/bridging/ShellColony.sol @@ -61,7 +61,12 @@ contract ShellColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards tokenBalances[_token] = balance; - bytes memory payload = abi.encodeWithSignature("recordClaimedFundsFromBridge(uint256,address,uint256)", block.chainid, _token, difference); + bytes memory payload = abi.encodeWithSignature( + "recordClaimedFundsFromBridge(uint256,address,uint256)", + block.chainid, + _token, + difference + ); ShellColonyNetwork(owner).bridgeMessage(payload); emit ColonyFundsClaimed(_token, balance); diff --git a/contracts/bridging/ShellColonyNetwork.sol b/contracts/bridging/ShellColonyNetwork.sol index 4585d0b463..352902a177 100644 --- a/contracts/bridging/ShellColonyNetwork.sol +++ b/contracts/bridging/ShellColonyNetwork.sol @@ -38,7 +38,7 @@ contract ShellColonyNetwork is DSAuth, Multicall, CallWithGuards { address public colonyBridgeAddress; uint256 public homeChainId; address public shellColonyResolverAddress; - mapping (address => bool) public shellColonies; + mapping(address => bool) public shellColonies; /// @notice Event logged when the colony network has data about a bridge contract set. /// @param bridgeAddress The address of the bridge contract that will be interacted with @@ -78,12 +78,16 @@ contract ShellColonyNetwork is DSAuth, Multicall, CallWithGuards { } function createShellColonyFromBridge(bytes32 _salt) public onlyColonyBridge { - EtherRouter etherRouter = EtherRouter(payable(ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( - _salt, - type(EtherRouterCreate3).creationCode, - abi.encodeWithSignature("setOwner(address)", (address(this))), - ICreateX.Values(0, 0) - ))); + EtherRouter etherRouter = EtherRouter( + payable( + ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( + _salt, + type(EtherRouterCreate3).creationCode, + abi.encodeWithSignature("setOwner(address)", (address(this))), + ICreateX.Values(0, 0) + ) + ) + ); shellColonies[address(etherRouter)] = true; @@ -91,6 +95,9 @@ contract ShellColonyNetwork is DSAuth, Multicall, CallWithGuards { } function bridgeMessage(bytes memory _payload) public onlyColony { - require(IColonyBridge(colonyBridgeAddress).sendMessage(homeChainId, msg.sender, _payload), "colony-network-bridge-message-failed"); + require( + IColonyBridge(colonyBridgeAddress).sendMessage(homeChainId, msg.sender, _payload), + "colony-network-bridge-message-failed" + ); } -} \ No newline at end of file +} diff --git a/contracts/bridging/WormholeBridgeForColony.sol b/contracts/bridging/WormholeBridgeForColony.sol index 55e34ee6bf..546b7523f2 100644 --- a/contracts/bridging/WormholeBridgeForColony.sol +++ b/contracts/bridging/WormholeBridgeForColony.sol @@ -23,7 +23,6 @@ import { IColonyNetwork } from "../colonyNetwork/IColonyNetwork.sol"; import { IColonyBridge } from "./IColonyBridge.sol"; import { CallWithGuards } from "../common/CallWithGuards.sol"; import { DSAuth } from "../../lib/dappsys/auth.sol"; -import "hardhat/console.sol"; contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { address public colonyNetwork; @@ -94,9 +93,6 @@ contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { require(valid, reason); // Check came from a known colony bridge - console.log("emitterAddress", wormholeAddressToEVMAddress(wormholeMessage.emitterAddress)); - console.log("emitterChainId", wormholeMessage.emitterChainId); - console.log("colonyBridges[wormholeMessage.emitterChainId]", colonyBridges[wormholeMessage.emitterChainId]); require( wormholeAddressToEVMAddress(wormholeMessage.emitterAddress) == @@ -112,22 +108,23 @@ contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { address destinationAddress; bytes memory payloadWithoutChainId; - (destinationChainId, destinationAddress, payloadWithoutChainId) = abi.decode(payload, (uint256, address, bytes)); + (destinationChainId, destinationAddress, payloadWithoutChainId) = abi.decode( + payload, + (uint256, address, bytes) + ); - console.log("destinationChainId", destinationChainId); require(destinationChainId == block.chainid, "colony-bridge-destination-chain-id-mismatch"); - console.log("destinationAddress", destinationAddress); - console.log("payload:"); - console.logBytes(payloadWithoutChainId); // Make the call requested to the destination address - (bool success, bytes memory returndata) = callWithGuards(destinationAddress, payloadWithoutChainId); + (bool success, bytes memory returndata) = callWithGuards( + destinationAddress, + payloadWithoutChainId + ); // Note that this is not a require because returndata might not be a string, and if we try // to decode it we'll get a revert. if (!success) { revert(abi.decode(returndata, (string))); } - console.log("call successful"); } function sendMessage( diff --git a/contracts/colony/ColonyDataTypes.sol b/contracts/colony/ColonyDataTypes.sol index f4961bfa5f..43551f77b3 100755 --- a/contracts/colony/ColonyDataTypes.sol +++ b/contracts/colony/ColonyDataTypes.sol @@ -329,6 +329,9 @@ interface ColonyDataTypes { // Chainid => tokenAddress => balance mapping (uint256 => mapping (address => uint256)) chainBalances; + + // Chainid => tokenAddress => payouts + mapping (uint256 => mapping (address => uint256)) chainPayouts; } struct Domain { diff --git a/contracts/colony/ColonyExpenditure.sol b/contracts/colony/ColonyExpenditure.sol index 8019e1e6e3..0b10a7d360 100644 --- a/contracts/colony/ColonyExpenditure.sol +++ b/contracts/colony/ColonyExpenditure.sol @@ -336,14 +336,6 @@ contract ColonyExpenditure is ColonyStorage { expenditureSlot = expenditureSlots[_id][_slot]; } - function getExpenditureSlotPayout( - uint256 _id, - uint256 _slot, - address _token - ) public view returns (uint256) { - return expenditureSlotPayouts[_id][_slot][_token]; - } - // Internal functions bool constant MAPPING = false; diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index 07e2e777d0..b81c35f45f 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -56,7 +56,7 @@ contract ColonyFunding is "colony-invalid-domain-inheritance" ); - moveFundsBetweenPotsFunctionality(_fromPot, _toPot, _amount, _token); + moveFundsBetweenPotsFunctionality(_fromPot, _toPot, _amount, block.chainid, _token); } function moveFundsBetweenPots( @@ -75,7 +75,7 @@ contract ColonyFunding is authDomain(_permissionDomainId, _toChildSkillIndex, getDomainFromFundingPot(_toPot)) validFundingTransfer(_fromPot, _toPot) { - moveFundsBetweenPotsFunctionality(_fromPot, _toPot, _amount, _token); + moveFundsBetweenPotsFunctionality(_fromPot, _toPot, _amount, block.chainid, _token); } function claimColonyFunds(address _token) public stoppable { @@ -86,13 +86,13 @@ contract ColonyFunding is // It's ether toClaim = (address(this).balance - nonRewardPotsTotal[_token]) - - fundingPots[0].balance[_token]; + getFundingPotBalance(0, _token); } else { // Assume it's an ERC 20 token. ERC20Extended targetToken = ERC20Extended(_token); toClaim = (targetToken.balanceOf(address(this)) - nonRewardPotsTotal[_token]) - - fundingPots[0].balance[_token]; // ignore-swc-123 + getFundingPotBalance(0, _token); // ignore-swc-123 } feeToPay = toClaim / getRewardInverse(); // ignore-swc-110 . This variable is set when the colony is @@ -101,8 +101,18 @@ contract ColonyFunding is // able to do with recovery mode. remainder = toClaim - feeToPay; nonRewardPotsTotal[_token] += remainder; - fundingPots[1].balance[_token] += remainder; - fundingPots[0].balance[_token] += feeToPay; + setFundingPotBalance( + 1, + block.chainid, + _token, + getFundingPotBalance(1, block.chainid, _token) + remainder + ); + setFundingPotBalance( + 0, + block.chainid, + _token, + getFundingPotBalance(0, block.chainid, _token) + feeToPay + ); emit ColonyFundsClaimed(msgSender(), _token, feeToPay, remainder); } @@ -199,7 +209,7 @@ contract ColonyFunding is address _token, uint256[] memory _amounts ) public stoppable expenditureDraft(_id) expenditureOnlyOwner(_id) { - setExpenditurePayoutsInternal(_id, _slots, _token, _amounts); + setExpenditurePayoutsInternal(_id, _slots, block.chainid, _token, _amounts); } /// @notice For arbitrators to update payouts with one token and one slot @@ -220,7 +230,7 @@ contract ColonyFunding is slots[0] = _slot; uint256[] memory amounts = new uint256[](1); amounts[0] = _amount; - setExpenditurePayoutsInternal(_id, slots, _token, amounts); + setExpenditurePayoutsInternal(_id, slots, block.chainid, _token, amounts); } /// @notice For owners to update payouts with one token and one slot @@ -234,15 +244,20 @@ contract ColonyFunding is slots[0] = _slot; uint256[] memory amounts = new uint256[](1); amounts[0] = _amount; - setExpenditurePayoutsInternal(_id, slots, _token, amounts); + setExpenditurePayoutsInternal(_id, slots, block.chainid, _token, amounts); } int256 constant MAX_PAYOUT_MODIFIER = int256(WAD); int256 constant MIN_PAYOUT_MODIFIER = -int256(WAD); + function claimExpenditurePayout(uint256 _id, uint256 _slot, address _token) public stoppable { + claimExpenditurePayout(_id, _slot, block.chainid, _token); + } + function claimExpenditurePayout( uint256 _id, uint256 _slot, + uint256 _chainId, address _token ) public stoppable expenditureFinalized(_id) { Expenditure storage expenditure = expenditures[_id]; @@ -258,25 +273,35 @@ contract ColonyFunding is "colony-expenditure-cannot-claim" ); - FundingPot storage fundingPot = fundingPots[expenditure.fundingPotId]; - assert(fundingPot.balance[_token] >= fundingPot.payouts[_token]); + uint256 fundingPotId = expenditure.fundingPotId; + assert( + getFundingPotBalance(fundingPotId, _chainId, _token) >= + getFundingPotPayout(fundingPotId, _chainId, _token) + ); - uint256 initialPayout = expenditureSlotPayouts[_id][_slot][_token]; - delete expenditureSlotPayouts[_id][_slot][_token]; + uint256 repPayout; + uint256 tokenPayout; - int256 payoutModifier = imin( - imax(slot.payoutModifier, MIN_PAYOUT_MODIFIER), - MAX_PAYOUT_MODIFIER - ); - uint256 payoutScalar = uint256(payoutModifier + int256(WAD)); + { + uint256 initialPayout = getExpenditureSlotPayout(_id, _slot, _chainId, _token); + setExpenditureSlotPayout(_id, _slot, _chainId, _token, 0); - uint256 repPayout = wmul(initialPayout, payoutScalar); - uint256 tokenPayout = min(initialPayout, repPayout); - uint256 tokenSurplus = initialPayout - tokenPayout; + int256 payoutModifier = imin( + imax(slot.payoutModifier, MIN_PAYOUT_MODIFIER), + MAX_PAYOUT_MODIFIER + ); + uint256 payoutScalar = uint256(payoutModifier + int256(WAD)); + repPayout = wmul(initialPayout, payoutScalar); + tokenPayout = min(initialPayout, repPayout); + uint256 tokenSurplus = initialPayout - tokenPayout; - // Deduct any surplus from the outstanding payouts (for payoutScalars < 1) - if (tokenSurplus > 0) { - fundingPot.payouts[_token] -= tokenSurplus; + // Deduct any surplus from the outstanding payouts (for payoutScalars < 1) + + if (tokenSurplus > 0) { + // UNCOMMENT + uint256 oldFundingPotPayout = getFundingPotPayout(fundingPotId, _chainId, _token); + setFundingPotPayout(fundingPotId, _chainId, _token, oldFundingPotPayout - tokenSurplus); + } } // Process reputation updates if internal token @@ -300,6 +325,7 @@ contract ColonyFunding is // Finish the payout uint256 payoutMinusFee = processPayout( expenditure.fundingPotId, + _chainId, _token, tokenPayout, slot.recipient @@ -315,25 +341,29 @@ contract ColonyFunding is } function getFundingPotBalance(uint256 _potId, address _token) public view returns (uint256) { - return fundingPots[_potId].balance[_token]; + return getFundingPotBalance(_potId, block.chainid, _token); } - function getFundingPotProxyBalance(uint256 _potId, uint256 _chainId, address _token) - public - view - returns (uint256) - { + function getFundingPotProxyBalance( + uint256 _potId, + uint256 _chainId, + address _token + ) public view returns (uint256) { return fundingPots[_potId].chainBalances[_chainId][_token]; } - function recordClaimedFundsFromBridge(uint256 _chainId, address _token, uint256 _amount) public stoppable { + function recordClaimedFundsFromBridge( + uint256 _chainId, + address _token, + uint256 _amount + ) public stoppable { fundingPots[1].chainBalances[_chainId][_token] += _amount; emit ProxyColonyFundsClaimed(_chainId, _token, _amount); } function getFundingPotPayout(uint256 _potId, address _token) public view returns (uint256) { - return fundingPots[_potId].payouts[_token]; + return getFundingPotPayout(_potId, block.chainid, _token); } function getFundingPot( @@ -380,19 +410,30 @@ contract ColonyFunding is uint256 _fromPot, uint256 _toPot, uint256 _amount, + uint256 _chainId, address _token ) internal { FundingPot storage fromPot = fundingPots[_fromPot]; FundingPot storage toPot = fundingPots[_toPot]; - fromPot.balance[_token] -= _amount; - toPot.balance[_token] += _amount; + setFundingPotBalance( + _fromPot, + _chainId, + _token, + getFundingPotBalance(_fromPot, _chainId, _token) - _amount + ); + setFundingPotBalance( + _toPot, + _chainId, + _token, + getFundingPotBalance(_toPot, _chainId, _token) + _amount + ); - if (_fromPot == 1) { + if (_fromPot == 1 && _chainId == block.chainid) { // If we're moving from the root pot, then check we haven't dropped below what we need // to cover any approvals that we've made. require( - fromPot.balance[_token] >= tokenApprovalTotals[_token], + getFundingPotBalance(_fromPot, _chainId, _token) >= tokenApprovalTotals[_token], "colony-funding-too-many-approvals" ); } @@ -402,17 +443,18 @@ contract ColonyFunding is // unless the expenditure was cancelled require( expenditures[fromPot.associatedTypeId].status == ExpenditureStatus.Cancelled || - fromPot.balance[_token] >= fromPot.payouts[_token], + getFundingPotBalance(_fromPot, _chainId, _token) >= + getFundingPotPayout(_fromPot, _chainId, _token), "colony-funding-expenditure-bad-state" ); uint256 fromPotPreviousAmount = fromPot.balance[_token] + _amount; - updatePayoutsWeCannotMakeAfterPotChange(_fromPot, _token, fromPotPreviousAmount); + updatePayoutsWeCannotMakeAfterPotChange(_fromPot, _chainId, _token, fromPotPreviousAmount); } if (toPot.associatedType == FundingPotAssociatedType.Expenditure) { - uint256 toPotPreviousAmount = toPot.balance[_token] - _amount; - updatePayoutsWeCannotMakeAfterPotChange(_toPot, _token, toPotPreviousAmount); + uint256 toPotPreviousAmount = getFundingPotBalance(_toPot, _chainId, _token) - _amount; + updatePayoutsWeCannotMakeAfterPotChange(_toPot, _chainId, _token, toPotPreviousAmount); } if (_toPot == 0) { @@ -424,20 +466,27 @@ contract ColonyFunding is function updatePayoutsWeCannotMakeAfterPotChange( uint256 _fundingPotId, + uint256 _chainId, address _token, uint256 _prev ) internal { FundingPot storage tokenPot = fundingPots[_fundingPotId]; - if (_prev >= tokenPot.payouts[_token]) { + if (_prev >= getFundingPotPayout(_fundingPotId, _chainId, _token)) { // If the old amount in the pot was enough to pay for the budget - if (tokenPot.balance[_token] < tokenPot.payouts[_token]) { + if ( + getFundingPotBalance(_fundingPotId, _chainId, _token) < + getFundingPotPayout(_fundingPotId, _chainId, _token) + ) { // And the new amount in the pot is not enough to pay for the budget... tokenPot.payoutsWeCannotMake += 1; // Then this is a set of payouts we cannot make that we could before. } } else { // If this 'else' is running, then the old amount in the pot could not pay for the budget - if (tokenPot.balance[_token] >= tokenPot.payouts[_token]) { + if ( + getFundingPotBalance(_fundingPotId, _chainId, _token) >= + getFundingPotPayout(_fundingPotId, _chainId, _token) + ) { // And the new amount in the pot can pay for the budget tokenPot.payoutsWeCannotMake -= 1; // Then this is a set of payouts we can make that we could not before. } @@ -469,6 +518,7 @@ contract ColonyFunding is function setExpenditurePayoutsInternal( uint256 _id, uint256[] memory _slots, + uint256 _chainId, address _token, uint256[] memory _amounts ) internal { @@ -477,14 +527,15 @@ contract ColonyFunding is FundingPot storage fundingPot = fundingPots[expenditures[_id].fundingPotId]; assert(fundingPot.associatedType == FundingPotAssociatedType.Expenditure); - uint256 previousTotal = fundingPot.payouts[_token]; - uint256 runningTotal = fundingPot.payouts[_token]; + uint256 previousTotal = getFundingPotPayout(expenditures[_id].fundingPotId, _chainId, _token); + uint256 runningTotal = previousTotal; for (uint256 i; i < _slots.length; i++) { require(_amounts[i] <= MAX_PAYOUT, "colony-payout-too-large"); - uint256 currentPayout = expenditureSlotPayouts[_id][_slots[i]][_token]; - - expenditureSlotPayouts[_id][_slots[i]][_token] = _amounts[i]; + uint256 currentPayout = getExpenditureSlotPayout(_id, _slots[i], _chainId, _token); + // uint256 currentPayout = expenditureSlotPayouts[_id][_slots[i]][_token]; + setExpenditureSlotPayout(_id, _slots[i], _chainId, _token, _amounts[i]); + // expenditureSlotPayouts[_id][_slots[i]][_token] = _amounts[i]; runningTotal = (runningTotal - currentPayout) + _amounts[i]; emit ExpenditurePayoutSet(msgSender(), _id, _slots[i], _token, _amounts[i]); @@ -500,38 +551,56 @@ contract ColonyFunding is function processPayout( uint256 _fundingPotId, + uint256 _chainId, address _token, uint256 _payout, address payable _user ) private returns (uint256) { - refundDomain(_fundingPotId, _token); + refundDomain(_fundingPotId, _chainId, _token); IColonyNetwork colonyNetworkContract = IColonyNetwork(colonyNetworkAddress); address payable metaColonyAddress = colonyNetworkContract.getMetaColony(); - fundingPots[_fundingPotId].balance[_token] -= _payout; - fundingPots[_fundingPotId].payouts[_token] -= _payout; - nonRewardPotsTotal[_token] -= _payout; + setFundingPotBalance( + _fundingPotId, + _chainId, + _token, + getFundingPotBalance(_fundingPotId, _chainId, _token) - _payout + ); + setFundingPotPayout( + _fundingPotId, + _chainId, + _token, + getFundingPotPayout(_fundingPotId, _chainId, _token) - _payout + ); - uint256 fee = isOwnExtension(_user) ? 0 : calculateNetworkFeeForPayout(_payout); - uint256 payoutToUser = _payout - fee; + uint256 payoutToUser; - if (_token == address(0x0)) { - // Payout ether - // Fee goes directly to Meta Colony - _user.transfer(payoutToUser); - metaColonyAddress.transfer(fee); - } else { - // Payout token - // If it's a whitelisted token, it goes straight to the metaColony - // If it's any other token, goes to the colonyNetwork contract first to be auctioned. - ERC20Extended payoutToken = ERC20Extended(_token); - assert(payoutToken.transfer(_user, payoutToUser)); - if (colonyNetworkContract.getPayoutWhitelist(_token)) { - assert(payoutToken.transfer(metaColonyAddress, fee)); + if (_chainId == block.chainid) { + nonRewardPotsTotal[_token] -= _payout; + + uint256 fee = isOwnExtension(_user) ? 0 : calculateNetworkFeeForPayout(_payout); + payoutToUser = _payout - fee; + + if (_token == address(0x0)) { + // Payout ether + // Fee goes directly to Meta Colony + _user.transfer(payoutToUser); + metaColonyAddress.transfer(fee); } else { - assert(payoutToken.transfer(colonyNetworkAddress, fee)); + // Payout token + // If it's a whitelisted token, it goes straight to the metaColony + // If it's any other token, goes to the colonyNetwork contract first to be auctioned. + ERC20Extended payoutToken = ERC20Extended(_token); + assert(payoutToken.transfer(_user, payoutToUser)); + if (colonyNetworkContract.getPayoutWhitelist(_token)) { + assert(payoutToken.transfer(metaColonyAddress, fee)); + } else { + assert(payoutToken.transfer(colonyNetworkAddress, fee)); + } } + } else { + // TODO: Shell colony payout } // slither-disable-next-line reentrancy-unlimited-gas @@ -540,17 +609,104 @@ contract ColonyFunding is return payoutToUser; } - function refundDomain(uint256 _fundingPotId, address _token) private { - FundingPot storage fundingPot = fundingPots[_fundingPotId]; - if (fundingPot.payouts[_token] < fundingPot.balance[_token]) { + function refundDomain(uint256 _fundingPotId, uint256 _chainId, address _token) private { + if ( + getFundingPotPayout(_fundingPotId, _chainId, _token) < + getFundingPotBalance(_fundingPotId, _chainId, _token) + ) { uint256 domainId = getDomainFromFundingPot(_fundingPotId); - uint256 surplus = fundingPot.balance[_token] - fundingPot.payouts[_token]; + uint256 surplus = getFundingPotBalance(_fundingPotId, _chainId, _token) - + getFundingPotPayout(_fundingPotId, _chainId, _token); + moveFundsBetweenPotsFunctionality( _fundingPotId, domains[domainId].fundingPotId, surplus, + _chainId, _token ); } } + + function getFundingPotBalance( + uint256 _potId, + uint256 _chainId, + address _token + ) internal view returns (uint256) { + if (_chainId == block.chainid) { + return fundingPots[_potId].balance[_token]; + } + return fundingPots[_potId].chainBalances[_chainId][_token]; + } + + function setFundingPotBalance( + uint256 _potId, + uint256 _chainId, + address _token, + uint256 _newValue + ) internal stoppable { + if (_chainId == block.chainid) { + fundingPots[_potId].balance[_token] = _newValue; + } else { + fundingPots[_potId].chainBalances[_chainId][_token] = _newValue; + } + } + + function getFundingPotPayout( + uint256 _potId, + uint256 _chainId, + address _token + ) internal view returns (uint256) { + if (_chainId == block.chainid) { + return fundingPots[_potId].payouts[_token]; + } + return fundingPots[_potId].chainPayouts[_chainId][_token]; + } + + function setFundingPotPayout( + uint256 _potId, + uint256 _chainId, + address _token, + uint256 _newValue + ) internal { + if (_chainId == block.chainid) { + fundingPots[_potId].payouts[_token] = _newValue; + } else { + fundingPots[_potId].chainPayouts[_chainId][_token] = _newValue; + } + } + + function getExpenditureSlotPayout( + uint256 _id, + uint256 _slot, + address _token + ) public view returns (uint256) { + return getExpenditureSlotPayout(_id, _slot, block.chainid, _token); + } + + function getExpenditureSlotPayout( + uint256 _id, + uint256 _slot, + uint256 chainId, + address _token + ) public view returns (uint256) { + if (chainId == block.chainid) { + return expenditureSlotPayouts[_id][_slot][_token]; + } + return expenditureSlotChainPayouts[_id][_slot][chainId][_token]; + } + + function setExpenditureSlotPayout( + uint256 _id, + uint256 _slot, + uint256 chainId, + address _token, + uint256 _newValue + ) internal { + if (chainId == block.chainid) { + expenditureSlotPayouts[_id][_slot][_token] = _newValue; + } else { + expenditureSlotChainPayouts[_id][_slot][chainId][_token] = _newValue; + } + } } diff --git a/contracts/colony/ColonyStorage.sol b/contracts/colony/ColonyStorage.sol index 66571718f2..c20e091211 100755 --- a/contracts/colony/ColonyStorage.sol +++ b/contracts/colony/ColonyStorage.sol @@ -117,6 +117,9 @@ contract ColonyStorage is ColonyDataTypes, ColonyNetworkDataTypes, DSMath, Commo // Mapping of domainId to allowed amount of reputation received tokens could generate if paid out mapping(uint256 => uint256) domainReputationApproval; // Storage slot 39 + // Expenditure Id > Slot Id > Chain Id > Token Address > Amount + mapping(uint256 => mapping(uint256 => mapping(uint256 => mapping(address => uint256)))) expenditureSlotChainPayouts; // Storage slot 40 + // Constants uint256 constant MAX_PAYOUT = 2 ** 128 - 1; // 340,282,366,920,938,463,463 WADs diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 79bc7e9e76..0f7dfc96de 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -859,7 +859,11 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, function getAllowedDomainReputationReceipt(uint256 _domainId) external view returns (uint256); function recordClaimedFundsFromBridge(uint256 _chainId, address _token, uint256 _amount) external; - function getFundingPotProxyBalance(uint256 _potId, uint256 _chainId, address _token) external view returns (uint256); + function getFundingPotProxyBalance( + uint256 _potId, + uint256 _chainId, + address _token + ) external view returns (uint256); function createShellColony(uint256 _destinationChainId, bytes32 _salt) external; From 872f64d27cbacae3bab506346866e57d4cd7450a Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 9 Aug 2024 07:39:52 +0100 Subject: [PATCH 08/72] Allow proxy colonies to pay funds --- contracts/bridging/ShellColony.sol | 16 +++- contracts/colony/ColonyFunding.sol | 71 +++++++++++++++-- contracts/colony/IColony.sol | 26 ++++++ contracts/colonyNetwork/ColonyNetwork.sol | 8 ++ .../colonyNetwork/ColonyNetworkShells.sol | 44 +++++------ contracts/colonyNetwork/IColonyNetwork.sol | 2 + test/cross-chain/cross-chain.js | 79 +++++++++++++++++-- 7 files changed, 205 insertions(+), 41 deletions(-) diff --git a/contracts/bridging/ShellColony.sol b/contracts/bridging/ShellColony.sol index 23e6c5c925..470fae62d1 100644 --- a/contracts/bridging/ShellColony.sol +++ b/contracts/bridging/ShellColony.sol @@ -46,6 +46,11 @@ contract ShellColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards // Events + modifier onlyColonyBridge() { + require(ShellColonyNetwork(owner).colonyBridgeAddress() == msgSender(), "colony-only-bridge"); + _; + } + event ColonyFundsClaimed(address token, uint256 balance); event TransferMade(address token, address user, uint256 amount); @@ -72,11 +77,16 @@ contract ShellColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards emit ColonyFundsClaimed(_token, balance); } - function transfer(address _token, address _user, uint256 _amount) public auth { + // TODO: secure this function + function transferFromBridge(address _token, address _recipient, uint256 _amount) public onlyColonyBridge() { tokenBalances[_token] -= _amount; - require(ERC20Extended(_token).transfer(_user, _amount), "colony-shell-transfer-failed"); + if (_token == address(0x0)) { + payable(_recipient).transfer(_amount); + } else { + require(ERC20Extended(_token).transfer(_recipient, _amount), "colony-shell-transfer-failed"); + } - emit TransferMade(_token, _user, _amount); + emit TransferMade(_token, _recipient, _amount); } } diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index b81c35f45f..9c51806d68 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -40,6 +40,35 @@ contract ColonyFunding is uint256 _toPot, uint256 _amount, address _token + ) + public + stoppable + { + moveFundsBetweenPots( + _permissionDomainId, + _childSkillIndex, + _domainId, + _fromChildSkillIndex, + _toChildSkillIndex, + _fromPot, + _toPot, + _amount, + block.chainid, + _token + ); + } + + function moveFundsBetweenPots( + uint256 _permissionDomainId, + uint256 _childSkillIndex, + uint256 _domainId, + uint256 _fromChildSkillIndex, + uint256 _toChildSkillIndex, + uint256 _fromPot, + uint256 _toPot, + uint256 _amount, + uint256 _chainId, + address _token ) public stoppable @@ -56,7 +85,7 @@ contract ColonyFunding is "colony-invalid-domain-inheritance" ); - moveFundsBetweenPotsFunctionality(_fromPot, _toPot, _amount, block.chainid, _token); + moveFundsBetweenPotsFunctionality(_fromPot, _toPot, _amount, _chainId, _token); } function moveFundsBetweenPots( @@ -220,6 +249,21 @@ contract ColonyFunding is uint256 _slot, address _token, uint256 _amount + ) + public + stoppable + { + setExpenditurePayout(_permissionDomainId, _childSkillIndex, _id, _slot, block.chainid, _token, _amount); + } + + function setExpenditurePayout( + uint256 _permissionDomainId, + uint256 _childSkillIndex, + uint256 _id, + uint256 _slot, + uint256 _chainId, + address _token, + uint256 _amount ) public stoppable @@ -230,7 +274,7 @@ contract ColonyFunding is slots[0] = _slot; uint256[] memory amounts = new uint256[](1); amounts[0] = _amount; - setExpenditurePayoutsInternal(_id, slots, block.chainid, _token, amounts); + setExpenditurePayoutsInternal(_id, slots, _chainId, _token, amounts); } /// @notice For owners to update payouts with one token and one slot @@ -448,7 +492,7 @@ contract ColonyFunding is "colony-funding-expenditure-bad-state" ); - uint256 fromPotPreviousAmount = fromPot.balance[_token] + _amount; + uint256 fromPotPreviousAmount = getFundingPotBalance(_fromPot, _chainId, _token) + _amount; updatePayoutsWeCannotMakeAfterPotChange(_fromPot, _chainId, _token, fromPotPreviousAmount); } @@ -495,20 +539,21 @@ contract ColonyFunding is function updatePayoutsWeCannotMakeAfterBudgetChange( uint256 _fundingPotId, + uint256 _chainId, address _token, uint256 _prev ) internal { FundingPot storage tokenPot = fundingPots[_fundingPotId]; - if (tokenPot.balance[_token] >= _prev) { + if (getFundingPotBalance(_fundingPotId, _chainId, _token) >= _prev) { // If the amount in the pot was enough to pay for the old budget... - if (tokenPot.balance[_token] < tokenPot.payouts[_token]) { + if (getFundingPotBalance(_fundingPotId, _chainId, _token) < getFundingPotPayout(_fundingPotId, _chainId, _token)) { // And the amount is not enough to pay for the new budget... tokenPot.payoutsWeCannotMake += 1; // Then this is a set of payouts we cannot make that we could before. } } else { // If this 'else' is running, then the amount in the pot was not enough to pay for the old budget - if (tokenPot.balance[_token] >= tokenPot.payouts[_token]) { + if (getFundingPotBalance(_fundingPotId, _chainId, _token) >= getFundingPotPayout(_fundingPotId, _chainId, _token)) { // And the amount is enough to pay for the new budget... tokenPot.payoutsWeCannotMake -= 1; // Then this is a set of payouts we can make that we could not before. } @@ -541,9 +586,12 @@ contract ColonyFunding is emit ExpenditurePayoutSet(msgSender(), _id, _slots[i], _token, _amounts[i]); } - fundingPot.payouts[_token] = runningTotal; + // fundingPot.payouts[_token] = runningTotal; + setFundingPotPayout(expenditures[_id].fundingPotId, _chainId, _token, runningTotal); + updatePayoutsWeCannotMakeAfterBudgetChange( expenditures[_id].fundingPotId, + _chainId, _token, previousTotal ); @@ -600,7 +648,14 @@ contract ColonyFunding is } } } else { - // TODO: Shell colony payout + // TODO: Shell colony payout + bytes memory payload = abi.encodeWithSignature( + "transferFromBridge(address,address,uint256)", + _token, + _user, + _payout + ); + IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, payload); } // slither-disable-next-line reentrancy-unlimited-gas diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 0f7dfc96de..56a1d528ae 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -585,6 +585,17 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, uint256 _amount ) external; + function setExpenditurePayout( + uint256 _permissionDomainId, + uint256 _childSkillIndex, + uint256 _id, + uint256 _slot, + uint256 _chainId, + address _token, + uint256 _amount + ) + external; + /// @notice @deprecated /// @notice Sets the skill on an expenditure slot. Can only be called by expenditure owner. /// @param _id Expenditure identifier @@ -653,6 +664,7 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, /// @param _slot Number of the slot /// @param _token Address of the token, `0x0` value indicates Ether function claimExpenditurePayout(uint256 _id, uint256 _slot, address _token) external; + function claimExpenditurePayout(uint256 _id, uint256 _slot, uint256 _chainId, address _token) external; /// @notice Get the number of expenditures in the colony. /// @return count The expenditure count @@ -813,6 +825,20 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, address _token ) external; + function moveFundsBetweenPots( + uint256 _permissionDomainId, + uint256 _childSkillIndex, + uint256 _domainId, + uint256 _fromChildSkillIndex, + uint256 _toChildSkillIndex, + uint256 _fromPot, + uint256 _toPot, + uint256 _amount, + uint256 _chainId, + address _token + ) external; + + /// @notice @deprecated /// @notice Move a given amount: `_amount` of `_token` funds from funding pot with id `_fromPot` to one with id `_toPot`. /// @param _permissionDomainId The domainId in which I have the permission to take this action diff --git a/contracts/colonyNetwork/ColonyNetwork.sol b/contracts/colonyNetwork/ColonyNetwork.sol index 3ef6f23e30..78d4588157 100644 --- a/contracts/colonyNetwork/ColonyNetwork.sol +++ b/contracts/colonyNetwork/ColonyNetwork.sol @@ -23,6 +23,7 @@ import { BasicMetaTransaction } from "./../common/BasicMetaTransaction.sol"; import { IReputationMiningCycle } from "./../reputationMiningCycle/IReputationMiningCycle.sol"; import { ColonyNetworkStorage } from "./ColonyNetworkStorage.sol"; import { Multicall } from "./../common/Multicall.sol"; +import { IColonyBridge } from "./../bridging/IColonyBridge.sol"; contract ColonyNetwork is BasicMetaTransaction, ColonyNetworkStorage, Multicall { function isColony(address _colony) public view returns (bool) { @@ -111,6 +112,13 @@ contract ColonyNetwork is BasicMetaTransaction, ColonyNetworkStorage, Multicall emit ColonyNetworkInitialised(_resolver); } + function bridgeMessage(uint256 _chainId, bytes memory _payload) public calledByColony { + require( + IColonyBridge(colonyBridgeAddress).sendMessage(_chainId, msg.sender, _payload), + "colony-network-bridge-message-failed" + ); + } + function getColony(uint256 _id) public view returns (address) { return colonies[_id]; } diff --git a/contracts/colonyNetwork/ColonyNetworkShells.sol b/contracts/colonyNetwork/ColonyNetworkShells.sol index 432d7300b9..e83fd47e8f 100644 --- a/contracts/colonyNetwork/ColonyNetworkShells.sol +++ b/contracts/colonyNetwork/ColonyNetworkShells.sol @@ -33,30 +33,30 @@ contract ColonyNetworkShells is ColonyNetworkStorage, Multicall { require(callThroughBridgeWithGuards(payload), "colony-network-shell-deploy-failed"); } - function ShellColonyTransfer( - address _colony, - address _token, - address _user, - uint256 _amount - ) public onlyColonyBridge { - ShellColony(_colony).transfer(_token, _user, _amount); - } + // function ShellColonyTransfer( + // address _colony, + // address _token, + // address _user, + // uint256 _amount + // ) public onlyColonyBridge { + // ShellColony(_colony).transfer(_token, _user, _amount); + // } - function sendShellColonyTransfer( - address _token, - address _user, - uint256 _amount - ) public calledByColony { - bytes memory payload = abi.encodeWithSignature( - "ShellColonyTransfer(address,address,address,uint256)", - msgSender(), - _token, - _user, - _amount - ); + // function sendShellColonyTransfer( + // address _token, + // address _user, + // uint256 _amount + // ) public calledByColony { + // bytes memory payload = abi.encodeWithSignature( + // "ShellColonyTransfer(address,address,address,uint256)", + // msgSender(), + // _token, + // _user, + // _amount + // ); - require(callThroughBridgeWithGuards(payload), "colony-network-shell-transfer-failed"); - } + // require(callThroughBridgeWithGuards(payload), "colony-network-shell-transfer-failed"); + // } // From shells diff --git a/contracts/colonyNetwork/IColonyNetwork.sol b/contracts/colonyNetwork/IColonyNetwork.sol index a22af7bd9e..a7bf9df4d7 100644 --- a/contracts/colonyNetwork/IColonyNetwork.sol +++ b/contracts/colonyNetwork/IColonyNetwork.sol @@ -529,6 +529,8 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery, IBasicMetaTransac /// @return bridge The address of the bridge to the mining chain, if set function getColonyBridgeAddress() external view returns (address bridge); + function bridgeMessage(uint256 _chainId, bytes memory _payload) external; + /// @notice Update the reputation on a foreign chain from the mining chain /// @dev Should error if called by anyone other than the known bridge from the mining chain /// @param newHash The new root hash diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 3d04620746..8da5f35f21 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -38,7 +38,15 @@ const MetaTxToken = artifacts.require("MetaTxToken"); // const { assert } = require("console"); const { setupBridging, deployBridge } = require("../../scripts/setup-bridging-contracts"); -const { MINING_CYCLE_DURATION, CHALLENGE_RESPONSE_WINDOW_DURATION, ROOT_ROLE, CURR_VERSION, CREATEX_ADDRESS } = require("../../helpers/constants"); +const { + MINING_CYCLE_DURATION, + CHALLENGE_RESPONSE_WINDOW_DURATION, + ROOT_ROLE, + CURR_VERSION, + CREATEX_ADDRESS, + UINT256_MAX, + WAD, +} = require("../../helpers/constants"); const { forwardTime, checkErrorRevertEthers, revert, snapshot, evmChainIdToWormholeChainId } = require("../../helpers/test-helper"); const ReputationMinerTestWrapper = require("../../packages/reputation-miner/test/ReputationMinerTestWrapper"); const { TruffleLoader } = require("../../packages/package-utils"); @@ -296,9 +304,9 @@ contract("Cross-chain", (accounts) => { } afterEach(async () => { - await revert(web3HomeProvider, homeSnapshotId); - await revert(web3ForeignProvider, foreignSnapshotId); - await resetRelayer(); + // await revert(web3HomeProvider, homeSnapshotId); + // await revert(web3ForeignProvider, foreignSnapshotId); + // await resetRelayer(); }); after(async () => { @@ -1190,6 +1198,10 @@ contract("Cross-chain", (accounts) => { const tokenFactory = new ethers.ContractFactory(MetaTxToken.abi, MetaTxToken.bytecode, ethersForeignSigner); foreignToken = await tokenFactory.deploy("Test Token", "TT", 18); + await (await foreignToken.unlock()).wait(); + + await (await colony.setArbitrationRole(1, UINT256_MAX_ETHERS, accounts[0], 1, true)).wait(); + await (await colony.setFundingRole(1, UINT256_MAX_ETHERS, accounts[0], 1, true)).wait(); }); it.only("Can track tokens received on the foreign chain", async () => { @@ -1205,7 +1217,7 @@ contract("Cross-chain", (accounts) => { tx = await shellColony.claimTokens(foreignToken.address); await tx.wait(); - let receipt = await p; + const receipt = await p; expect(receipt.status).to.equal(1); // Check bookkeeping on the home chain @@ -1214,24 +1226,75 @@ contract("Cross-chain", (accounts) => { expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); }); - it.skip("Can track tokens sent on the foreign chain", async () => { + it.only("Can track tokens sent on the foreign chain", async () => { const tokenAmount = ethers.utils.parseEther("100"); let tx = await foreignToken["mint(address,uint256)"](shellColony.address, tokenAmount); await tx.wait(); // Claim on the foreign chain - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + let p = bridgeMonitor.getPromiseForNextBridgedTransaction(); tx = await shellColony.claimTokens(foreignToken.address); await tx.wait(); await p; - // Make a payment that pays out 30 + const paymentAmount = ethers.utils.parseEther("30"); + tx = await colony.makeExpenditure(1, UINT256_MAX_ETHERS, 1); + await tx.wait(); + const expenditureId = await colony.getExpenditureCount(); + + tx = await colony.setExpenditureRecipient(expenditureId, 1, accounts[0]); + await tx.wait(); + + console.log("set recipient"); + + tx = await colony["setExpenditurePayout(uint256,uint256,uint256,uint256,uint256,address,uint256)"]( + 1, + UINT256_MAX_ETHERS, + expenditureId, + 1, + foreignChainId, + foreignToken.address, + paymentAmount, + ); + await tx.wait(); + console.log("set payout"); + const domain1 = await colony.getDomain(1); + const expenditure = await colony.getExpenditure(expenditureId); + + tx = await colony["moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)"]( + 1, + UINT256_MAX_ETHERS, + 1, + UINT256_MAX_ETHERS, + UINT256_MAX_ETHERS, + domain1.fundingPotId, + expenditure.fundingPotId, + paymentAmount, + foreignChainId, + foreignToken.address, + ); + await tx.wait(); + tx = await colony.finalizeExpenditure(expenditureId); + await tx.wait(); + + p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + tx = await colony["claimExpenditurePayout(uint256,uint256,uint256,address)"](expenditureId, 1, foreignChainId, foreignToken.address); + await tx.wait(); + await p; // Check bookkeeping on the home chain + const balance1 = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + expect(balance1.toHexString()).to.equal(ethers.utils.parseEther("70").toHexString()); + // Check actually paid on foreign chain + const colonyBalance = await foreignToken.balanceOf(shellColony.address); + const recipientBalance = await foreignToken.balanceOf(accounts[0]); + + expect(colonyBalance.toHexString()).to.equal(ethers.utils.parseEther("70").toHexString()); + expect(recipientBalance.toHexString()).to.equal(ethers.utils.parseEther("30").toHexString()); }); }); From 316c93a23a45ab7572967000e58964e98a741cdc Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 9 Aug 2024 08:18:38 +0100 Subject: [PATCH 09/72] Arbitrary transactions through proxy colonies --- contracts/bridging/ShellColony.sol | 9 ++++ .../colony/ColonyArbitraryTransaction.sol | 13 ++++++ contracts/colony/IColony.sol | 2 + test/cross-chain/cross-chain.js | 44 +++++++++++++++++++ 4 files changed, 68 insertions(+) diff --git a/contracts/bridging/ShellColony.sol b/contracts/bridging/ShellColony.sol index 470fae62d1..8a6053189b 100644 --- a/contracts/bridging/ShellColony.sol +++ b/contracts/bridging/ShellColony.sol @@ -89,4 +89,13 @@ contract ShellColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards emit TransferMade(_token, _recipient, _amount); } + + function makeArbitraryTransaction(address _target, bytes memory _payload) public onlyColonyBridge() { + require(_target != address(this), "colony-cannot-target-self"); + require(_target != ShellColonyNetwork(owner).colonyBridgeAddress(), "colony-cannot-target-bridge"); + require(_target != owner, "colony-cannot-target-bridge"); + + (bool success, ) = _target.call(_payload); + require(success, "colony-arbitrary-transaction-failed"); + } } diff --git a/contracts/colony/ColonyArbitraryTransaction.sol b/contracts/colony/ColonyArbitraryTransaction.sol index 5cbbb1f693..86ab677129 100644 --- a/contracts/colony/ColonyArbitraryTransaction.sol +++ b/contracts/colony/ColonyArbitraryTransaction.sol @@ -44,6 +44,19 @@ contract ColonyArbitraryTransaction is ColonyStorage { return res; } + function makeProxyArbitraryTransaction( + uint256 _chainId, + address _to, + bytes memory _action + ) public stoppable auth returns (bool) { + bytes memory payload = abi.encodeWithSignature( + "makeArbitraryTransaction(address,bytes)", + _to, + _action + ); + IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, payload); + } + function makeArbitraryTransactions( address[] memory _targets, bytes[] memory _actions, diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 56a1d528ae..df18b60004 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -61,6 +61,8 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, bytes memory _action ) external returns (bool success); + function makeProxyArbitraryTransaction(uint256 chainId,address _to,bytes memory action) external; + /// @notice Execute arbitrary transactions on behalf of the Colony in series /// @param _targets Array of addressed to be targeted /// @param _actions Array of Bytes arrays encoding the function calls and arguments diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 8da5f35f21..8e25b64c48 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -1298,6 +1298,50 @@ contract("Cross-chain", (accounts) => { }); }); + describe("making arbitrary transactions on another chain", async () => { + let colony; + let shellColony; + let foreignToken; + beforeEach(async () => { + colony = await setupColony(homeColonyNetwork); + + const events = await homeColonyNetwork.queryFilter(homeColonyNetwork.filters.ColonyAdded()); + // homeColonyNetwork.fil + // Deploy a proxy colony on the foreign network + + const colonyCreationSalt = await homeColonyNetwork.getColonyCreationSalt({ blockTag: events[events.length - 1].blockNumber }); + + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + + const tx = await colony.createShellColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); + await tx.wait(); + + await p; + shellColony = new ethers.Contract(colony.address, ShellColony.abi, ethersForeignSigner); + // Deploy a token on the foreign network + + const tokenFactory = new ethers.ContractFactory(MetaTxToken.abi, MetaTxToken.bytecode, ethersForeignSigner); + foreignToken = await tokenFactory.deploy("Test Token", "TT", 18); + await (await foreignToken.unlock()).wait(); + await (await foreignToken.setOwner(shellColony.address)).wait(); + }); + + it.only("can make arbitrary transactions on the foreign chain", async () => { + const balanceBefore = await foreignToken.balanceOf(shellColony.address); + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + + const payload = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [shellColony.address, ethers.utils.parseEther("100")]); + + const tx = await colony.makeProxyArbitraryTransaction(foreignChainId, foreignToken.address, payload); + await tx.wait(); + await p; + + const balanceAfter = await foreignToken.balanceOf(shellColony.address); + console.log(balanceBefore.toHexString(), balanceAfter.toHexString()); + expect(balanceAfter.sub(balanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); + }); + }); + describe("bridge functions are secure", async () => { it("only the configured colonyNetwork can call `sendMessage`", async () => { const tx = await foreignColonyBridge.sendMessage(1, "0x00000000", { gasLimit: 1000000 }); From 1e4499b0379db8d3792b474c58bd161134b52a73 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 9 Aug 2024 10:57:21 +0100 Subject: [PATCH 10/72] Remove ColonyNetworkShells --- .../colonyNetwork/ColonyNetworkShells.sol | 73 ------------------- helpers/upgradable-contracts.js | 2 - test/truffle-fixture.js | 6 -- 3 files changed, 81 deletions(-) delete mode 100644 contracts/colonyNetwork/ColonyNetworkShells.sol diff --git a/contracts/colonyNetwork/ColonyNetworkShells.sol b/contracts/colonyNetwork/ColonyNetworkShells.sol deleted file mode 100644 index e83fd47e8f..0000000000 --- a/contracts/colonyNetwork/ColonyNetworkShells.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -/* - This file is part of The Colony Network. - - The Colony Network is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - The Colony Network is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with The Colony Network. If not, see . -*/ - -pragma solidity 0.8.27; -pragma experimental ABIEncoderV2; - -import { IColony } from "./../colony/IColony.sol"; -import { Multicall } from "./../common/Multicall.sol"; -import { ColonyNetworkStorage } from "./ColonyNetworkStorage.sol"; -import { ShellColony } from "./../bridging/ShellColony.sol"; - -contract ColonyNetworkShells is ColonyNetworkStorage, Multicall { - // To shells - - function sendDeployShellColony(bytes32 _salt) public calledByColony { - bytes memory payload = abi.encodeWithSignature("deployShellColony(bytes32)", _salt); - - require(callThroughBridgeWithGuards(payload), "colony-network-shell-deploy-failed"); - } - - // function ShellColonyTransfer( - // address _colony, - // address _token, - // address _user, - // uint256 _amount - // ) public onlyColonyBridge { - // ShellColony(_colony).transfer(_token, _user, _amount); - // } - - // function sendShellColonyTransfer( - // address _token, - // address _user, - // uint256 _amount - // ) public calledByColony { - // bytes memory payload = abi.encodeWithSignature( - // "ShellColonyTransfer(address,address,address,uint256)", - // msgSender(), - // _token, - // _user, - // _amount - // ); - - // require(callThroughBridgeWithGuards(payload), "colony-network-shell-transfer-failed"); - // } - - // From shells - - function sendClaimShellColonyFunds(address _token, uint256 _balance) public calledByColony { - bytes memory payload = abi.encodeWithSignature( - "claimShellColonyFunds(address,address,uint256)", - msgSender(), - _token, - _balance - ); - - require(callThroughBridgeWithGuards(payload), "colony-network-shell-claim-failed"); - } -} diff --git a/helpers/upgradable-contracts.js b/helpers/upgradable-contracts.js index 9992304012..13c94a4e17 100644 --- a/helpers/upgradable-contracts.js +++ b/helpers/upgradable-contracts.js @@ -135,7 +135,6 @@ exports.setupUpgradableColonyNetwork = async function setupUpgradableColonyNetwo colonyNetworkENS, colonyNetworkExtensions, colonyNetworkSkills, - colonyNetworkShells, contractRecovery, ) { const deployedImplementations = {}; @@ -146,7 +145,6 @@ exports.setupUpgradableColonyNetwork = async function setupUpgradableColonyNetwo deployedImplementations.ColonyNetworkENS = colonyNetworkENS.address; deployedImplementations.ColonyNetworkExtensions = colonyNetworkExtensions.address; deployedImplementations.ColonyNetworkSkills = colonyNetworkSkills.address; - deployedImplementations.ColonyNetworkShells = colonyNetworkShells.address; deployedImplementations.ContractRecovery = contractRecovery.address; await exports.setupEtherRouter("colonyNetwork", "IColonyNetwork", deployedImplementations, resolver); diff --git a/test/truffle-fixture.js b/test/truffle-fixture.js index 49b786a54f..bb22dafa57 100644 --- a/test/truffle-fixture.js +++ b/test/truffle-fixture.js @@ -24,7 +24,6 @@ const ColonyNetworkAuction = artifacts.require("ColonyNetworkAuction"); const ColonyNetworkENS = artifacts.require("ColonyNetworkENS"); const ColonyNetworkExtensions = artifacts.require("ColonyNetworkExtensions"); const ColonyNetworkSkills = artifacts.require("ColonyNetworkSkills"); -const ColonyNetworkShells = artifacts.require("ColonyNetworkShells"); const IColonyNetwork = artifacts.require("IColonyNetwork"); const ENSRegistry = artifacts.require("ENSRegistry"); @@ -137,9 +136,6 @@ async function deployContracts() { const colonyNetworkSkills = await ColonyNetworkSkills.new(); ColonyNetworkSkills.setAsDeployed(colonyNetworkSkills); - const colonyNetworkShells = await ColonyNetworkShells.new(); - ColonyNetworkShells.setAsDeployed(colonyNetworkShells); - const reputationMiningCycle = await ReputationMiningCycle.new(); ReputationMiningCycle.setAsDeployed(reputationMiningCycle); @@ -160,7 +156,6 @@ async function setupColonyNetwork() { const colonyNetworkENS = await ColonyNetworkENS.deployed(); const colonyNetworkExtensions = await ColonyNetworkExtensions.deployed(); const colonyNetworkSkills = await ColonyNetworkSkills.deployed(); - const colonyNetworkShells = await ColonyNetworkShells.deployed(); // const etherRouter = await EtherRouter.deployed(); const resolver = await Resolver.deployed(); const contractRecovery = await ContractRecovery.deployed(); @@ -196,7 +191,6 @@ async function setupColonyNetwork() { colonyNetworkENS, colonyNetworkExtensions, colonyNetworkSkills, - colonyNetworkShells, contractRecovery, ); From fc6fe2726091a11b34d5d4e9bc84cf34e8a35734 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 9 Aug 2024 10:58:05 +0100 Subject: [PATCH 11/72] Multiple arbitrary transactions at once through shells --- contracts/bridging/ShellColony.sol | 19 ++++++++------ .../colony/ColonyArbitraryTransaction.sol | 12 ++++----- contracts/colony/IColony.sol | 2 +- test/cross-chain/cross-chain.js | 25 ++++++++++++++++++- 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/contracts/bridging/ShellColony.sol b/contracts/bridging/ShellColony.sol index 8a6053189b..3b7c20b9bc 100644 --- a/contracts/bridging/ShellColony.sol +++ b/contracts/bridging/ShellColony.sol @@ -90,12 +90,17 @@ contract ShellColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards emit TransferMade(_token, _recipient, _amount); } - function makeArbitraryTransaction(address _target, bytes memory _payload) public onlyColonyBridge() { - require(_target != address(this), "colony-cannot-target-self"); - require(_target != ShellColonyNetwork(owner).colonyBridgeAddress(), "colony-cannot-target-bridge"); - require(_target != owner, "colony-cannot-target-bridge"); - - (bool success, ) = _target.call(_payload); - require(success, "colony-arbitrary-transaction-failed"); + function makeArbitraryTransactions(address[] memory _targets, bytes[] memory _payloads) public onlyColonyBridge() { + require(_targets.length == _payloads.length, "colony-targets-and-payloads-length-mismatch"); + address bridgeAddress = ShellColonyNetwork(owner).colonyBridgeAddress(); + for (uint256 i; i < _targets.length; i += 1) { + // TODO: Stop, or otherwise handle, approve / transferFrom + require(_targets[i] != bridgeAddress, "colony-cannot-target-bridge"); + require(_targets[i] != owner, "colony-cannot-target-network"); + + (bool success, ) = _targets[i].call(_payloads[i]); + require(success, "colony-arbitrary-transaction-failed"); + } } + } diff --git a/contracts/colony/ColonyArbitraryTransaction.sol b/contracts/colony/ColonyArbitraryTransaction.sol index 86ab677129..3fc6c39740 100644 --- a/contracts/colony/ColonyArbitraryTransaction.sol +++ b/contracts/colony/ColonyArbitraryTransaction.sol @@ -44,15 +44,15 @@ contract ColonyArbitraryTransaction is ColonyStorage { return res; } - function makeProxyArbitraryTransaction( + function makeProxyArbitraryTransactions( uint256 _chainId, - address _to, - bytes memory _action + address[] memory _destinations, + bytes[] memory _actions ) public stoppable auth returns (bool) { bytes memory payload = abi.encodeWithSignature( - "makeArbitraryTransaction(address,bytes)", - _to, - _action + "makeArbitraryTransactions(address[],bytes[])", + _destinations, + _actions ); IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, payload); } diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index df18b60004..f2df80babf 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -61,7 +61,7 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, bytes memory _action ) external returns (bool success); - function makeProxyArbitraryTransaction(uint256 chainId,address _to,bytes memory action) external; + function makeProxyArbitraryTransactions(uint256 chainId, address[] memory _destinations, bytes[] memory _actions) external; /// @notice Execute arbitrary transactions on behalf of the Colony in series /// @param _targets Array of addressed to be targeted diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 8e25b64c48..3e6c294fc1 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -1332,7 +1332,7 @@ contract("Cross-chain", (accounts) => { const payload = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [shellColony.address, ethers.utils.parseEther("100")]); - const tx = await colony.makeProxyArbitraryTransaction(foreignChainId, foreignToken.address, payload); + const tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], [payload]); await tx.wait(); await p; @@ -1340,6 +1340,29 @@ contract("Cross-chain", (accounts) => { console.log(balanceBefore.toHexString(), balanceAfter.toHexString()); expect(balanceAfter.sub(balanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); }); + + it.only("can make multiple arbitrary transactions on the foreign chain in one go", async () => { + const shellBalanceBefore = await foreignToken.balanceOf(shellColony.address); + const colonyBalanceBefore = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(2); + + const payload1 = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [shellColony.address, ethers.utils.parseEther("100")]); + const payload2 = shellColony.interface.encodeFunctionData("claimTokens(address)", [foreignToken.address]); + + const tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address, shellColony.address], [payload1, payload2]); + await tx.wait(); + await p; + + const shellBalanceAfter = await foreignToken.balanceOf(shellColony.address); + console.log(shellBalanceBefore.toHexString(), shellBalanceAfter.toHexString()); + expect(shellBalanceAfter.sub(shellBalanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); + + // Check that the second transaction was successful + + const colonyBalanceAfter = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + expect(colonyBalanceAfter.sub(colonyBalanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); + }); }); describe("bridge functions are secure", async () => { From 5e7fc41b1034bfeb63381e5cac9902f3ab6aa607 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 9 Aug 2024 11:19:56 +0100 Subject: [PATCH 12/72] Shell -> Proxy --- .../{ShellColony.sol => ProxyColony.sol} | 10 +-- ...lonyNetwork.sol => ProxyColonyNetwork.sol} | 14 ++-- contracts/colony/Colony.sol | 4 +- contracts/colony/IColony.sol | 2 +- .../colonyNetwork/ColonyNetworkDeployer.sol | 4 +- contracts/colonyNetwork/IColonyNetwork.sol | 1 + helpers/upgradable-contracts.js | 6 +- test/cross-chain/cross-chain.js | 78 +++++++++---------- 8 files changed, 60 insertions(+), 59 deletions(-) rename contracts/bridging/{ShellColony.sol => ProxyColony.sol} (92%) rename contracts/bridging/{ShellColonyNetwork.sol => ProxyColonyNetwork.sol} (90%) diff --git a/contracts/bridging/ShellColony.sol b/contracts/bridging/ProxyColony.sol similarity index 92% rename from contracts/bridging/ShellColony.sol rename to contracts/bridging/ProxyColony.sol index 3b7c20b9bc..98afc6bdba 100644 --- a/contracts/bridging/ShellColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -25,9 +25,9 @@ import { DSAuth } from "./../../lib/dappsys/auth.sol"; import { ERC20Extended } from "./../common/ERC20Extended.sol"; import { Multicall } from "./../common/Multicall.sol"; import { IColonyNetwork } from "./../colonyNetwork/IColonyNetwork.sol"; -import { ShellColonyNetwork } from "./ShellColonyNetwork.sol"; +import { ProxyColonyNetwork } from "./ProxyColonyNetwork.sol"; -contract ShellColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards { +contract ProxyColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards { // Address of the Resolver contract used by EtherRouter for lookups and routing address resolver; // Storage slot 2 (from DSAuth there is authority and owner at storage slots 0 and 1 respectively) @@ -47,7 +47,7 @@ contract ShellColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards // Events modifier onlyColonyBridge() { - require(ShellColonyNetwork(owner).colonyBridgeAddress() == msgSender(), "colony-only-bridge"); + require(ProxyColonyNetwork(owner).colonyBridgeAddress() == msgSender(), "colony-only-bridge"); _; } @@ -72,7 +72,7 @@ contract ShellColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards _token, difference ); - ShellColonyNetwork(owner).bridgeMessage(payload); + ProxyColonyNetwork(owner).bridgeMessage(payload); emit ColonyFundsClaimed(_token, balance); } @@ -92,7 +92,7 @@ contract ShellColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards function makeArbitraryTransactions(address[] memory _targets, bytes[] memory _payloads) public onlyColonyBridge() { require(_targets.length == _payloads.length, "colony-targets-and-payloads-length-mismatch"); - address bridgeAddress = ShellColonyNetwork(owner).colonyBridgeAddress(); + address bridgeAddress = ProxyColonyNetwork(owner).colonyBridgeAddress(); for (uint256 i; i < _targets.length; i += 1) { // TODO: Stop, or otherwise handle, approve / transferFrom require(_targets[i] != bridgeAddress, "colony-cannot-target-bridge"); diff --git a/contracts/bridging/ShellColonyNetwork.sol b/contracts/bridging/ProxyColonyNetwork.sol similarity index 90% rename from contracts/bridging/ShellColonyNetwork.sol rename to contracts/bridging/ProxyColonyNetwork.sol index 352902a177..0a92bc272e 100644 --- a/contracts/bridging/ShellColonyNetwork.sol +++ b/contracts/bridging/ProxyColonyNetwork.sol @@ -16,7 +16,7 @@ along with The Colony Network. If not, see . */ -pragma solidity 0.8.25; +pragma solidity 0.8.27; pragma experimental ABIEncoderV2; import { BasicMetaTransaction } from "./../common/BasicMetaTransaction.sol"; @@ -30,14 +30,14 @@ import { ICreateX } from "./../../lib/createx/src/ICreateX.sol"; import { EtherRouter } from "./../common/EtherRouter.sol"; import { IColonyBridge } from "./IColonyBridge.sol"; -contract ShellColonyNetwork is DSAuth, Multicall, CallWithGuards { +contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards { address resolver; // Storage slot 2 (from DSAuth there is authority and owner at storage slots 0 and 1 respectively) address constant CREATEX_ADDRESS = 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed; address public colonyBridgeAddress; uint256 public homeChainId; - address public shellColonyResolverAddress; + address public proxyColonyResolverAddress; mapping(address => bool) public shellColonies; /// @notice Event logged when the colony network has data about a bridge contract set. @@ -69,15 +69,15 @@ contract ShellColonyNetwork is DSAuth, Multicall, CallWithGuards { emit BridgeSet(_bridgeAddress); } - function setShellColonyResolverAddress(address _resolver) public auth { - shellColonyResolverAddress = _resolver; + function setProxyColonyResolverAddress(address _resolver) public auth { + proxyColonyResolverAddress = _resolver; } function setHomeChainId(uint256 _homeChainId) public auth { homeChainId = _homeChainId; } - function createShellColonyFromBridge(bytes32 _salt) public onlyColonyBridge { + function createProxyColonyFromBridge(bytes32 _salt) public onlyColonyBridge { EtherRouter etherRouter = EtherRouter( payable( ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( @@ -91,7 +91,7 @@ contract ShellColonyNetwork is DSAuth, Multicall, CallWithGuards { shellColonies[address(etherRouter)] = true; - etherRouter.setResolver(shellColonyResolverAddress); // ignore-swc-113 + etherRouter.setResolver(proxyColonyResolverAddress); // ignore-swc-113 } function bridgeMessage(bytes memory _payload) public onlyColony { diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index 6ded0057ad..c5ea8b14c8 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -324,8 +324,8 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); } - function createShellColony(uint256 _destinationChainId, bytes32 _salt) public { - IColonyNetwork(colonyNetworkAddress).createShellColony(_destinationChainId, _salt); + function createProxyColony(uint256 _destinationChainId, bytes32 _salt) public { + IColonyNetwork(colonyNetworkAddress).createProxyColony(_destinationChainId, _salt); } function getMetatransactionNonce(address _user) public view override returns (uint256 nonce) { diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index f2df80babf..27a8f2c5e2 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -893,7 +893,7 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, address _token ) external view returns (uint256); - function createShellColony(uint256 _destinationChainId, bytes32 _salt) external; + function createProxyColony(uint256 _destinationChainId, bytes32 _salt) external; /// @notice Get the total amount of tokens `_token` minus amount reserved to be paid to the reputation and token holders as rewards. /// @param _token Address of the token, `0x0` value indicates Ether diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index aa13b77924..a082cb337c 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -132,9 +132,9 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage { return (address(token), colonyAddress); } - function createShellColony(uint256 _destinationChainId, bytes32 _salt) public calledByColony { + function createProxyColony(uint256 _destinationChainId, bytes32 _salt) public calledByColony { // TODO: Check if the colony is allowed to use the salt - bytes memory payload = abi.encodeWithSignature("createShellColonyFromBridge(bytes32)", _salt); + bytes memory payload = abi.encodeWithSignature("createProxyColonyFromBridge(bytes32)", _salt); IColonyBridge(colonyBridgeAddress).sendMessage(_destinationChainId, address(this), payload); } diff --git a/contracts/colonyNetwork/IColonyNetwork.sol b/contracts/colonyNetwork/IColonyNetwork.sol index a7bf9df4d7..a2b121733f 100644 --- a/contracts/colonyNetwork/IColonyNetwork.sol +++ b/contracts/colonyNetwork/IColonyNetwork.sol @@ -650,6 +650,7 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery, IBasicMetaTransac address _colonyAddress, uint256 _domainId ) external view returns (address domainTokenReceiverAddress); + /// @notice Send the claimFunds transaction from the shell to the colony /// @param _token The token being held by the shell /// @param _balance The shell's current balance of the token diff --git a/helpers/upgradable-contracts.js b/helpers/upgradable-contracts.js index 13c94a4e17..231b5b734d 100644 --- a/helpers/upgradable-contracts.js +++ b/helpers/upgradable-contracts.js @@ -200,11 +200,11 @@ exports.setupENSRegistrar = async function setupENSRegistrar(colonyNetwork, ensR await ensRegistry.setSubnodeOwner(rootNode, COLONY_HASH, colonyNetwork.address); }; -exports.setupShellColonyNetwork = async function setupShellColonyNetwork(etherRouter, shellColonyNetwork, resolver) { +exports.setupProxyColonyNetwork = async function setupProxyColonyNetwork(etherRouter, proxyColonyNetwork, resolver) { const deployedImplementations = {}; - deployedImplementations.ShellColonyNetwork = shellColonyNetwork.address; + deployedImplementations.ProxyColonyNetwork = proxyColonyNetwork.address; - await exports.setupEtherRouter("bridging", "ShellColonyNetwork", deployedImplementations, resolver); + await exports.setupEtherRouter("bridging", "ProxyColonyNetwork", deployedImplementations, resolver); const txOrReceipt = await etherRouter.setResolver(resolver.address); if (txOrReceipt.wait) { await txOrReceipt.wait(); diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 3e6c294fc1..79d603711f 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -32,8 +32,8 @@ const IColony = artifacts.require("IColony"); const ICreateX = artifacts.require("ICreateX"); const IReputationMiningCycle = artifacts.require("IReputationMiningCycle"); const WormholeBridgeForColony = artifacts.require("WormholeBridgeForColony"); -const ShellColonyNetwork = artifacts.require("ShellColonyNetwork"); -const ShellColony = artifacts.require("ShellColony"); +const ProxyColonyNetwork = artifacts.require("ProxyColonyNetwork"); +const ProxyColony = artifacts.require("ProxyColony"); const MetaTxToken = artifacts.require("MetaTxToken"); // const { assert } = require("console"); const { setupBridging, deployBridge } = require("../../scripts/setup-bridging-contracts"); @@ -50,7 +50,7 @@ const { const { forwardTime, checkErrorRevertEthers, revert, snapshot, evmChainIdToWormholeChainId } = require("../../helpers/test-helper"); const ReputationMinerTestWrapper = require("../../packages/reputation-miner/test/ReputationMinerTestWrapper"); const { TruffleLoader } = require("../../packages/package-utils"); -const { setupShellColonyNetwork, setupEtherRouter } = require("../../helpers/upgradable-contracts"); +const { setupProxyColonyNetwork, setupEtherRouter } = require("../../helpers/upgradable-contracts"); const UINT256_MAX_ETHERS = ethers.BigNumber.from(2).pow(256).sub(1); @@ -189,23 +189,23 @@ contract("Cross-chain", (accounts) => { ethersForeignSigner, ); let resolver = await new ethers.ContractFactory(Resolver.abi, Resolver.bytecode, ethersForeignSigner).deploy(); - const shellColonyNetworkImplementation = await new ethers.ContractFactory( - ShellColonyNetwork.abi, - ShellColonyNetwork.bytecode, + const proxyColonyNetworkImplementation = await new ethers.ContractFactory( + ProxyColonyNetwork.abi, + ProxyColonyNetwork.bytecode, ethersForeignSigner, ).deploy(); - await setupShellColonyNetwork(etherRouter, shellColonyNetworkImplementation, resolver); + await setupProxyColonyNetwork(etherRouter, proxyColonyNetworkImplementation, resolver); console.log("**** shell colony network set up"); // Set up the resolver for shell colonies resolver = await new ethers.ContractFactory(Resolver.abi, Resolver.bytecode, ethersForeignSigner).deploy(); - const shellColonyImplementation = await new ethers.ContractFactory(ShellColony.abi, ShellColony.bytecode, ethersForeignSigner).deploy(); + const proxyColonyImplementation = await new ethers.ContractFactory(ProxyColony.abi, ProxyColony.bytecode, ethersForeignSigner).deploy(); - await setupEtherRouter("bridging", "ShellColony", { ShellColony: shellColonyImplementation.address }, resolver); - const shellColonyNetwork = new ethers.Contract(etherRouter.address, ShellColonyNetwork.abi, ethersForeignSigner); + await setupEtherRouter("bridging", "ProxyColony", { ProxyColony: proxyColonyImplementation.address }, resolver); + const proxyColonyNetwork = new ethers.Contract(etherRouter.address, ProxyColonyNetwork.abi, ethersForeignSigner); - await shellColonyNetwork.setShellColonyResolverAddress(resolver.address); + await proxyColonyNetwork.setProxyColonyResolverAddress(resolver.address); } catch (err) { console.log(err); process.exit(1); @@ -220,7 +220,7 @@ contract("Cross-chain", (accounts) => { homeColonyNetwork = await new ethers.Contract(homeEtherRouterAddress, IColonyNetwork.abi, ethersHomeSigner); const foreignEtherRouterAddress = homeEtherRouterAddress; - foreignColonyNetwork = await new ethers.Contract(foreignEtherRouterAddress, ShellColonyNetwork.abi, ethersForeignSigner); + foreignColonyNetwork = await new ethers.Contract(foreignEtherRouterAddress, ProxyColonyNetwork.abi, ethersForeignSigner); }); beforeEach(async () => { @@ -304,9 +304,9 @@ contract("Cross-chain", (accounts) => { } afterEach(async () => { - // await revert(web3HomeProvider, homeSnapshotId); - // await revert(web3ForeignProvider, foreignSnapshotId); - // await resetRelayer(); + await revert(web3HomeProvider, homeSnapshotId); + await revert(web3ForeignProvider, foreignSnapshotId); + await resetRelayer(); }); after(async () => { @@ -320,9 +320,9 @@ contract("Cross-chain", (accounts) => { const homeVersionResolver = await homeColonyNetwork.getColonyVersionResolver(CURR_VERSION); console.log(foreignColonyNetwork.address); console.log(await ethersForeignProvider.getBlockNumber()); - const shellColonyResolver = await foreignColonyNetwork.shellColonyResolverAddress(); + const proxyColonyResolver = await foreignColonyNetwork.proxyColonyResolverAddress(); expect(homeVersionResolver).to.not.equal(ADDRESS_ZERO); - expect(shellColonyResolver).to.not.equal(ADDRESS_ZERO); + expect(proxyColonyResolver).to.not.equal(ADDRESS_ZERO); }); it.only("colonies deployed on different chains can have same address", async () => { @@ -354,7 +354,7 @@ contract("Cross-chain", (accounts) => { const deployedColony = new ethers.Contract(colonyAddress, IColony.abi, ethersHomeSigner); - tx = await deployedColony.createShellColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); + tx = await deployedColony.createProxyColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); await tx.wait(); await p; @@ -367,7 +367,7 @@ contract("Cross-chain", (accounts) => { const colonyAsEtherRouter = new ethers.Contract(deployedColony.address, EtherRouter.abi, ethersForeignSigner); const resolverAddress = await colonyAsEtherRouter.resolver(); - const expectedResolver = await foreignColonyNetwork.shellColonyResolverAddress(); + const expectedResolver = await foreignColonyNetwork.proxyColonyResolverAddress(); expect(resolverAddress).to.equal(expectedResolver); }); @@ -1177,7 +1177,7 @@ contract("Cross-chain", (accounts) => { describe("collecting and paying out tokens on another chain", async () => { let foreignToken; let colony; - let shellColony; + let proxyColony; beforeEach(async () => { colony = await setupColony(homeColonyNetwork); @@ -1189,11 +1189,11 @@ contract("Cross-chain", (accounts) => { const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); - const tx = await colony.createShellColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); + const tx = await colony.createProxyColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); await tx.wait(); await p; - shellColony = new ethers.Contract(colony.address, ShellColony.abi, ethersForeignSigner); + proxyColony = new ethers.Contract(colony.address, ProxyColony.abi, ethersForeignSigner); // Deploy a token on the foreign network const tokenFactory = new ethers.ContractFactory(MetaTxToken.abi, MetaTxToken.bytecode, ethersForeignSigner); @@ -1207,14 +1207,14 @@ contract("Cross-chain", (accounts) => { it.only("Can track tokens received on the foreign chain", async () => { const tokenAmount = ethers.utils.parseEther("100"); - let tx = await foreignToken["mint(address,uint256)"](shellColony.address, tokenAmount); + let tx = await foreignToken["mint(address,uint256)"](proxyColony.address, tokenAmount); await tx.wait(); // Claim on the foreign chain const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); - tx = await shellColony.claimTokens(foreignToken.address); + tx = await proxyColony.claimTokens(foreignToken.address); await tx.wait(); const receipt = await p; @@ -1229,12 +1229,12 @@ contract("Cross-chain", (accounts) => { it.only("Can track tokens sent on the foreign chain", async () => { const tokenAmount = ethers.utils.parseEther("100"); - let tx = await foreignToken["mint(address,uint256)"](shellColony.address, tokenAmount); + let tx = await foreignToken["mint(address,uint256)"](proxyColony.address, tokenAmount); await tx.wait(); // Claim on the foreign chain let p = bridgeMonitor.getPromiseForNextBridgedTransaction(); - tx = await shellColony.claimTokens(foreignToken.address); + tx = await proxyColony.claimTokens(foreignToken.address); await tx.wait(); await p; @@ -1290,7 +1290,7 @@ contract("Cross-chain", (accounts) => { expect(balance1.toHexString()).to.equal(ethers.utils.parseEther("70").toHexString()); // Check actually paid on foreign chain - const colonyBalance = await foreignToken.balanceOf(shellColony.address); + const colonyBalance = await foreignToken.balanceOf(proxyColony.address); const recipientBalance = await foreignToken.balanceOf(accounts[0]); expect(colonyBalance.toHexString()).to.equal(ethers.utils.parseEther("70").toHexString()); @@ -1300,7 +1300,7 @@ contract("Cross-chain", (accounts) => { describe("making arbitrary transactions on another chain", async () => { let colony; - let shellColony; + let proxyColony; let foreignToken; beforeEach(async () => { colony = await setupColony(homeColonyNetwork); @@ -1313,48 +1313,48 @@ contract("Cross-chain", (accounts) => { const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); - const tx = await colony.createShellColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); + const tx = await colony.createProxyColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); await tx.wait(); await p; - shellColony = new ethers.Contract(colony.address, ShellColony.abi, ethersForeignSigner); + proxyColony = new ethers.Contract(colony.address, ProxyColony.abi, ethersForeignSigner); // Deploy a token on the foreign network const tokenFactory = new ethers.ContractFactory(MetaTxToken.abi, MetaTxToken.bytecode, ethersForeignSigner); foreignToken = await tokenFactory.deploy("Test Token", "TT", 18); await (await foreignToken.unlock()).wait(); - await (await foreignToken.setOwner(shellColony.address)).wait(); + await (await foreignToken.setOwner(proxyColony.address)).wait(); }); it.only("can make arbitrary transactions on the foreign chain", async () => { - const balanceBefore = await foreignToken.balanceOf(shellColony.address); + const balanceBefore = await foreignToken.balanceOf(proxyColony.address); const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); - const payload = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [shellColony.address, ethers.utils.parseEther("100")]); + const payload = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [proxyColony.address, ethers.utils.parseEther("100")]); const tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], [payload]); await tx.wait(); await p; - const balanceAfter = await foreignToken.balanceOf(shellColony.address); + const balanceAfter = await foreignToken.balanceOf(proxyColony.address); console.log(balanceBefore.toHexString(), balanceAfter.toHexString()); expect(balanceAfter.sub(balanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); }); it.only("can make multiple arbitrary transactions on the foreign chain in one go", async () => { - const shellBalanceBefore = await foreignToken.balanceOf(shellColony.address); + const shellBalanceBefore = await foreignToken.balanceOf(proxyColony.address); const colonyBalanceBefore = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); const p = bridgeMonitor.getPromiseForNextBridgedTransaction(2); - const payload1 = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [shellColony.address, ethers.utils.parseEther("100")]); - const payload2 = shellColony.interface.encodeFunctionData("claimTokens(address)", [foreignToken.address]); + const payload1 = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [proxyColony.address, ethers.utils.parseEther("100")]); + const payload2 = proxyColony.interface.encodeFunctionData("claimTokens(address)", [foreignToken.address]); - const tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address, shellColony.address], [payload1, payload2]); + const tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address, proxyColony.address], [payload1, payload2]); await tx.wait(); await p; - const shellBalanceAfter = await foreignToken.balanceOf(shellColony.address); + const shellBalanceAfter = await foreignToken.balanceOf(proxyColony.address); console.log(shellBalanceBefore.toHexString(), shellBalanceAfter.toHexString()); expect(shellBalanceAfter.sub(shellBalanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); From a1cb961b464999a81061b86c55a1cd11d2a1e90d Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 9 Aug 2024 15:18:48 +0100 Subject: [PATCH 13/72] Add hardhat deploy-proxy-network --- hardhat.config.js | 6 +++ helpers/upgradable-contracts.js | 4 +- test/deploy-proxy-network-fixture.js | 56 ++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 test/deploy-proxy-network-fixture.js diff --git a/hardhat.config.js b/hardhat.config.js index 9a16594770..959f28097c 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -108,6 +108,12 @@ task("deploy", "Deploy Colony Network as per truffle-fixture.js").setAction(asyn await deployNetwork(); }); +task("deploy-proxy-network", "Deploy Proxy Colony Network").setAction(async () => { + const deployProxyNetwork = require("./test/deploy-proxy-network-fixture"); // eslint-disable-line global-require + + await deployProxyNetwork(); +}); + module.exports = { defaultNetwork: "hardhat", solidity: { diff --git a/helpers/upgradable-contracts.js b/helpers/upgradable-contracts.js index 231b5b734d..67bb2a53b8 100644 --- a/helpers/upgradable-contracts.js +++ b/helpers/upgradable-contracts.js @@ -200,9 +200,9 @@ exports.setupENSRegistrar = async function setupENSRegistrar(colonyNetwork, ensR await ensRegistry.setSubnodeOwner(rootNode, COLONY_HASH, colonyNetwork.address); }; -exports.setupProxyColonyNetwork = async function setupProxyColonyNetwork(etherRouter, proxyColonyNetwork, resolver) { +exports.setupProxyColonyNetwork = async function setupProxyColonyNetwork(etherRouter, proxyColonyNetworkImplementation, resolver) { const deployedImplementations = {}; - deployedImplementations.ProxyColonyNetwork = proxyColonyNetwork.address; + deployedImplementations.ProxyColonyNetwork = proxyColonyNetworkImplementation.address; await exports.setupEtherRouter("bridging", "ProxyColonyNetwork", deployedImplementations, resolver); const txOrReceipt = await etherRouter.setResolver(resolver.address); diff --git a/test/deploy-proxy-network-fixture.js b/test/deploy-proxy-network-fixture.js new file mode 100644 index 0000000000..92d23a600a --- /dev/null +++ b/test/deploy-proxy-network-fixture.js @@ -0,0 +1,56 @@ +/* globals artifacts, hre */ + +const EtherRouter = artifacts.require("EtherRouter"); +const EtherRouterCreate3 = artifacts.require("EtherRouterCreate3"); +const Resolver = artifacts.require("Resolver"); + +const truffleContract = require("@truffle/contract"); +const createXABI = require("../lib/createx/artifacts/src/ICreateX.sol/ICreateX.json"); + +const { setupEtherRouter } = require("../helpers/upgradable-contracts"); +const { CREATEX_ADDRESS } = require("../helpers/constants"); + +const { setupProxyColonyNetwork } = require("../helpers/upgradable-contracts"); + +const ProxyColonyNetwork = artifacts.require("ProxyColonyNetwork"); +const ProxyColony = artifacts.require("ProxyColony"); + +module.exports = async () => { + const accounts = await web3.eth.getAccounts(); + + await hre.run("ensureCreateXDeployed"); + const CreateX = truffleContract({ abi: createXABI.abi }); + CreateX.setProvider(web3.currentProvider); + const createX = await CreateX.at(CREATEX_ADDRESS); + + // This is a fake instance of an etherRouter, just so we can call encodeABs + const fakeEtherRouter = await EtherRouterCreate3.at(CREATEX_ADDRESS); + const setOwnerData = fakeEtherRouter.contract.methods.setOwner(accounts[0]).encodeABI(); + + const tx = await createX.methods["deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))"]( + `0xb77d57f4959eafa0339424b83fcfaf9c15407461005e95d52076387600e2c1e9`, + EtherRouterCreate3.bytecode, + setOwnerData, + [0, 0], + { from: accounts[0] }, + ); + + const etherRouter = await EtherRouter.at(tx.logs.filter((log) => log.event === "ContractCreation")[0].args.newContract); + + const proxyColonyNetworkImplementation = await ProxyColonyNetwork.new(); + ProxyColonyNetwork.setAsDeployed(proxyColonyNetworkImplementation); + + let resolver = await Resolver.new(); + + await setupProxyColonyNetwork(etherRouter, proxyColonyNetworkImplementation, resolver); + + // Set up the resolver for shell colonies and register it with the network + + resolver = await Resolver.new(); + const proxyColonyImplementation = await ProxyColony.new(); + + await setupEtherRouter("bridging", "ProxyColony", { ProxyColony: proxyColonyImplementation.address }, resolver); + const proxyColonyNetwork = await ProxyColonyNetwork.at(etherRouter.address); + + await proxyColonyNetwork.setProxyColonyResolverAddress(resolver.address); +}; From 7765c23a23880bfd259d9205065d0be19221e884 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Sat, 10 Aug 2024 07:38:18 +0100 Subject: [PATCH 14/72] Move bridge data setting functions to setup-bridging-contracts --- helpers/constants.js | 2 + scripts/setup-bridging-contracts.js | 67 ++++++++++++++++++++++++++++- test/cross-chain/cross-chain.js | 42 ++---------------- 3 files changed, 70 insertions(+), 41 deletions(-) diff --git a/helpers/constants.js b/helpers/constants.js index 47404938f8..a13f2cbbd9 100644 --- a/helpers/constants.js +++ b/helpers/constants.js @@ -82,6 +82,7 @@ const MAINNET_CHAINID = 1; const FORKED_MAINNET_CHAINID = 2656691; const CREATEX_ADDRESS = "0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed"; +const NETWORK_ADDRESS = "0x777760996135F0791E2e1a74aFAa060711197777"; module.exports = { UINT256_MAX, @@ -140,4 +141,5 @@ module.exports = { MAINNET_CHAINID, FORKED_MAINNET_CHAINID, CREATEX_ADDRESS, + NETWORK_ADDRESS, }; diff --git a/scripts/setup-bridging-contracts.js b/scripts/setup-bridging-contracts.js index b134ff1ab9..734b885d69 100644 --- a/scripts/setup-bridging-contracts.js +++ b/scripts/setup-bridging-contracts.js @@ -10,7 +10,7 @@ const fs = require("fs"); const ethers = require("ethers"); // eslint-disable-line const { spawn } = require("child_process"); const { TruffleLoader } = require("../packages/package-utils"); -const { WAD } = require("../helpers/constants"); +const { WAD, NETWORK_ADDRESS } = require("../helpers/constants"); const loader = new TruffleLoader({ contractRoot: path.resolve(__dirname, "..", `artifacts${process.env.SOLIDITY_COVERAGE ? "-coverage" : ""}`, "contracts"), @@ -199,9 +199,72 @@ async function setupBridging(homeRpcUrl, foreignRpcUrl) { console.log(`Zodiac Bridge module address: ${zodiacBridge.address}`); console.log(`ERC721 address: ${erc721.address}`); console.log(`Token address: ${token.address}`); + + await setForeignBridgeData(homeColonyBridge.address, foreignColonyBridge.address, ethersHomeSigner, ethersForeignSigner); + await setHomeBridgeData(homeColonyBridge.address, foreignColonyBridge.address, ethersHomeSigner, ethersForeignSigner); + return { gnosisSafe, resetRelayer, guardianSpy, zodiacBridge, homeBridge, foreignBridge, homeColonyBridge, foreignColonyBridge }; } +async function setForeignBridgeData(homeColonyBridgeAddress, foreignColonyBridgeAddress, ethersHomeSigner, ethersForeignSigner) { + const contractDir = path.resolve(__dirname, "..", "artifacts", "contracts", "bridging"); + const WormholeBridgeForColony = await loader.load({ contractDir, contractName: "WormholeBridgeForColony" }); + const ProxyColonyNetwork = await loader.load({ contractDir, contractName: "ProxyColonyNetwork" }); + + const bridge = new ethers.Contract(foreignColonyBridgeAddress, WormholeBridgeForColony.abi, ethersForeignSigner); + + const homeChainId = (await ethersHomeSigner.provider.getNetwork()).chainId; + const foreignChainId = (await ethersForeignSigner.provider.getNetwork()).chainId; + + let tx = await bridge.setColonyBridgeAddress(foreignChainId, foreignColonyBridgeAddress); + await tx.wait(); + tx = await bridge.setColonyBridgeAddress(homeChainId, homeColonyBridgeAddress); + await tx.wait(); + + tx = await bridge.setColonyNetworkAddress(NETWORK_ADDRESS); + await tx.wait(); + + // TODO: Figure out a better way of setting / controlling this? + console.log("setting foreign colony bridge address", foreignColonyBridgeAddress); + const foreignColonyNetwork = new ethers.Contract(NETWORK_ADDRESS, ProxyColonyNetwork.abi, ethersForeignSigner); + tx = await foreignColonyNetwork.setColonyBridgeAddress(foreignColonyBridgeAddress); + await tx.wait(); + tx = await foreignColonyNetwork.setHomeChainId(homeChainId); + await tx.wait(); + console.log("done"); +} + +async function setHomeBridgeData(homeColonyBridgeAddress, foreignColonyBridgeAddress, ethersHomeSigner, ethersForeignSigner) { + let contractDir = path.resolve(__dirname, "..", "artifacts", "contracts", "bridging"); + const WormholeBridgeForColony = await loader.load({ contractDir, contractName: "WormholeBridgeForColony" }); + + contractDir = path.resolve(__dirname, "..", "artifacts", "contracts", "colonyNetwork"); + const IColonyNetwork = await loader.load({ contractDir, contractName: "IColonyNetwork" }); + + const bridge = new ethers.Contract(homeColonyBridgeAddress, WormholeBridgeForColony.abi, ethersHomeSigner); + const homeChainId = (await ethersHomeSigner.provider.getNetwork()).chainId; + const foreignChainId = (await ethersForeignSigner.provider.getNetwork()).chainId; + + let tx = await bridge.setColonyBridgeAddress(foreignChainId, foreignColonyBridgeAddress); + await tx.wait(); + tx = await bridge.setColonyBridgeAddress(homeChainId, homeColonyBridgeAddress); + await tx.wait(); + + tx = await bridge.setColonyNetworkAddress(NETWORK_ADDRESS); + await tx.wait(); + + const homeColonyNetwork = new ethers.Contract(NETWORK_ADDRESS, IColonyNetwork.abi, ethersHomeSigner); + const mcAddress = await homeColonyNetwork.getMetaColony(); + + contractDir = path.resolve(__dirname, "..", "artifacts", "contracts", "colony"); + const IMetaColony = await loader.load({ contractDir, contractName: "IMetaColony" }); + + const homeMetacolony = new ethers.Contract(mcAddress, IMetaColony.abi, ethersHomeSigner); + + tx = await homeMetacolony.setColonyBridgeAddress(homeColonyBridgeAddress); + await tx.wait(); +} + async function getSig(provider, account, dataHash) { const sig = await provider.send("eth_sign", [account, dataHash]); const r = `${sig.substring(2, 66)}`; @@ -241,4 +304,4 @@ if (process.argv.includes("start-bridging-environment")) { setupBridging("http://127.0.0.1:8545", "http://127.0.0.1:8546"); } -module.exports = { setupBridging, deployBridge }; +module.exports = { setupBridging, deployBridge, setHomeBridgeData, setForeignBridgeData }; diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 79d603711f..39dac88450 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -36,7 +36,7 @@ const ProxyColonyNetwork = artifacts.require("ProxyColonyNetwork"); const ProxyColony = artifacts.require("ProxyColony"); const MetaTxToken = artifacts.require("MetaTxToken"); // const { assert } = require("console"); -const { setupBridging, deployBridge } = require("../../scripts/setup-bridging-contracts"); +const { setupBridging, deployBridge, setForeignBridgeData, setHomeBridgeData } = require("../../scripts/setup-bridging-contracts"); const { MINING_CYCLE_DURATION, @@ -108,41 +108,6 @@ contract("Cross-chain", (accounts) => { const ethersForeignSigner2 = new ethers.providers.StaticJsonRpcProvider(foreignRpcUrl).getSigner(1); const ethersHomeSigner2 = new ethers.providers.StaticJsonRpcProvider(homeRpcUrl).getSigner(1); - async function setForeignBridgeData(foreignColonyBridgeForColony) { - const bridge = new ethers.Contract(foreignColonyBridge.address, WormholeBridgeForColony.abi, ethersForeignSigner); - - let tx = await bridge.setColonyBridgeAddress(foreignChainId, foreignColonyBridge.address); - await tx.wait(); - tx = await bridge.setColonyBridgeAddress(homeChainId, homeColonyBridge.address); - await tx.wait(); - - tx = await bridge.setColonyNetworkAddress(foreignColonyNetwork.address); - await tx.wait(); - - // TODO: Figure out a better way of setting / controlling this? - console.log("setting foreign colony bridge address", foreignColonyBridgeForColony); - tx = await foreignColonyNetwork.setColonyBridgeAddress(foreignColonyBridgeForColony); - await tx.wait(); - tx = await foreignColonyNetwork.setHomeChainId(homeChainId); - await tx.wait(); - console.log("done"); - } - - async function setHomeBridgeData(homeColonyBridgeAddressForColony) { - const bridge = new ethers.Contract(homeColonyBridge.address, WormholeBridgeForColony.abi, ethersHomeSigner); - - let tx = await bridge.setColonyBridgeAddress(foreignChainId, foreignColonyBridge.address); - await tx.wait(); - tx = await bridge.setColonyBridgeAddress(homeChainId, homeColonyBridge.address); - await tx.wait(); - - tx = await bridge.setColonyNetworkAddress(homeColonyNetwork.address); - await tx.wait(); - - tx = await homeMetacolony.setColonyBridgeAddress(homeColonyBridgeAddressForColony); - await tx.wait(); - } - before(async () => { await exec(`PORT=${FOREIGN_PORT} bash ./scripts/setup-foreign-chain.sh`); ({ guardianSpy, resetRelayer, gnosisSafe, zodiacBridge, homeBridge, foreignBridge, foreignColonyBridge, homeColonyBridge } = await setupBridging( @@ -243,9 +208,8 @@ contract("Cross-chain", (accounts) => { console.log("got mc"); homeMetacolony = await new ethers.Contract(homeMCAddress, IMetaColony.abi, ethersHomeSigner); - await setForeignBridgeData(foreignColonyBridge.address); - await setHomeBridgeData(homeColonyBridge.address); - + await setForeignBridgeData(homeColonyBridge.address, foreignColonyBridge.address, ethersHomeSigner, ethersForeignSigner); + await setHomeBridgeData(homeColonyBridge.address, foreignColonyBridge.address, ethersHomeSigner, ethersForeignSigner); // Bridge over skills that have been created on the foreign chain // const latestSkillId = await foreignColonyNetwork.getSkillCount(); From 81dc27bbe6d8880aa2460ab733f4ee1b55ef78d8 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 12 Aug 2024 11:42:10 +0100 Subject: [PATCH 15/72] setup-bridging-contracts can handle multiple remote chains --- hardhat.config.js | 5 + packages/wormhole-relayer/config.example.js | 4 + packages/wormhole-relayer/index.ts | 221 ++++++++++++++++++++ scripts/mockGuardianSpy.ts | 103 +++++++-- scripts/setup-bridging-contracts.js | 220 +++++++++++-------- 5 files changed, 445 insertions(+), 108 deletions(-) diff --git a/hardhat.config.js b/hardhat.config.js index 959f28097c..4c9a4b5539 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -215,6 +215,11 @@ module.exports = { cancun: 0, }, }, + 265669101: { + hardforkHistory: { + cancun: 0, + }, + }, }, }, }, diff --git a/packages/wormhole-relayer/config.example.js b/packages/wormhole-relayer/config.example.js index e3388242d5..53f4153aec 100644 --- a/packages/wormhole-relayer/config.example.js +++ b/packages/wormhole-relayer/config.example.js @@ -5,10 +5,14 @@ module.exports = { [wormhole.CHAIN_ID_ARBITRUM_SEPOLIA]: { endpoints: ["http://localhost:8545"], colonyBridgeAddress: "0x633899227A3BC1f79de097149E1E3C8097c07b1a", + payForGas: true, + evmChainId: 265669100, }, [wormhole.CHAIN_ID_SEPOLIA]: { endpoints: ["http://localhost:8546"], colonyBridgeAddress: "0x161944B5601a7d3004E20d4Ca823F710838Ea1be", + payForGas: true, + evmChainId: 265669101, }, }, }; diff --git a/packages/wormhole-relayer/index.ts b/packages/wormhole-relayer/index.ts index 2285266ce3..10e3940ecd 100644 --- a/packages/wormhole-relayer/index.ts +++ b/packages/wormhole-relayer/index.ts @@ -114,6 +114,7 @@ const loader = new TruffleLoader({ let destinationBridge; +<<<<<<< HEAD if (vaa.emitterChain === CHAIN_ID_ARBITRUM_SEPOLIA) { destinationBridge = colonyBridges[CHAIN_ID_SEPOLIA]; } else if (vaa.emitterChain === CHAIN_ID_SEPOLIA) { @@ -121,6 +122,83 @@ const loader = new TruffleLoader({ } else { console.log("Unknown chain", vaa.emitterChain); return next(); +||||||| parent of 45f052f5 (setup-bridging-contracts can handle multiple remote chains) + app.spy("localhost:7073"); + // app.useStorage(store); + // app.logger(console); + + // Set up middleware + // app.use(logging(console)); // <-- logging middleware + app.use(providers(config)); + // app.use(stagingArea()); + // app.use(sourceTx()); + + + // const colonyBridgeAddresses = { + // [CHAIN_ID_ARBITRUM_SEPOLIA]: "0x633899227A3BC1f79de097149E1E3C8097c07b1a", + // [CHAIN_ID_SEPOLIA]: "0x161944B5601a7d3004E20d4Ca823F710838Ea1be", + // }; + + const { TruffleLoader } = require("../package-utils"); + const path = require("path"); + const loader = new TruffleLoader({ + contractRoot: path.resolve(__dirname, "..", "..", "artifacts", "contracts") + }); + + const colonyBridges = {}; + const colonyBridgeContractDef = await loader.load({ contractDir: "bridging", contractName: "WormholeBridgeForColony" }); + const ethers = require('ethers'); + const privateKey = "0xfe6066af949ec3c2c88ac10f47907c6d4e200c37b28b5af49e7d0ffd5c301c5c"; + for (const chainId of Object.keys(config.chains)) { + const colonyBridgeAddress = config.chains[chainId].colonyBridgeAddress; + const providerAddress = config.chains[chainId].endpoints[0]; + const wallet = new ethers.Wallet(privateKey, new RetryProvider(providerAddress)); + const nonceManager = new NonceManager(wallet); + + colonyBridges[chainId] = new ethers.Contract(colonyBridgeAddress, colonyBridgeContractDef.abi, nonceManager); +======= + app.spy("localhost:7073"); + // app.useStorage(store); + // app.logger(console); + + // Set up middleware + // app.use(logging(console)); // <-- logging middleware + app.use(providers(config)); + // app.use(stagingArea()); + // app.use(sourceTx()); + + + // const colonyBridgeAddresses = { + // [CHAIN_ID_ARBITRUM_SEPOLIA]: "0x633899227A3BC1f79de097149E1E3C8097c07b1a", + // [CHAIN_ID_SEPOLIA]: "0x161944B5601a7d3004E20d4Ca823F710838Ea1be", + // }; + + const { TruffleLoader } = require("../package-utils"); + const path = require("path"); + const loader = new TruffleLoader({ + contractRoot: path.resolve(__dirname, "..", "..", "artifacts", "contracts") + }); + + const colonyBridges = {}; + const colonyBridgeContractDef = await loader.load({ contractDir: "bridging", contractName: "WormholeBridgeForColony" }); + const ethers = require('ethers'); + const privateKey = "0xfe6066af949ec3c2c88ac10f47907c6d4e200c37b28b5af49e7d0ffd5c301c5c"; + for (const chainId of Object.keys(config.chains)) { + const colonyBridgeAddress = config.chains[chainId].colonyBridgeAddress; + const providerAddress = config.chains[chainId].endpoints[0]; + const wallet = new ethers.Wallet(privateKey, new RetryProvider(providerAddress)); + const nonceManager = new NonceManager(wallet); + + colonyBridges[chainId] = new ethers.Contract(colonyBridgeAddress, colonyBridgeContractDef.abi, nonceManager); + const networkDetails = await wallet.provider.getNetwork(); + console.log('networkDetails', networkDetails) + if (networkDetails.chainId !== config.chains[chainId].evmChainId) { + console.log('Network details do not match config for chain', chainId); + console.log('Got an evmChainId of', networkDetails.chainId, 'but expected', config.chains[chainId].evmChainId); + console.log('Exiting'); + process.exit(1); + } +>>>>>>> 45f052f5 (setup-bridging-contracts can handle multiple remote chains) } try { @@ -147,6 +225,149 @@ const loader = new TruffleLoader({ // add and configure any other middleware .. +<<<<<<< HEAD // start app, blocks until unrecoverable error or process is stopped await app.listen(); })(); +||||||| parent of 45f052f5 (setup-bridging-contracts can handle multiple remote chains) + // add a filter with a callback that will be + // invoked on finding a VAA that matches the filter + + const colonyBridgeAddresses: { + [chainid: string]: string + } = {}; + + Object.keys(config.chains).forEach((chainid) => colonyBridgeAddresses[chainid] = config.chains[chainid].colonyBridgeAddress); + app.multiple( colonyBridgeAddresses, + async (ctx, next) => { + // console.log('callback'); + const vaa = ctx.vaa; + if (!vaa) { + return next(); + } + const hash = ctx.sourceTxHash; + // console.log(vaa); + console.log( + `Got a VAA with sequence: ${vaa.sequence} from with txhash: ${hash}`, + ); + + let destinationBridge; + + if (vaa.emitterChain === CHAIN_ID_ARBITRUM_SEPOLIA) { + destinationBridge = colonyBridges[CHAIN_ID_SEPOLIA]; + } else if (vaa.emitterChain === CHAIN_ID_SEPOLIA) { + destinationBridge = colonyBridges[CHAIN_ID_ARBITRUM_SEPOLIA]; + } else { + console.log('Unknown chain', vaa.emitterChain); + return next(); + } + + try { + // TODO: Explicit gas limit is a nod to tests... + const tx = await destinationBridge.receiveMessage(ctx.vaaBytes, { gasLimit: 1000000 }); + const r = await tx.wait(); + console.log('bridged with txhash' + tx.hash) + } catch(err) { + console.log("ERROR", err); + console.log('trying estimate gas with', err.transaction.to, err.transaction.data); + try { + + const errEst = await destinationBridge.provider.estimateGas({ + to: err.transaction.to, + data: err.transaction.data + }); + console.log('errEst', errEst.toString()); + } catch(err2){ + console.log('ERROR2', err2); + } + } + + next(); + }, + ); + + // add and configure any other middleware .. + + // start app, blocks until unrecoverable error or process is stopped + await app.listen(); + + })(); + +======= + // add a filter with a callback that will be + // invoked on finding a VAA that matches the filter + + const colonyBridgeAddresses: { + [chainid: string]: string + } = {}; + + Object.keys(config.chains).forEach((chainid) => colonyBridgeAddresses[chainid] = config.chains[chainid].colonyBridgeAddress); + app.multiple( colonyBridgeAddresses, + async (ctx, next) => { + // console.log('callback'); + const vaa = ctx.vaa; + if (!vaa) { + return next(); + } + const hash = ctx.sourceTxHash; + const [destinationEvmChainId, destinationAddress, payload] = (new ethers.utils.AbiCoder).decode(['uint256', 'address', 'bytes'],`0x${vaa.payload.toString('hex')}`); + console.log('destinationEvmChainId', destinationEvmChainId); + console.log('destinationAddress', destinationAddress); + console.log('payload', payload); + + + console.log( + `Got a VAA with sequence: ${vaa.sequence} from with txhash: ${hash}`, + ); + + const destinationChainConfig = Object.values(config.chains).find((c) => c.evmChainId === destinationEvmChainId.toNumber()) + if (!destinationChainConfig) { + console.log('No destination chain config found for chain id', destinationEvmChainId.toNumber()); + return next(); + } + + if (!destinationChainConfig.payForGas) { + console.log('We do not pay for gas on destination chain. Skipping'); + return next(); + } + + const destinationWormholeId = Object.keys(config.chains).find((wormholeChainId) => { return config.chains[wormholeChainId].evmChainId === destinationEvmChainId.toNumber() }); + if (!destinationWormholeId) { + console.log('No wormhole chain id found for destination chain id', destinationEvmChainId.toNumber()); + return next(); + } + + const destinationBridge = colonyBridges[destinationWormholeId]; + + try { + // TODO: Explicit gas limit is a nod to tests... + const tx = await destinationBridge.receiveMessage(ctx.vaaBytes, { gasLimit: 1000000 }); + const r = await tx.wait(); + console.log('bridged with txhash' + tx.hash) + } catch(err) { + console.log("ERROR", err); + console.log('trying estimate gas with', err.transaction.to, err.transaction.data); + try { + + const errEst = await destinationBridge.provider.estimateGas({ + to: err.transaction.to, + data: err.transaction.data + }); + console.log('errEst', errEst.toString()); + } catch(err2){ + console.log('ERROR2', err2); + } + } + + next(); + }, + ); + + // add and configure any other middleware .. + + // start app, blocks until unrecoverable error or process is stopped + await app.listen(); + + })(); + +>>>>>>> 45f052f5 (setup-bridging-contracts can handle multiple remote chains) diff --git a/scripts/mockGuardianSpy.ts b/scripts/mockGuardianSpy.ts index 140ddeb4b4..949d26394a 100644 --- a/scripts/mockGuardianSpy.ts +++ b/scripts/mockGuardianSpy.ts @@ -90,11 +90,11 @@ class MockGuardianSpy { foreignColonyBridgeAddress: string, ) { this.homeRpc = homeRpc; - this.foreignRpc = foreignRpc; + this.foreignRpcs = foreignRpcs; this.homeBridgeAddress = homeBridgeAddress; - this.foreignBridgeAddress = foreignBridgeAddress; + this.foreignBridgeAddresses = foreignBridgeAddresses; this.homeColonyBridgeAddress = homeColonyBridgeAddress; - this.foreignColonyBridgeAddress = foreignColonyBridgeAddress; + this.foreignColonyBridgeAddresses = foreignColonyBridgeAddresses; this.setupListeners(); @@ -177,12 +177,51 @@ class MockGuardianSpy { return vaaHeader + vaaBody.toString("hex").slice(2); } + setupForeignBridges(foreignRpc, foreignBridgeAddress, foreignColonyBridgeAddress) { + const signerForeign = new RetryProvider(foreignRpc).getSigner(); + const foreignBridge = new ethers.Contract(foreignBridgeAddress, bridgeAbi, signerForeign); + const foreignWormholeBridgeForColony = new ethers.Contract(foreignColonyBridgeAddress, wormholeBridgeForColonyAbi, signerForeign); + + this.foreignBridges.push(foreignBridge); + this.foreignWormholeBridgesForColony.push(foreignWormholeBridgeForColony); + } + + + async getColonyBridgeWithChainId(chainId) { + if ((await this.homeBridge.provider.getNetwork()).chainId === chainId) { + return this.homeBridge; + } + for (const foreignBridge of this.foreignWormholeBridgesForColony) { + if ((await foreignBridge.provider.getNetwork()).chainId === chainId) { + return foreignBridge; + } + } + throw new Error("No bridge found for chainId"); + }; + + getWormholeChainId(chainId) { + // Due to limitations, for local testing, our wormhole chainIDs have to be 'real' wormhole chainids. + // So I've decreed that for chainId 256669100, we use 10003 (which is really arbitrum sepolia) + // and for chainId 256669101, we use 10002 (which is really sepolia). + // This isn't ideal, but it's the best solution I have for now + if (chainId === 265669100) { + return 10003; + } else if (chainId === 265669101) { + return 10002; + } else if (chainId === 265669102) { + return 10005; + } + throw new Error("Unsupported chainId"); + } + setupListeners() { if (this.homeBridge) { this.homeBridge.removeAllListeners("LogMessagePublished"); } - if (this.foreignBridge) { - this.foreignBridge.removeAllListeners("LogMessagePublished"); + if (this.foreignBridges && this.foreignBridges.length > 0) { + for (const bridge of this.foreignBridges) { + bridge.removeAllListeners("LogMessagePublished"); + } } this.signerHome = new RetryProvider(this.homeRpc).getSigner(); @@ -193,6 +232,15 @@ class MockGuardianSpy { this.homeWormholeBridgeForColony = new ethers.Contract(this.homeColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerHome); this.foreignWormholeBridgeForColony = new ethers.Contract(this.foreignColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerForeign); + this.signerHome = new RetryProvider(this.homeRpc).getSigner(); + // this.signerForeign = new RetryProvider(this.foreignRpc).getSigner(); + this.homeBridge = new ethers.Contract(this.homeBridgeAddress, bridgeAbi, this.signerHome); + // this.foreignBridge = new ethers.Contract(this.foreignBridgeAddress, bridgeAbi, this.signerForeign); + this.homeWormholeBridgeForColony = new ethers.Contract(this.homeColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerHome); + // this.foreignWormholeBridgeForColony = new ethers.Contract(this.foreignColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerForeign); + for (let i = 0; i < this.foreignRpcs.length; i++) { + this.setupForeignBridges(this.foreignRpcs[i], this.foreignBridgeAddresses[i], this.foreignColonyBridgeAddresses[i]); + } this.skipCount = 0; this.queue = []; @@ -206,13 +254,20 @@ class MockGuardianSpy { // This isn't ideal, but it's the best solution I have for now const wormholeChainId = evmChainIdToWormholeChainId(chainId); - if (this.skipCount > 0) { - this.skipped.push([this.foreignWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]); - this.skipCount -= 1; - return; + const destinationBridge = await this.getColonyBridgeWithChainId(destinationEvmChainId.toNumber()); + const wormholeChainId = this.getWormholeChainId(chainId); + + if (this.skipCount > 0) { + this.skipped.push([destinationBridge, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]); + this.skipCount -= 1; + return; + } + this.queue.push([destinationBridge, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]); + await this.processQueue(); + } catch (e) { + console.log("Error in LogMessagePublished listener"); + console.log(e); } - this.queue.push([this.foreignWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]); - await this.processQueue(); }); this.foreignBridge.on("LogMessagePublished", async (sender, sequence, nonce, payload, consistencyLevel) => { @@ -223,19 +278,26 @@ class MockGuardianSpy { // This isn't ideal, but it's the best solution I have for now const wormholeChainId = evmChainIdToWormholeChainId(chainId); - if (this.skipCount > 0) { - this.skipped.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]); - this.skipCount -= 1; - return; - } - this.queue.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]); + if (destinationEvmChainId.toNumber() !== 265669100) { + throw new Error("Unsupported chainId - change assumptions in mockGuardianSpy.ts"); + } - await this.processQueue(); - }); + const wormholeChainId = this.getWormholeChainId(chainId); + + if (this.skipCount > 0) { + this.skipped.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]); + this.skipCount -= 1; + return; + } + this.queue.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]); + + await this.processQueue(); + }); + } console.log("Mock Bridge Monitor running"); console.log("Home bridge address: ", this.homeBridgeAddress); - console.log("Foreign bridge address: ", this.foreignBridgeAddress); + console.log("Foreign bridge addresses: ", this.foreignBridgeAddresses); } close() {} // eslint-disable-line class-methods-use-this @@ -275,7 +337,6 @@ class MockGuardianSpy { } this.bridgingPromiseCount -= 1; - if (this.bridgingPromiseCount === 0) { const receipt = await bridge.provider.getTransactionReceipt(tx.hash); this.resolveBridgingPromise(receipt); diff --git a/scripts/setup-bridging-contracts.js b/scripts/setup-bridging-contracts.js index 734b885d69..ca956fd47e 100644 --- a/scripts/setup-bridging-contracts.js +++ b/scripts/setup-bridging-contracts.js @@ -19,18 +19,22 @@ const loader = new TruffleLoader({ const ADDRESS_ZERO = ethers.constants.AddressZero; const MockGuardianSpy = require("./mockGuardianSpy").default; -async function setupBridging(homeRpcUrl, foreignRpcUrl) { +async function setupBridging(homeRpcUrl, foreignRpcUrls) { console.log("setup-bridging-contracts: Not to be used in production"); if (process.env.NODE_ENV === "production") { process.exit(1); } - - const ethersForeignProvider = new ethers.providers.StaticJsonRpcProvider(foreignRpcUrl); - const ethersForeignSigner = ethersForeignProvider.getSigner(); + const ethersForeignProviders = foreignRpcUrls.map((foreignRpcUrl) => new ethers.providers.StaticJsonRpcProvider(foreignRpcUrl)); + // const ethersForeignProvider = new ethers.providers.StaticJsonRpcProvider(foreignRpcUrl); + const ethersForeignSigners = ethersForeignProviders.map((provider) => provider.getSigner()); + // const ethersForeignSigner = ethersForeignProvider.getSigner(); const ethersHomeProvider = new ethers.providers.StaticJsonRpcProvider(homeRpcUrl); const ethersHomeSigner = ethersHomeProvider.getSigner(); + const homeChainId = (await ethersHomeSigner.provider.getNetwork()).chainId; + const foreignChainIds = await Promise.all(ethersForeignProviders.map((provider) => provider.getNetwork().then((network) => network.chainId))); + // const foreignChainId = (await ethersForeignSigner.provider.getNetwork()).chainId; - const accounts = await ethersForeignProvider.listAccounts(); + const accounts = await ethersHomeProvider.listAccounts(); let contractDir; contractDir = path.resolve(__dirname, "..", "artifacts", "lib", "safe-contracts", "contracts"); @@ -46,100 +50,130 @@ async function setupBridging(homeRpcUrl, foreignRpcUrl) { contractDir = path.resolve(__dirname, "..", "artifacts", "colonyToken"); const Token = await loader.load({ contractDir, contractName: "Token" }); - // This is the address that the gnosis safe proxy factory should have been deployed to by the deploy command using hardhat in their repo - const gspf = new ethers.Contract("0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2", GnosisSafeProxyFactory.abi, ethersForeignSigner); - - // 0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552 is the address the gnosis safe implementation should have been deployed at - let receipt = await gspf.createProxy("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552", "0x"); - let tx = await receipt.wait(); - - const safeAddress = tx.events[0].args.proxy; - const gnosisSafe = new ethers.Contract(safeAddress, GnosisSafe.abi, ethersForeignSigner); - console.log("Gnosis Safe address: ", gnosisSafe.address); - - receipt = await gnosisSafe.setup([accounts[0]], 1, ADDRESS_ZERO, "0x", ADDRESS_ZERO, ADDRESS_ZERO, 0, ADDRESS_ZERO); - await receipt.wait(); - - const zodiacBridgeFactory = new ethers.ContractFactory(ZodiacBridgeModuleMock.abi, ZodiacBridgeModuleMock.bytecode, ethersForeignSigner); - const zodiacBridge = await zodiacBridgeFactory.deploy(safeAddress); - await zodiacBridge.deployTransaction.wait(); - console.log("Bridge module address: ", zodiacBridge.address); - - const erc721MockFactory = new ethers.ContractFactory(Erc721Mock.abi, Erc721Mock.bytecode, ethersForeignSigner); - const erc721 = await erc721MockFactory.deploy(); - await erc721.deployTransaction.wait(); - console.log("ERC721 address: ", erc721.address); - - const tokenId = 1; - const mintTx = await erc721.mint(gnosisSafe.address, tokenId); - await mintTx.wait(); - const inventory = await erc721.balanceOf(gnosisSafe.address); + const foreignBridgeAddresses = []; + const foreignColonyBridgeAddresses = []; + const foreignBridges = []; + const foreignColonyBridges = []; + const gnosisSafes = []; + const zodiacBridges = []; + + // eslint-disable-next-line no-restricted-syntax + for (const ethersForeignSigner of ethersForeignSigners) { + // This is the address that the gnosis safe proxy factory should have been deployed to by the deploy command using hardhat in their repo + const gspf = new ethers.Contract("0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2", GnosisSafeProxyFactory.abi, ethersForeignSigner); + + // 0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552 is the address the gnosis safe implementation should have been deployed at + let receipt = await gspf.createProxy("0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552", "0x"); + let tx = await receipt.wait(); + + const safeAddress = tx.events[0].args.proxy; + const gnosisSafe = new ethers.Contract(safeAddress, GnosisSafe.abi, ethersForeignSigner); + console.log("Gnosis Safe address: ", gnosisSafe.address); + + receipt = await gnosisSafe.setup([accounts[0]], 1, ADDRESS_ZERO, "0x", ADDRESS_ZERO, ADDRESS_ZERO, 0, ADDRESS_ZERO); + await receipt.wait(); + + const zodiacBridgeFactory = new ethers.ContractFactory(ZodiacBridgeModuleMock.abi, ZodiacBridgeModuleMock.bytecode, ethersForeignSigner); + const zodiacBridge = await zodiacBridgeFactory.deploy(safeAddress); + await zodiacBridge.deployTransaction.wait(); + console.log("Bridge module address: ", zodiacBridge.address); + + const erc721MockFactory = new ethers.ContractFactory(Erc721Mock.abi, Erc721Mock.bytecode, ethersForeignSigner); + const erc721 = await erc721MockFactory.deploy(); + await erc721.deployTransaction.wait(); + console.log("ERC721 address: ", erc721.address); + + const tokenId = 1; + const mintTx = await erc721.mint(gnosisSafe.address, tokenId); + await mintTx.wait(); + const inventory = await erc721.balanceOf(gnosisSafe.address); + + console.log(`Safe ${gnosisSafe.address} contains ${inventory} NFT.`); // Should eq 1. + if (inventory.toString() !== "1") { + console.log("Safe did not contain exactly 1 NFT"); + process.exit(); + } - console.log(`Safe ${gnosisSafe.address} contains ${inventory} NFT.`); // Should eq 1. - if (inventory.toString() !== "1") { - console.log("Safe did not contain exactly 1 NFT"); - process.exit(); - } + const TokenFactory = new ethers.ContractFactory(Token.abi, Token.bytecode, ethersForeignSigner); + const token = await TokenFactory.deploy("Test", "TST", 18); + await token.deployTransaction.wait(); + console.log("Token address: ", Token.address); - const TokenFactory = new ethers.ContractFactory(Token.abi, Token.bytecode, ethersForeignSigner); - const token = await TokenFactory.deploy("Test", "TST", 18); - await token.deployTransaction.wait(); - console.log("Token address: ", Token.address); + await token.unlock(); + const mintTokensTx = await token["mint(address,uint256)"](gnosisSafe.address, WAD.muln(100).toString()); + await mintTokensTx.wait(); + const safeBalance = await token.balanceOf(gnosisSafe.address); - await token.unlock(); - const mintTokensTx = await token["mint(address,uint256)"](gnosisSafe.address, WAD.muln(100).toString()); - await mintTokensTx.wait(); - const safeBalance = await token.balanceOf(gnosisSafe.address); + console.log(`Safe ${gnosisSafe.address} contains ${safeBalance} tokens.`); // Should eq 100000000000000000000. + if (safeBalance.toString() !== "100000000000000000000") { + console.log("Safe did not contain exactly 100000000000000000000 tokens after minting"); + process.exit(); + } - console.log(`Safe ${gnosisSafe.address} contains ${safeBalance} tokens.`); // Should eq 100000000000000000000. - if (safeBalance.toString() !== "100000000000000000000") { - console.log("Safe did not contain exactly 100000000000000000000 tokens after minting"); - process.exit(); - } + // Add bridge module to safe - // Add bridge module to safe + const nonce = await gnosisSafe.nonce(); - const nonce = await gnosisSafe.nonce(); + const data = gnosisSafe.interface.encodeFunctionData("enableModule(address)", [zodiacBridge.address]); + const safeTxArgs = [safeAddress, 0, data, 0, 100000, 100000, 0, ADDRESS_ZERO, ADDRESS_ZERO, nonce]; + const safeData = await gnosisSafe.encodeTransactionData(...safeTxArgs); + const safeDataHash = await gnosisSafe.getTransactionHash(...safeTxArgs); - const data = gnosisSafe.interface.encodeFunctionData("enableModule(address)", [zodiacBridge.address]); - const safeTxArgs = [safeAddress, 0, data, 0, 100000, 100000, 0, ADDRESS_ZERO, ADDRESS_ZERO, nonce]; - const safeData = await gnosisSafe.encodeTransactionData(...safeTxArgs); - const safeDataHash = await gnosisSafe.getTransactionHash(...safeTxArgs); + const sig = await getSig(ethersForeignSigner.provider, accounts[0], safeDataHash); - const sig = await getSig(ethersForeignProvider, accounts[0], safeDataHash); + await gnosisSafe.checkNSignatures(safeDataHash, safeData, sig, 1); - await gnosisSafe.checkNSignatures(safeDataHash, safeData, sig, 1); + tx = await gnosisSafe.execTransaction(...safeTxArgs.slice(0, -1), sig); - tx = await gnosisSafe.execTransaction(...safeTxArgs.slice(0, -1), sig); + const enabled = await gnosisSafe.isModuleEnabled(zodiacBridge.address); - const enabled = await gnosisSafe.isModuleEnabled(zodiacBridge.address); + if (!enabled) { + console.log("Gnosis safe did not have bridge module enabled, exiting"); + process.exit(1); + } - if (!enabled) { - console.log("Gnosis safe did not have bridge module enabled, exiting"); - process.exit(1); + // Deploy a foreign bridge + const [foreignBridge, foreignColonyBridge] = await deployBridge(ethersForeignSigner); + const foreignChainId = (await ethersForeignSigner.provider.getNetwork()).chainId; + + foreignBridgeAddresses.push(foreignBridge.address); + foreignColonyBridgeAddresses.push(foreignColonyBridge.address); + + console.log(`On chain ${foreignChainId}:`); + console.log(`foreign bridge address: ${foreignBridge.address}`); + console.log(`foreign colony bridge address: ${foreignColonyBridge.address}`); + console.log(`foreign rpc url: ${foreignRpcUrls[0]}`); + console.log(`gnosis safe address: ${gnosisSafe.address}`); + console.log(`zodiac bridge module address: ${zodiacBridge.address}`); + console.log(`erc721 address: ${erc721.address}`); + console.log(`token address: ${token.address}`); + + foreignBridges.push(foreignBridge); + foreignColonyBridges.push(foreignColonyBridge); + gnosisSafes.push(gnosisSafe); + zodiacBridges.push(zodiacBridge); } - // Deploy a foreign bridge - const [foreignBridge, foreignColonyBridge] = await deployBridge(ethersForeignSigner); - // Deploy a home bridge const [homeBridge, homeColonyBridge] = await deployBridge(ethersHomeSigner); // Start the bridge service console.log(`Home RPC Url: ${homeRpcUrl}`); - console.log(`Foreign RPC Url: ${foreignRpcUrl}`); + // console.log(`Foreign RPC Url: ${foreignRpcUrl}`); const guardianSpy = new MockGuardianSpy( homeRpcUrl, - foreignRpcUrl, + foreignRpcUrls, homeBridge.address, - foreignBridge.address, + foreignBridgeAddresses, homeColonyBridge.address, - foreignColonyBridge.address, + foreignColonyBridgeAddresses, ); // eslint-disable-line no-unused-vars // TODO: Start the bridge monitor console.log("Starting bridge monitor"); + const foreignWormholeChainIds = ["wormhole.CHAIN_ID_SEPOLIA", "wormhole.CHAIN_ID_OPTIMISM_SEPOLIA"]; + // Write config file const config = ` const wormhole = require("@certusone/wormhole-sdk"); @@ -149,12 +183,20 @@ async function setupBridging(homeRpcUrl, foreignRpcUrl) { [wormhole.CHAIN_ID_ARBITRUM_SEPOLIA]: { endpoints: ["${homeRpcUrl}"], colonyBridgeAddress: "${homeColonyBridge.address}", - }, - [wormhole.CHAIN_ID_SEPOLIA]: { + payForGas: true, + evmChainId: ${homeChainId} + },${foreignRpcUrls + .map( + (foreignRpcUrl, index) => ` + [${foreignWormholeChainIds[index]}]: { endpoints: ["${foreignRpcUrl}"], - colonyBridgeAddress: "${foreignColonyBridge.address}", - }, - }, + colonyBridgeAddress: "${foreignColonyBridgeAddresses[index]}", + payForGas: ${index % 2 === 0}, + evmChainId: ${foreignChainIds[index]} + },`, + ) + .join("")} + }, }; `; fs.writeFileSync(path.resolve(__dirname, "..", "packages", "wormhole-relayer", "config.js"), config); @@ -192,16 +234,18 @@ async function setupBridging(homeRpcUrl, foreignRpcUrl) { } console.log(`Home bridge address: ${homeBridge.address}`); - console.log(`Foreign bridge address: ${foreignBridge.address}`); - console.log(`Home colony bridge address: ${homeColonyBridge.address}`); - console.log(`Foreign colony bridge address: ${foreignColonyBridge.address}`); - console.log(`Gnosis Safe address: ${gnosisSafe.address}`); - console.log(`Zodiac Bridge module address: ${zodiacBridge.address}`); - console.log(`ERC721 address: ${erc721.address}`); - console.log(`Token address: ${token.address}`); - - await setForeignBridgeData(homeColonyBridge.address, foreignColonyBridge.address, ethersHomeSigner, ethersForeignSigner); - await setHomeBridgeData(homeColonyBridge.address, foreignColonyBridge.address, ethersHomeSigner, ethersForeignSigner); + console.log(`Foreign bridge addresses: ${foreignBridgeAddresses.join(", ")}`); + console.log(`Home colony bridge addresses: ${homeColonyBridge.address}`); + console.log(`Foreign colony bridge addresses: ${foreignColonyBridgeAddresses.join(", ")}`); + // console.log(`Gnosis Safe addresses: ${gnosisSafe.address}`); + // console.log(`Zodiac Bridge module addresses: ${zodiacBridge.address}`); + // console.log(`ERC721 addresses: ${erc721.address}`); + // console.log(`Token addresses: ${token.address}`); + + for (let i = 0; i < ethersForeignSigners.length; i += 1) { + await setForeignBridgeData(homeColonyBridge.address, foreignColonyBridgeAddresses[i], ethersHomeSigner, ethersForeignSigners[i]); + await setHomeBridgeData(homeColonyBridge.address, foreignColonyBridgeAddresses[i], ethersHomeSigner, ethersForeignSigners[i]); + } return { gnosisSafe, resetRelayer, guardianSpy, zodiacBridge, homeBridge, foreignBridge, homeColonyBridge, foreignColonyBridge }; } @@ -294,7 +338,9 @@ async function deployBridge(signer) { let tx = await bridge.setWormholeAddress(wormhole.address); await tx.wait(); - tx = await bridge.setChainIdMapping([265669100, 265669101], [10003, 10002]); + + // TODO: These aren't all needed on every bridge + tx = await bridge.setChainIdMapping([265669100, 265669101, 265669102], [10003, 10002, 10005]); await tx.wait(); return [wormhole, bridge]; From 0172181765375840116ebecc73704c319f45169d Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Tue, 27 Aug 2024 16:50:56 +0100 Subject: [PATCH 16/72] Add ability to call ProxyNetwork cross-chain; remove old cross-chain functionality --- contracts/bridging/ProxyColonyNetwork.sol | 7 +- .../colony/ColonyArbitraryTransaction.sol | 12 + contracts/colony/IMetaColony.sol | 5 + contracts/colonyNetwork/ColonyNetwork.sol | 7 + .../colonyNetwork/ColonyNetworkDataTypes.sol | 26 --- .../colonyNetwork/ColonyNetworkDeployer.sol | 3 - .../colonyNetwork/ColonyNetworkMining.sol | 34 --- .../colonyNetwork/ColonyNetworkSkills.sol | 220 ------------------ contracts/colonyNetwork/IColonyNetwork.sol | 93 +------- test/cross-chain/cross-chain.js | 107 ++++++--- 10 files changed, 104 insertions(+), 410 deletions(-) diff --git a/contracts/bridging/ProxyColonyNetwork.sol b/contracts/bridging/ProxyColonyNetwork.sol index 0a92bc272e..ea63205755 100644 --- a/contracts/bridging/ProxyColonyNetwork.sol +++ b/contracts/bridging/ProxyColonyNetwork.sol @@ -59,7 +59,12 @@ contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards { _; } - function setColonyBridgeAddress(address _bridgeAddress) public auth { + modifier ownerOrBridge() { + require(msgSender() == colonyBridgeAddress || msgSender() == owner, "colony-network-caller-must-be-owner-or-bridge"); + _; + } + + function setColonyBridgeAddress(address _bridgeAddress) public ownerOrBridge { // TODO: Move this somewhere else to guard against unsupported chainids // require(_chainId <= type(uint128).max, "colony-network-chainid-too-large"); diff --git a/contracts/colony/ColonyArbitraryTransaction.sol b/contracts/colony/ColonyArbitraryTransaction.sol index 3fc6c39740..4df5758111 100644 --- a/contracts/colony/ColonyArbitraryTransaction.sol +++ b/contracts/colony/ColonyArbitraryTransaction.sol @@ -57,6 +57,18 @@ contract ColonyArbitraryTransaction is ColonyStorage { IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, payload); } + function callProxyNetwork( + uint256 _chainId, + bytes[] memory _actions + ) public stoppable auth returns (bool) { + bytes memory payload = abi.encodeWithSignature( + "multicall(bytes[])", + _actions + ); + + IColonyNetwork(colonyNetworkAddress).bridgeMessageToNetwork(_chainId, payload); + } + function makeArbitraryTransactions( address[] memory _targets, bytes[] memory _actions, diff --git a/contracts/colony/IMetaColony.sol b/contracts/colony/IMetaColony.sol index 67cc1768b3..fb2c66fac9 100644 --- a/contracts/colony/IMetaColony.sol +++ b/contracts/colony/IMetaColony.sol @@ -66,4 +66,9 @@ interface IMetaColony is IColony { bytes32 newHash, uint256 newNLeaves ) external; + + function callProxyNetwork( + uint256 _chainId, + bytes[] memory _actions + ) external; } diff --git a/contracts/colonyNetwork/ColonyNetwork.sol b/contracts/colonyNetwork/ColonyNetwork.sol index 78d4588157..1971170a28 100644 --- a/contracts/colonyNetwork/ColonyNetwork.sol +++ b/contracts/colonyNetwork/ColonyNetwork.sol @@ -119,6 +119,13 @@ contract ColonyNetwork is BasicMetaTransaction, ColonyNetworkStorage, Multicall ); } + function bridgeMessageToNetwork(uint256 _chainId, bytes memory _payload) public calledByMetaColony { + require( + IColonyBridge(colonyBridgeAddress).sendMessage(_chainId, address(this), _payload), + "colony-network-bridge-message-failed" + ); + } + function getColony(uint256 _id) public view returns (address) { return colonies[_id]; } diff --git a/contracts/colonyNetwork/ColonyNetworkDataTypes.sol b/contracts/colonyNetwork/ColonyNetworkDataTypes.sol index 3f4c3f0e68..38a0f38852 100755 --- a/contracts/colonyNetwork/ColonyNetworkDataTypes.sol +++ b/contracts/colonyNetwork/ColonyNetworkDataTypes.sol @@ -67,19 +67,6 @@ interface ColonyNetworkDataTypes { /// @param parentSkillId The id of the parent skill under which this new skill is added event SkillAdded(uint256 skillId, uint256 parentSkillId); - /// @notice Event logged when bridging of a skill creation did not succeed. - /// @param skillId The skillId that failed to bridge - event SkillCreationStored(uint256 skillId); - - /// @notice Event logged when a skill is received from a bridge, but can't yet be - /// added to the skill tree. - /// @param skillId The skillId of the skill that was bridged - event SkillStoredFromBridge(uint256 skillId); - - /// @notice Event logged when a skill is successfully added from a bridge. - /// @param skillId The skillId of the skill that was bridged - event SkillAddedFromBridge(uint256 skillId); - /// @notice Event logged when a new auction is created and started /// @dev Emitted from `IColonyNetwork.startTokenAuction` function /// @param auction Address of the created auction contract @@ -174,19 +161,6 @@ interface ColonyNetworkDataTypes { /// @param count The number of the reputation update trying to be bridged in that colony event ReputationUpdateSentToBridge(address colony, uint256 count); - /// @notice Event logged when a reputation update is received from a bridge, but can't be - /// added to the reputation update log due to being bridged out of order or the skill not existing. - /// @param chainId The chainId of the chain the bridge is associated with - /// @param colony The address of the colony where reputation is being emitted - /// @param updateNumber The number of the reputation update bridged in that colony - event ReputationUpdateStoredFromBridge(uint256 chainId, address colony, uint256 updateNumber); - - /// @notice Event logged when a reputation update is successfully bridged. - /// @param chainId The chainId of the chain the bridge is associated with - /// @param colony The address of the colony where reputation is being emitted - /// @param updateNumber The number of the reputation update bridged in that colony - event ReputationUpdateAddedFromBridge(uint256 chainId, address colony, uint256 updateNumber); - struct Skill { // total number of parent skills uint128 nParents; diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index a082cb337c..a9fc321e12 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -290,9 +290,6 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage { // Initialise the domain tree with defaults by just incrementing the skillCount skillCount += 1; - // If we're not mining chain, then bridge the skill - IColonyNetwork(address(this)).bridgeSkillIfNotMiningChain(skillCount); - colony.initialiseColony(address(this), _tokenAddress); emit ColonyAdded(colonyCount, address(etherRouter), _tokenAddress); diff --git a/contracts/colonyNetwork/ColonyNetworkMining.sol b/contracts/colonyNetwork/ColonyNetworkMining.sol index 05fc167474..e2fe1417ad 100644 --- a/contracts/colonyNetwork/ColonyNetworkMining.sol +++ b/contracts/colonyNetwork/ColonyNetworkMining.sol @@ -98,40 +98,6 @@ contract ColonyNetworkMining is ColonyNetworkStorage { // Well this is a weird hack to need function newAddressArray() internal pure returns (address[] memory) {} - function setReputationRootHashFromBridge( - bytes32 _newHash, - uint256 _newNLeaves, - uint256 _nonce - ) public stoppable onlyNotMiningChain onlyColonyBridge { - require( - _nonce >= bridgeCurrentRootHashNonces[block.chainid], - "colony-mining-bridge-invalid-nonce" - ); - bridgeCurrentRootHashNonces[block.chainid] = _nonce; - reputationRootHash = _newHash; - reputationRootHashNLeaves = _newNLeaves; - - emit ReputationRootHashSet(_newHash, _newNLeaves, newAddressArray(), 0); - } - - function bridgeCurrentRootHash(uint256 _chainId) public onlyMiningChain stoppable { - require(colonyBridgeAddress != address(0x0), "colony-network-bridge-not-set"); - - bridgeCurrentRootHashNonces[_chainId] += 1; - - bytes memory payload = abi.encodeWithSignature( - "setReputationRootHashFromBridge(bytes32,uint256,uint256)", - reputationRootHash, - reputationRootHashNLeaves, - bridgeCurrentRootHashNonces[_chainId] - ); - - // slither-disable-next-line unchecked-lowlevel - // bool success = IColonyBridge(colonyBridgeAddress).sendMessage(_chainId, payload); - // We require success so estimation calls can tell us if bridging is going to work - // require(success, "colony-mining-bridge-call-failed"); - } - function setReputationRootHash( bytes32 newHash, uint256 newNLeaves, diff --git a/contracts/colonyNetwork/ColonyNetworkSkills.sol b/contracts/colonyNetwork/ColonyNetworkSkills.sol index cf6b68fd12..0a03ee1d9d 100644 --- a/contracts/colonyNetwork/ColonyNetworkSkills.sol +++ b/contracts/colonyNetwork/ColonyNetworkSkills.sol @@ -33,8 +33,6 @@ contract ColonyNetworkSkills is ColonyNetworkStorage, Multicall { skillCount += 1; addSkillToChainTree(_parentSkillId, skillCount); - bridgeSkillIfNotMiningChain(skillCount); - return skillCount; } @@ -44,8 +42,6 @@ contract ColonyNetworkSkills is ColonyNetworkStorage, Multicall { function initialiseRootLocalSkill() public stoppable calledByColony returns (uint256) { skillCount += 1; - // If we're not mining chain, then bridge the skill - bridgeSkillIfNotMiningChain(skillCount); return skillCount; } @@ -81,193 +77,6 @@ contract ColonyNetworkSkills is ColonyNetworkStorage, Multicall { emit BridgeSet(_bridgeAddress); } - function bridgeSkillIfNotMiningChain(uint256 _skillId) public stoppable skillExists(_skillId) { - if (isMiningChain()) { - return; - } - // Build the transaction we're going to send to the bridge to register the - // creation of this skill on the home chain - uint256 parentSkillId = skills[_skillId].parents.length == 0 - ? (toRootSkillId(block.chainid)) - : skills[_skillId].parents[0]; - - bytes memory payload = abi.encodeWithSignature( - "addSkillFromBridge(uint256,uint256)", - parentSkillId, - _skillId - ); - - // Send bridge transaction - bool success = callThroughBridgeWithGuards(payload); - - if (!success) { - // Skill creation is implicitly stored by the fact that skillCount has been incremented - emit SkillCreationStored(_skillId); - } - } - - function bridgePendingReputationUpdate( - address _colony, - uint256 _updateNumber - ) public stoppable onlyNotMiningChain { - require(colonyBridgeAddress != address(0x0), "colony-network-foreign-bridge-not-set"); - require( - pendingReputationUpdates[block.chainid][_colony][_updateNumber - 1].colony == address(0x00), - "colony-network-not-next-pending-update" - ); - - PendingReputationUpdate storage pendingUpdate = pendingReputationUpdates[block.chainid][ - _colony - ][_updateNumber]; - require(pendingUpdate.colony != address(0x00), "colony-network-update-does-not-exist"); - - int256 updateAmount = decayReputation(pendingUpdate.amount, pendingUpdate.timestamp); - - // Build the transaction we're going to send to the bridge - bytes memory payload = abi.encodeWithSignature( - "addReputationUpdateLogFromBridge(address,address,int256,uint256,uint256)", - pendingUpdate.colony, - pendingUpdate.user, - updateAmount, - pendingUpdate.skillId, - _updateNumber - ); - - delete pendingReputationUpdates[block.chainid][_colony][_updateNumber]; - - bool success = callThroughBridgeWithGuards(payload); - - require(success, "colony-network-bridging-tx-unsuccessful"); - - emit ReputationUpdateSentToBridge(_colony, _updateNumber); - } - - // Bridging (receiving) - - function addSkillFromBridge( - uint256 _parentSkillId, - uint256 _skillId - ) public always onlyMiningChain onlyColonyBridge { - uint256 bridgeChainId = toChainId(_skillId); - if (networkSkillCounts[bridgeChainId] == 0) { - // Initialise the skill count to match the foreign chain - networkSkillCounts[bridgeChainId] = toRootSkillId(bridgeChainId); - } - - require(networkSkillCounts[bridgeChainId] < _skillId, "colony-network-skill-already-added"); - - // Check skill count - if not next, then store for later. - if (networkSkillCounts[bridgeChainId] + 1 == _skillId) { - addSkillToChainTree(_parentSkillId, _skillId); - networkSkillCounts[bridgeChainId] += 1; - - emit SkillAddedFromBridge(_skillId); - } else { - require( - pendingSkillAdditions[bridgeChainId][_skillId] == 0, - "colony-network-skill-already-pending" - ); - - pendingSkillAdditions[bridgeChainId][_skillId] = _parentSkillId; - - emit SkillStoredFromBridge(_skillId); - } - } - - function addReputationUpdateLogFromBridge( - address _colony, - address _user, - int256 _amount, - uint256 _skillId, - uint256 _updateNumber - ) public stoppable onlyMiningChain onlyColonyBridge { - uint256 bridgeChainId = toChainId(_skillId); - - require( - reputationUpdateCount[bridgeChainId][_colony] < _updateNumber, - "colony-network-update-already-added" - ); - - // If next expected update, add to log - if ( - reputationUpdateCount[bridgeChainId][_colony] + 1 == _updateNumber && // It's the next reputation update for this colony - networkSkillCounts[bridgeChainId] >= _skillId // Skill has been bridged - ) { - reputationUpdateCount[bridgeChainId][_colony] += 1; - appendReputationUpdateLogInternal(_user, _amount, _skillId, _colony); - - emit ReputationUpdateAddedFromBridge(bridgeChainId, _colony, _updateNumber); - } else { - // Not next update, store for later - require( - pendingReputationUpdates[bridgeChainId][_colony][_updateNumber].timestamp == 0, - "colony-network-update-already-pending" - ); - pendingReputationUpdates[bridgeChainId][_colony][_updateNumber] = PendingReputationUpdate( - _user, - _amount, - _skillId, - _colony, - block.timestamp - ); - - emit ReputationUpdateStoredFromBridge(bridgeChainId, _colony, _updateNumber); - } - } - - function addPendingSkill(uint256 _skillId) public always onlyMiningChain { - uint256 bridgeChainId = toChainId(_skillId); - - // Require that specified skill is next - // Note this also implicitly checks that the chainId prefix of the skill is correct - require( - networkSkillCounts[bridgeChainId] + 1 == _skillId, - "colony-network-not-next-bridged-skill" - ); - - uint256 parentSkillId = pendingSkillAdditions[bridgeChainId][_skillId]; - require(parentSkillId != 0, "colony-network-no-such-bridged-skill"); - addSkillToChainTree(parentSkillId, _skillId); - networkSkillCounts[bridgeChainId] += 1; - - // Delete the pending addition - delete pendingSkillAdditions[bridgeChainId][_skillId]; - - emit SkillAddedFromBridge(_skillId); - } - - function addPendingReputationUpdate( - uint256 _chainId, - address _colony - ) public stoppable onlyMiningChain { - uint256 mostRecentUpdateNumber = reputationUpdateCount[_chainId][_colony]; - assert( - pendingReputationUpdates[_chainId][_colony][mostRecentUpdateNumber].colony == address(0x00) - ); - - PendingReputationUpdate storage pendingUpdate = pendingReputationUpdates[_chainId][_colony][ - mostRecentUpdateNumber + 1 - ]; - require(pendingUpdate.colony != address(0x00), "colony-network-next-update-does-not-exist"); - - // Skill creation must have been bridged - require( - networkSkillCounts[toChainId(pendingUpdate.skillId)] >= pendingUpdate.skillId, - "colony-network-invalid-skill-id" - ); - - reputationUpdateCount[_chainId][_colony] += 1; - address user = pendingUpdate.user; - uint256 skillId = pendingUpdate.skillId; - int256 updateAmount = decayReputation(pendingUpdate.amount, pendingUpdate.timestamp); - - delete pendingReputationUpdates[_chainId][_colony][mostRecentUpdateNumber + 1]; - - appendReputationUpdateLogInternal(user, updateAmount, skillId, _colony); - - emit ReputationUpdateAddedFromBridge(_chainId, _colony, mostRecentUpdateNumber + 1); - } - // View function getParentSkillId( @@ -297,35 +106,6 @@ contract ColonyNetworkSkills is ColonyNetworkStorage, Multicall { return colonyBridgeAddress; } - function getBridgedSkillCounts(uint256 _chainId) public view returns (uint256) { - if (networkSkillCounts[_chainId] == 0) { - return toRootSkillId(_chainId); - } - return networkSkillCounts[_chainId]; - } - - function getBridgedReputationUpdateCount( - uint256 _chainId, - address _colony - ) public view returns (uint256) { - return reputationUpdateCount[_chainId][_colony]; - } - - function getPendingSkillAddition( - uint256 _chainId, - uint256 _skillCount - ) public view returns (uint256) { - return pendingSkillAdditions[_chainId][_skillCount]; - } - - function getPendingReputationUpdate( - uint256 _chainId, - address _colony, - uint256 _updateNumber - ) public view onlyMiningChain returns (PendingReputationUpdate memory) { - return pendingReputationUpdates[_chainId][_colony][_updateNumber]; - } - // Internal function addSkillToChainTree(uint256 _parentSkillId, uint256 _skillId) private { diff --git a/contracts/colonyNetwork/IColonyNetwork.sol b/contracts/colonyNetwork/IColonyNetwork.sol index a2b121733f..b596104508 100644 --- a/contracts/colonyNetwork/IColonyNetwork.sol +++ b/contracts/colonyNetwork/IColonyNetwork.sol @@ -530,98 +530,7 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery, IBasicMetaTransac function getColonyBridgeAddress() external view returns (address bridge); function bridgeMessage(uint256 _chainId, bytes memory _payload) external; - - /// @notice Update the reputation on a foreign chain from the mining chain - /// @dev Should error if called by anyone other than the known bridge from the mining chain - /// @param newHash The new root hash - /// @param newNLeaves The new nLeaves in the root hash - /// @param nonce The nonce to ensure these txs can't be replayed - function setReputationRootHashFromBridge( - bytes32 newHash, - uint256 newNLeaves, - uint256 nonce - ) external; - - /// @notice Initiate a cross-chain update of the current reputation state - /// @param chainId The chainid we want to bridge to - function bridgeCurrentRootHash(uint256 chainId) external; - - /// @notice Called to re-send the bridging transaction for a skill to the - /// @param skillId The skillId we're bridging the creation of - function bridgeSkillIfNotMiningChain(uint256 skillId) external; - - /// @notice Function called by bridge transactions to add a new skill - /// @param _parentSkillId The parent id of the new skill - /// @param _skillCount The number of the new skill being created - function addSkillFromBridge(uint256 _parentSkillId, uint256 _skillCount) external; - - /// @notice Called to add a bridged skill that wasn't next when it was bridged, - /// but now is - /// @param _skillId The skillId of the skill being bridged - function addPendingSkill(uint256 _skillId) external; - - /// @notice Called to get the information about a skill that has been bridged out of order - /// @param _chainId The chainId we're bridging from - /// @param _skillCount The skill count - /// @return parentId The parent id of the skill being added - function getPendingSkillAddition( - uint256 _chainId, - uint256 _skillCount - ) external view returns (uint256 parentId); - - /// @notice Get the (currently bridged) skill count of another chain - /// @param _chainId The chainid of foreign chain - /// @return skillCount The skillCount of the corresponding chain - function getBridgedSkillCounts(uint256 _chainId) external view returns (uint256 skillCount); - - /// @notice Adds a reputation update entry to log. - /// @dev Errors if it is called by anyone but a known bridge - /// @param _colony The colony the reputation is being awarded in - /// @param _user The address of the user for the reputation update - /// @param _amount The amount of reputation change for the update, this can be a negative as well as a positive value - /// @param _skillId The skill for the reputation update - /// @param _updateNumber The counter used for ordering bridged updates - function addReputationUpdateLogFromBridge( - address _colony, - address _user, - int _amount, - uint _skillId, - uint256 _updateNumber - ) external; - - /// @notice Get the (currently bridged) reputation update count of a chain - /// @param _chainId The chainid of the chain - /// @param _colony The colony being queried - /// @return bridgedReputationCount The bridge reputation count of the corresponding chain - /// @dev On the non-mining chain, this tracks the number of reputation updates that have either been bridged, or attempted to - /// be bridged (and failed, and are now pending bridging). On the mining chain, it tracks how many have been successfully bridged - /// and added to the log. - function getBridgedReputationUpdateCount( - uint256 _chainId, - address _colony - ) external view returns (uint256 bridgedReputationCount); - - /// @notice Try to bridge a reputation update that (previously) failed - /// @param _colony The colony being queried - /// @param _updateNumber the emission index to bridge - function bridgePendingReputationUpdate(address _colony, uint256 _updateNumber) external; - - /// @notice Get the details of a reputation update that was bridged but was not added to the log because it was - /// bridged out of order - /// @param _chainId The chainId the update was bridged from - /// @param _colony The colony being queried - /// @param _updateNumber the updatenumber being queries - /// @return update The update stored for that chain/colony/updateNumber - function getPendingReputationUpdate( - uint256 _chainId, - address _colony, - uint256 _updateNumber - ) external view returns (PendingReputationUpdate memory update); - - /// @notice Try to emit the next reputation update that was bridged but previously failed, if any - /// @param _chainId The chainId the update was bridged from - /// @param _colony The colony being queried - function addPendingReputationUpdate(uint256 _chainId, address _colony) external; + function bridgeMessageToNetwork(uint256 _chainId, bytes memory _payload) external; /// @notice Function called by a colony to ensure that a DomainTokenReceiver has been deployed and set up correctly /// for a particular domain. diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 39dac88450..66fe6cabf3 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -77,7 +77,7 @@ contract("Cross-chain", (accounts) => { let resetRelayer; let homeMetacolony; - let foreignMetacolony; + let proxyMetacolony; let web3HomeProvider; let web3ForeignProvider; @@ -201,8 +201,8 @@ contract("Cross-chain", (accounts) => { tx = await homeBridge.setBridgeEnabled(true); await tx.wait(); - // const foreignMCAddress = await foreignColonyNetwork.getMetaColony(); - // foreignMetacolony = await new ethers.Contract(foreignMCAddress, IMetaColony.abi, ethersForeignSigner); + const proxyMCAddress = await homeColonyNetwork.getMetaColony(); // Not a mistake - they have the same address, and .getMetaColony doesn't exist on ProxyColonyNetwork + proxyMetacolony = await new ethers.Contract(proxyMCAddress, IMetaColony.abi, ethersForeignSigner); console.log("get mc"); const homeMCAddress = await homeColonyNetwork.getMetaColony(); console.log("got mc"); @@ -278,7 +278,7 @@ contract("Cross-chain", (accounts) => { }); describe("administrating cross-network bridges", async () => { - it.only("colonyNetwork should have the same address on each chain", async () => { + it("colonyNetwork should have the same address on each chain", async () => { expect(homeColonyNetwork.address).to.equal(foreignColonyNetwork.address); // Check we have colony Network there - this equality is expected because of how we set up the addresses const homeVersionResolver = await homeColonyNetwork.getColonyVersionResolver(CURR_VERSION); @@ -289,7 +289,7 @@ contract("Cross-chain", (accounts) => { expect(proxyColonyResolver).to.not.equal(ADDRESS_ZERO); }); - it.only("colonies deployed on different chains can have same address", async () => { + it("colonies deployed on different chains can have same address", async () => { // Deploy a colony only on one chain, so that normal contract creations wouldn't have the same address await setupColony(homeColonyNetwork); @@ -347,19 +347,74 @@ contract("Cross-chain", (accounts) => { expect(foreignColonyBridgeAddress).to.equal(foreignColonyBridge.address); }); - it("setColonyBridgeAddress on Network can only be called by the metacolony", async () => { - const tx = await foreignColonyNetwork.setColonyBridgeAddress(foreignColonyBridge.address, { gasLimit: 1000000 }); + it("setColonyBridgeAddress on proxy Network can be called directly by the owner (and not a random address)", async () => { + const owner = await foreignColonyNetwork.owner(); + expect(await foreignColonyNetwork.signer.getAddress()).to.equal(owner); + let tx = await foreignColonyNetwork.setColonyBridgeAddress(foreignColonyBridge.address, { gasLimit: 1000000 }); + await tx.wait(); + + const foreignColonyNetwork2 = new ethers.Contract(foreignColonyNetwork.address, IColonyNetwork.abi, ethersForeignSigner2); + expect(await foreignColonyNetwork2.signer.getAddress()).to.not.equal(owner); + tx = await foreignColonyNetwork2.setColonyBridgeAddress(foreignColonyBridge.address, { gasLimit: 1000000, from: accounts[1] }); + await checkErrorRevertEthers(tx.wait(), "colony-network-caller-must-be-owner-or-bridge"); + }); + + it("setColonyBridgeAddress on Home Network can only be called by the meta colony", async () => { + const tx = await homeColonyNetwork.setColonyBridgeAddress(homeColonyBridge.address, { gasLimit: 1000000 }); await checkErrorRevertEthers(tx.wait(), "colony-caller-must-be-meta-colony"); }); + it("callProxyNetwork can only be called by root permissions on the metacolony", async () => { + const payload = foreignColonyNetwork.interface.encodeFunctionData("setColonyBridgeAddress", [ADDRESS_ZERO]); + const homeMetacolony2 = new ethers.Contract(homeMetacolony.address, IMetaColony.abi, ethersHomeSigner2); + let tx = await homeMetacolony2.callProxyNetwork(foreignChainId, [payload], { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "ds-auth-unauthorized"); + + // Add root permissions + tx = await homeMetacolony.setUserRoles( + 1, + UINT256_MAX_ETHERS, + accounts[1], + 1, + ethers.utils.hexZeroPad(ethers.BigNumber.from(ethers.BigNumber.from(2).pow(ROOT_ROLE)).toHexString(), 32), + ); + + await tx.wait(); + + // Can now call + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const tx3 = await homeMetacolony2.callProxyNetwork(foreignChainId, [payload]); + await tx3.wait(); + await p; + + // Check call was successful + const bridgeAddressAfter = await foreignColonyNetwork.colonyBridgeAddress(); + expect(bridgeAddressAfter).to.equal(ADDRESS_ZERO); + + // Reset permissions + tx = await homeMetacolony.setUserRoles(1, UINT256_MAX_ETHERS, accounts[1], 1, ethers.utils.hexZeroPad("0x00", 32)); + await tx.wait(); + }); + + it("setColonyBridgeAddress on Proxy Network can be used across the bridge", async () => { + const bridgeAddress = await foreignColonyNetwork.colonyBridgeAddress(); + const payload = foreignColonyNetwork.interface.encodeFunctionData("setColonyBridgeAddress", [ADDRESS_ZERO]); + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const tx = await homeMetacolony.callProxyNetwork(foreignChainId, [payload]); + await tx.wait(); + await p; + const bridgeAddressAfter = await foreignColonyNetwork.colonyBridgeAddress(); + expect(bridgeAddressAfter).to.not.equal(bridgeAddress); + }); + it("setColonyBridgeAddress on Metacolony can't be called by an address without root permissions", async () => { - const foreignMetacolony2 = new ethers.Contract(foreignMetacolony.address, IColonyNetwork.abi, ethersForeignSigner2); + const homeMetacolony2 = new ethers.Contract(proxyMetacolony.address, IColonyNetwork.abi, ethersHomeSigner2); - let tx = await foreignMetacolony2.setColonyBridgeAddress(ADDRESS_ZERO, { gasLimit: 1000000 }); + let tx = await homeMetacolony2.setColonyBridgeAddress(ADDRESS_ZERO, { gasLimit: 1000000 }); await checkErrorRevertEthers(tx.wait(), "ds-auth-unauthorized"); // Add root permissions - tx = await foreignMetacolony.setUserRoles( + tx = await homeMetacolony.setUserRoles( 1, UINT256_MAX_ETHERS, accounts[1], @@ -369,13 +424,13 @@ contract("Cross-chain", (accounts) => { await tx.wait(); // Can now call - tx = await foreignMetacolony2.setColonyBridgeAddress(ADDRESS_ZERO, { + tx = await homeMetacolony2.setColonyBridgeAddress(ADDRESS_ZERO, { gasLimit: 1000000, }); await tx.wait(); // Reset permissions - tx = await foreignMetacolony.setUserRoles(1, UINT256_MAX_ETHERS, accounts[1], 1, ethers.utils.hexZeroPad("0x00", 32)); + tx = await proxyMetacolony.setUserRoles(1, UINT256_MAX_ETHERS, accounts[1], 1, ethers.utils.hexZeroPad("0x00", 32)); await tx.wait(); }); @@ -386,22 +441,6 @@ contract("Cross-chain", (accounts) => { await checkErrorRevertEthers(tx.wait(), "colony-bridge-chainid-too-large"); }); - it("updating the bridge for a chain does not reset the bridged skill count", async () => { - const countBefore = await homeColonyNetwork.getBridgedSkillCounts(foreignChainId); - const tx = await homeMetacolony.setColonyBridgeAddress(ADDRESS_ZERO); - await tx.wait(); - - const countAfter = await homeColonyNetwork.getBridgedSkillCounts(foreignChainId); - expect(countAfter).to.not.equal(0); - expect(countAfter.sub(countBefore).toNumber()).to.equal(0); - }); - - it("the bridged skill count has a sensible default", async () => { - const unsetChainId = ethers.BigNumber.from(123456789); - const count = await homeColonyNetwork.getBridgedSkillCounts(unsetChainId); - expect(count.toString()).to.equal(unsetChainId.shl(128).toString()); - }); - it("only owners can set properties on the ColonyBridge", async () => { let tx = await homeColonyBridge.connect(ethersHomeSigner2).setColonyNetworkAddress(ADDRESS_ZERO, { gasLimit: 1000000 }); await checkErrorRevertEthers(tx.wait(), "ds-auth-unauthorized"); @@ -1168,7 +1207,7 @@ contract("Cross-chain", (accounts) => { await (await colony.setFundingRole(1, UINT256_MAX_ETHERS, accounts[0], 1, true)).wait(); }); - it.only("Can track tokens received on the foreign chain", async () => { + it("Can track tokens received on the foreign chain", async () => { const tokenAmount = ethers.utils.parseEther("100"); let tx = await foreignToken["mint(address,uint256)"](proxyColony.address, tokenAmount); @@ -1190,7 +1229,7 @@ contract("Cross-chain", (accounts) => { expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); }); - it.only("Can track tokens sent on the foreign chain", async () => { + it("Can track tokens sent on the foreign chain", async () => { const tokenAmount = ethers.utils.parseEther("100"); let tx = await foreignToken["mint(address,uint256)"](proxyColony.address, tokenAmount); @@ -1290,7 +1329,7 @@ contract("Cross-chain", (accounts) => { await (await foreignToken.setOwner(proxyColony.address)).wait(); }); - it.only("can make arbitrary transactions on the foreign chain", async () => { + it("can make arbitrary transactions on the foreign chain", async () => { const balanceBefore = await foreignToken.balanceOf(proxyColony.address); const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); @@ -1305,7 +1344,7 @@ contract("Cross-chain", (accounts) => { expect(balanceAfter.sub(balanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); }); - it.only("can make multiple arbitrary transactions on the foreign chain in one go", async () => { + it("can make multiple arbitrary transactions on the foreign chain in one go", async () => { const shellBalanceBefore = await foreignToken.balanceOf(proxyColony.address); const colonyBalanceBefore = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); @@ -1331,7 +1370,7 @@ contract("Cross-chain", (accounts) => { describe("bridge functions are secure", async () => { it("only the configured colonyNetwork can call `sendMessage`", async () => { - const tx = await foreignColonyBridge.sendMessage(1, "0x00000000", { gasLimit: 1000000 }); + const tx = await foreignColonyBridge.sendMessage(1, ADDRESS_ZERO, "0x00000000", { gasLimit: 1000000 }); await checkErrorRevertEthers(tx.wait(), "wormhole-bridge-only-colony-network"); }); @@ -1606,7 +1645,7 @@ contract("Cross-chain", (accounts) => { homeColonyBridge.address, 0, 0, - foreignColonyNetwork.interface.encodeFunctionData("setReputationRootHashFromBridge", [ethers.utils.hexZeroPad("0xdeadbeef", 32), 0, 1]), + foreignColonyNetwork.interface.encodeFunctionData("setProxyColonyResolverAddress", [ADDRESS_ZERO]), 100, wormholeHomeChainId, ); From 61f8b84e4d746eea3cae5ad97e17081b4e087347 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Tue, 27 Aug 2024 17:07:20 +0100 Subject: [PATCH 17/72] Add docs --- contracts/bridging/ProxyColony.sol | 12 +- contracts/bridging/ProxyColonyNetwork.sol | 5 +- .../colony/ColonyArbitraryTransaction.sol | 5 +- contracts/colony/ColonyFunding.sol | 46 ++++--- contracts/colony/IColony.sol | 65 +++++++++- contracts/colony/IMetaColony.sol | 8 +- contracts/colonyNetwork/ColonyNetwork.sol | 5 +- contracts/colonyNetwork/IColonyNetwork.sol | 20 ++- docs/interfaces/icolony.md | 103 ++++++++++++---- docs/interfaces/icolonynetwork.md | 22 +--- docs/interfaces/imetacolony.md | 114 ++++++++++++++++++ 11 files changed, 320 insertions(+), 85 deletions(-) diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index 98afc6bdba..3e6ebd3d2a 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -78,7 +78,11 @@ contract ProxyColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards } // TODO: secure this function - function transferFromBridge(address _token, address _recipient, uint256 _amount) public onlyColonyBridge() { + function transferFromBridge( + address _token, + address _recipient, + uint256 _amount + ) public onlyColonyBridge { tokenBalances[_token] -= _amount; if (_token == address(0x0)) { @@ -90,7 +94,10 @@ contract ProxyColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards emit TransferMade(_token, _recipient, _amount); } - function makeArbitraryTransactions(address[] memory _targets, bytes[] memory _payloads) public onlyColonyBridge() { + function makeArbitraryTransactions( + address[] memory _targets, + bytes[] memory _payloads + ) public onlyColonyBridge { require(_targets.length == _payloads.length, "colony-targets-and-payloads-length-mismatch"); address bridgeAddress = ProxyColonyNetwork(owner).colonyBridgeAddress(); for (uint256 i; i < _targets.length; i += 1) { @@ -102,5 +109,4 @@ contract ProxyColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards require(success, "colony-arbitrary-transaction-failed"); } } - } diff --git a/contracts/bridging/ProxyColonyNetwork.sol b/contracts/bridging/ProxyColonyNetwork.sol index ea63205755..cef56b4d60 100644 --- a/contracts/bridging/ProxyColonyNetwork.sol +++ b/contracts/bridging/ProxyColonyNetwork.sol @@ -60,7 +60,10 @@ contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards { } modifier ownerOrBridge() { - require(msgSender() == colonyBridgeAddress || msgSender() == owner, "colony-network-caller-must-be-owner-or-bridge"); + require( + msgSender() == colonyBridgeAddress || msgSender() == owner, + "colony-network-caller-must-be-owner-or-bridge" + ); _; } diff --git a/contracts/colony/ColonyArbitraryTransaction.sol b/contracts/colony/ColonyArbitraryTransaction.sol index 4df5758111..ae6528db75 100644 --- a/contracts/colony/ColonyArbitraryTransaction.sol +++ b/contracts/colony/ColonyArbitraryTransaction.sol @@ -61,10 +61,7 @@ contract ColonyArbitraryTransaction is ColonyStorage { uint256 _chainId, bytes[] memory _actions ) public stoppable auth returns (bool) { - bytes memory payload = abi.encodeWithSignature( - "multicall(bytes[])", - _actions - ); + bytes memory payload = abi.encodeWithSignature("multicall(bytes[])", _actions); IColonyNetwork(colonyNetworkAddress).bridgeMessageToNetwork(_chainId, payload); } diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index 9c51806d68..4413236d2d 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -40,10 +40,7 @@ contract ColonyFunding is uint256 _toPot, uint256 _amount, address _token - ) - public - stoppable - { + ) public stoppable { moveFundsBetweenPots( _permissionDomainId, _childSkillIndex, @@ -249,11 +246,16 @@ contract ColonyFunding is uint256 _slot, address _token, uint256 _amount - ) - public - stoppable - { - setExpenditurePayout(_permissionDomainId, _childSkillIndex, _id, _slot, block.chainid, _token, _amount); + ) public stoppable { + setExpenditurePayout( + _permissionDomainId, + _childSkillIndex, + _id, + _slot, + block.chainid, + _token, + _amount + ); } function setExpenditurePayout( @@ -547,13 +549,19 @@ contract ColonyFunding is if (getFundingPotBalance(_fundingPotId, _chainId, _token) >= _prev) { // If the amount in the pot was enough to pay for the old budget... - if (getFundingPotBalance(_fundingPotId, _chainId, _token) < getFundingPotPayout(_fundingPotId, _chainId, _token)) { + if ( + getFundingPotBalance(_fundingPotId, _chainId, _token) < + getFundingPotPayout(_fundingPotId, _chainId, _token) + ) { // And the amount is not enough to pay for the new budget... tokenPot.payoutsWeCannotMake += 1; // Then this is a set of payouts we cannot make that we could before. } } else { // If this 'else' is running, then the amount in the pot was not enough to pay for the old budget - if (getFundingPotBalance(_fundingPotId, _chainId, _token) >= getFundingPotPayout(_fundingPotId, _chainId, _token)) { + if ( + getFundingPotBalance(_fundingPotId, _chainId, _token) >= + getFundingPotPayout(_fundingPotId, _chainId, _token) + ) { // And the amount is enough to pay for the new budget... tokenPot.payoutsWeCannotMake -= 1; // Then this is a set of payouts we can make that we could not before. } @@ -648,14 +656,14 @@ contract ColonyFunding is } } } else { - // TODO: Shell colony payout - bytes memory payload = abi.encodeWithSignature( - "transferFromBridge(address,address,uint256)", - _token, - _user, - _payout - ); - IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, payload); + // TODO: Shell colony payout + bytes memory payload = abi.encodeWithSignature( + "transferFromBridge(address,address,uint256)", + _token, + _user, + _payout + ); + IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, payload); } // slither-disable-next-line reentrancy-unlimited-gas diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 27a8f2c5e2..cad25c6f0e 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -61,7 +61,16 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, bytes memory _action ) external returns (bool success); - function makeProxyArbitraryTransactions(uint256 chainId, address[] memory _destinations, bytes[] memory _actions) external; + /// @notice Execute arbitrary transactions on behalf of the Colony via a proxy colony on another chain + /// @dev If proxy colony not already deployed, will do nothing + /// @param chainId The chainId of the proxy colony + /// @param _destinations Array of addresses to be targeted + /// @param _actions Array of Bytes arrays encoding the function calls and arguments + function makeProxyArbitraryTransactions( + uint256 chainId, + address[] memory _destinations, + bytes[] memory _actions + ) external; /// @notice Execute arbitrary transactions on behalf of the Colony in series /// @param _targets Array of addressed to be targeted @@ -572,6 +581,7 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, ) external; /// @notice Set the token payout in a given expenditure slot. Can only be called by an Arbitration user. + /// @notice This function is deprecated and will be removed in a future version /// @param _permissionDomainId The domainId in which I have the permission to take this action /// @param _childSkillIndex The index that the `_domainId` is relative to `_permissionDomainId` /// @param _id Id of the expenditure @@ -587,6 +597,14 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, uint256 _amount ) external; + /// @notice Set the token payout in a given expenditure slot. Can only be called by an Arbitration user. + /// @param _permissionDomainId The domainId in which I have the permission to take this action + /// @param _childSkillIndex The index that the `_domainId` is relative to `_permissionDomainId` + /// @param _id Id of the expenditure + /// @param _slot The slot to set the payout + /// @param _chainId The chainId of the token + /// @param _token Address of the token, `0x0` value indicates Ether + /// @param _amount Payout amount function setExpenditurePayout( uint256 _permissionDomainId, uint256 _childSkillIndex, @@ -595,8 +613,7 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, uint256 _chainId, address _token, uint256 _amount - ) - external; + ) external; /// @notice @deprecated /// @notice Sets the skill on an expenditure slot. Can only be called by expenditure owner. @@ -662,11 +679,23 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, ) external; /// @notice Claim the payout for an expenditure slot. Here the network receives a fee from each payout. + /// @notice This function is deprecated and will be removed in a future version /// @param _id Expenditure identifier /// @param _slot Number of the slot /// @param _token Address of the token, `0x0` value indicates Ether function claimExpenditurePayout(uint256 _id, uint256 _slot, address _token) external; - function claimExpenditurePayout(uint256 _id, uint256 _slot, uint256 _chainId, address _token) external; + + /// @notice Claim the payout for an expenditure slot. Here the network receives a fee from each payout. + /// @param _id Expenditure identifier + /// @param _slot Number of the slot + /// @param _chainId The chainId of the token + /// @param _token Address of the token, `0x0` value indicates Ether + function claimExpenditurePayout( + uint256 _id, + uint256 _slot, + uint256 _chainId, + address _token + ) external; /// @notice Get the number of expenditures in the colony. /// @return count The expenditure count @@ -827,6 +856,18 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, address _token ) external; + /// @notice Move a given amount: `_amount` of `_token` funds from funding pot with id `_fromPot` to one with id `_toPot`. + /// @param _permissionDomainId The domainId in which I have the permission to take this action + /// @param _childSkillIndex The child index in _permissionDomainId where I will be taking this action + /// @param _domainId The domain where I am taking this action, pointed to by _permissionDomainId and _childSkillIndex + /// @param _fromChildSkillIndex In the array of child skills for the skill associated with the domain pointed to by _permissionDomainId + _childSkillIndex, + /// the index of the skill associated with the domain that contains _fromPot + /// @param _toChildSkillIndex The same, but for the _toPot which the funds are being moved to + /// @param _fromPot Funding pot id providing the funds + /// @param _toPot Funding pot id receiving the funds + /// @param _amount Amount of funds + /// @param _chainId The chainId of the token + /// @param _token Address of the token, `0x0` value indicates Ether function moveFundsBetweenPots( uint256 _permissionDomainId, uint256 _childSkillIndex, @@ -840,7 +881,6 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, address _token ) external; - /// @notice @deprecated /// @notice Move a given amount: `_amount` of `_token` funds from funding pot with id `_fromPot` to one with id `_toPot`. /// @param _permissionDomainId The domainId in which I have the permission to take this action @@ -886,13 +926,26 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, /// @return uint256 amount Amount of the token that the domain can receive function getAllowedDomainReputationReceipt(uint256 _domainId) external view returns (uint256); + /// @notice Used by the bridge to indicate that funds have been claimed on another chain. + /// @param _chainId Chain id of the chain where the funds were claimed + /// @param _token Address of the token, `0x0` value indicates Ether + /// @param _amount Amount of funds claimed function recordClaimedFundsFromBridge(uint256 _chainId, address _token, uint256 _amount) external; + + /// @notice Get the balance of a funding pot for a specific token on a specific chain + /// @param _potId Id of the funding pot + /// @param _chainId Chain id of the token + /// @param _token Address of the token, `0x0` value indicates Ether + /// @return balance Balance of the funding pot function getFundingPotProxyBalance( uint256 _potId, uint256 _chainId, address _token - ) external view returns (uint256); + ) external view returns (uint256 balance); + /// @notice Create a proxy colony on another chain + /// @param _destinationChainId Chain id of the destination chain + /// @param _salt The colony creation salt that was used on creation of the colony function createProxyColony(uint256 _destinationChainId, bytes32 _salt) external; /// @notice Get the total amount of tokens `_token` minus amount reserved to be paid to the reputation and token holders as rewards. diff --git a/contracts/colony/IMetaColony.sol b/contracts/colony/IMetaColony.sol index fb2c66fac9..4a0adb610f 100644 --- a/contracts/colony/IMetaColony.sol +++ b/contracts/colony/IMetaColony.sol @@ -67,8 +67,8 @@ interface IMetaColony is IColony { uint256 newNLeaves ) external; - function callProxyNetwork( - uint256 _chainId, - bytes[] memory _actions - ) external; + /// @notice Call (a) function(s) on the proxyColonyNetwork on a different chain + /// @param _chainId The chainId of the chain the function is being called on + /// @param _actions The actions to be called + function callProxyNetwork(uint256 _chainId, bytes[] memory _actions) external; } diff --git a/contracts/colonyNetwork/ColonyNetwork.sol b/contracts/colonyNetwork/ColonyNetwork.sol index 1971170a28..e1db9b74ec 100644 --- a/contracts/colonyNetwork/ColonyNetwork.sol +++ b/contracts/colonyNetwork/ColonyNetwork.sol @@ -119,7 +119,10 @@ contract ColonyNetwork is BasicMetaTransaction, ColonyNetworkStorage, Multicall ); } - function bridgeMessageToNetwork(uint256 _chainId, bytes memory _payload) public calledByMetaColony { + function bridgeMessageToNetwork( + uint256 _chainId, + bytes memory _payload + ) public calledByMetaColony { require( IColonyBridge(colonyBridgeAddress).sendMessage(_chainId, address(this), _payload), "colony-network-bridge-message-failed" diff --git a/contracts/colonyNetwork/IColonyNetwork.sol b/contracts/colonyNetwork/IColonyNetwork.sol index b596104508..0ecd5f1554 100644 --- a/contracts/colonyNetwork/IColonyNetwork.sol +++ b/contracts/colonyNetwork/IColonyNetwork.sol @@ -529,7 +529,16 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery, IBasicMetaTransac /// @return bridge The address of the bridge to the mining chain, if set function getColonyBridgeAddress() external view returns (address bridge); + /// @notice Bridge a message to another chain + /// @param _chainId The chainId of the chain to bridge to + /// @param _payload The message to bridge + /// @dev This will bridge the message to the same address that requested the bridge on the other chain function bridgeMessage(uint256 _chainId, bytes memory _payload) external; + + /// @notice Bridge a message to the ProxyNetwork on another chain + /// @param _chainId The chainId of the chain to bridge to + /// @param _payload The message to bridge + /// @dev This should only be able to be called by the metacolony function bridgeMessageToNetwork(uint256 _chainId, bytes memory _payload) external; /// @notice Function called by a colony to ensure that a DomainTokenReceiver has been deployed and set up correctly @@ -560,10 +569,9 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery, IBasicMetaTransac uint256 _domainId ) external view returns (address domainTokenReceiverAddress); - /// @notice Send the claimFunds transaction from the shell to the colony - /// @param _token The token being held by the shell - /// @param _balance The shell's current balance of the token - function sendClaimShellColonyFunds(address _token, uint256 _balance) external; - - function createShellColony(uint256 _destinationChainId, bytes32 _salt) external; + /// @notice Handles calls to create a new colony on another chain + /// @dev Should only be called by a colony, if you're trying to call this directly you're doing something wrong + /// @param _destinationChainId The chainId of the chain to create the colony on + /// @param _salt The salt to use for the colony creation + function createProxyColony(uint256 _destinationChainId, bytes32 _salt) external; } diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index 6a5ca5605f..86d51c9513 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -189,6 +189,20 @@ Move any funds received by the colony for a specific domain to that domain's pot ### ▸ `claimExpenditurePayout(uint256 _id, uint256 _slot, address _token)` +This function is deprecated and will be removed in a future version + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_id|uint256|Expenditure identifier +|_slot|uint256|Number of the slot +|_token|address|Address of the token, `0x0` value indicates Ether + + +### ▸ `claimExpenditurePayout(uint256 _id, uint256 _slot, uint256 _chainId, address _token)` + Claim the payout for an expenditure slot. Here the network receives a fee from each payout. @@ -198,6 +212,7 @@ Claim the payout for an expenditure slot. Here the network receives a fee from e |---|---|---| |_id|uint256|Expenditure identifier |_slot|uint256|Number of the slot +|_chainId|uint256|The chainId of the token |_token|address|Address of the token, `0x0` value indicates Ether @@ -218,17 +233,17 @@ Claim the reward payout at `_payoutId`. User needs to provide their reputation a |siblings|bytes32[]|The siblings of the proof -### ▸ `createShellColony(uint256 _destinationChainId, bytes32 _salt)` - +### ▸ `createProxyColony(uint256 _destinationChainId, bytes32 _salt)` +Create a proxy colony on another chain **Parameters** |Name|Type|Description| |---|---|---| -|_destinationChainId|uint256| -|_salt|bytes32| +|_destinationChainId|uint256|Chain id of the destination chain +|_salt|bytes32|The colony creation salt that was used on creation of the colony ### ▸ `deobligateStake(address _user, uint256 _domainId, uint256 _amount)` @@ -724,24 +739,24 @@ Get the assigned `_token` payouts of pot with id `_potId`. |---|---|---| |payout|uint256|Funding pot payout amount -### ▸ `getFundingPotProxyBalance(uint256 _potId, uint256 _chainId, address _token):uint256 uint256` - +### ▸ `getFundingPotProxyBalance(uint256 _potId, uint256 _chainId, address _token):uint256 balance` +Get the balance of a funding pot for a specific token on a specific chain **Parameters** |Name|Type|Description| |---|---|---| -|_potId|uint256| -|_chainId|uint256| -|_token|address| +|_potId|uint256|Id of the funding pot +|_chainId|uint256|Chain id of the token +|_token|address|Address of the token, `0x0` value indicates Ether **Return Parameters** |Name|Type|Description| |---|---|---| -|uint256|uint256| +|balance|uint256|Balance of the funding pot ### ▸ `getLocalSkill(uint256 localSkillId):LocalSkill localSkill` @@ -1181,6 +1196,21 @@ Add a new expenditure in the colony. Secured function to authorised members. |---|---|---| |expenditureId|uint256|Identifier of the newly created expenditure +### ▸ `makeProxyArbitraryTransactions(uint256 chainId, address[] memory _destinations, bytes[] memory _actions)` + +Execute arbitrary transactions on behalf of the Colony via a proxy colony on another chain + +*Note: If proxy colony not already deployed, will do nothing* + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|chainId|uint256|The chainId of the proxy colony +|_destinations|address[]|Array of addresses to be targeted +|_actions|bytes[]|Array of Bytes arrays encoding the function calls and arguments + + ### ▸ `makeSingleArbitraryTransaction(address _target, bytes memory _action):bool success` Executes a single arbitrary transaction @@ -1245,6 +1275,27 @@ Move a given amount: `_amount` of `_token` funds from funding pot with id `_from |_token|address|Address of the token, `0x0` value indicates Ether +### ▸ `moveFundsBetweenPots(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, uint256 _fromChildSkillIndex, uint256 _toChildSkillIndex, uint256 _fromPot, uint256 _toPot, uint256 _amount, uint256 _chainId, address _token)` + +Move a given amount: `_amount` of `_token` funds from funding pot with id `_fromPot` to one with id `_toPot`. + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which I have the permission to take this action +|_childSkillIndex|uint256|The child index in _permissionDomainId where I will be taking this action +|_domainId|uint256|The domain where I am taking this action, pointed to by _permissionDomainId and _childSkillIndex +|_fromChildSkillIndex|uint256|In the array of child skills for the skill associated with the domain pointed to by _permissionDomainId + _childSkillIndex, the index of the skill associated with the domain that contains _fromPot +|_toChildSkillIndex|uint256|The same, but for the _toPot which the funds are being moved to +|_fromPot|uint256|Funding pot id providing the funds +|_toPot|uint256|Funding pot id receiving the funds +|_amount|uint256|Amount of funds +|_chainId|uint256|The chainId of the token +|_token|address|Address of the token, `0x0` value indicates Ether + + ### ▸ `moveFundsBetweenPots(uint256 _permissionDomainId, uint256 _fromChildSkillIndex, uint256 _toChildSkillIndex, uint256 _fromPot, uint256 _toPot, uint256 _amount, address _token)` Move a given amount: `_amount` of `_token` funds from funding pot with id `_fromPot` to one with id `_toPot`. @@ -1319,20 +1370,6 @@ Get the owner of the contract |---|---|---| |owner|address|The owner of the contract -### ▸ `recordClaimedFundsFromBridge(uint256 _chainId, address _token, uint256 _amount)` - - - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_chainId|uint256| -|_token|address| -|_amount|uint256| - - ### ▸ `registerColonyLabel(string memory colonyName, string memory orbitdb)` Register colony's ENS label. @@ -1493,6 +1530,23 @@ Set the token payout on an expenditure slot. Can only be called by expenditure o ### ▸ `setExpenditurePayout(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _id, uint256 _slot, address _token, uint256 _amount)` +This function is deprecated and will be removed in a future version + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which I have the permission to take this action +|_childSkillIndex|uint256|The index that the `_domainId` is relative to `_permissionDomainId` +|_id|uint256|Id of the expenditure +|_slot|uint256|The slot to set the payout +|_token|address|Address of the token, `0x0` value indicates Ether +|_amount|uint256|Payout amount + + +### ▸ `setExpenditurePayout(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _id, uint256 _slot, uint256 _chainId, address _token, uint256 _amount)` + Set the token payout in a given expenditure slot. Can only be called by an Arbitration user. @@ -1504,6 +1558,7 @@ Set the token payout in a given expenditure slot. Can only be called by an Arbit |_childSkillIndex|uint256|The index that the `_domainId` is relative to `_permissionDomainId` |_id|uint256|Id of the expenditure |_slot|uint256|The slot to set the payout +|_chainId|uint256|The chainId of the token |_token|address|Address of the token, `0x0` value indicates Ether |_amount|uint256|Payout amount diff --git a/docs/interfaces/icolonynetwork.md b/docs/interfaces/icolonynetwork.md index 1d976c2ece..efe11b1cf3 100644 --- a/docs/interfaces/icolonynetwork.md +++ b/docs/interfaces/icolonynetwork.md @@ -268,17 +268,18 @@ Create the Meta Colony, same as a normal colony plus the root skill. |_tokenAddress|address|Address of the CLNY token -### ▸ `createShellColony(uint256 _destinationChainId, bytes32 _salt)` - +### ▸ `createProxyColony(uint256 _destinationChainId, bytes32 _salt)` +Handles calls to create a new colony on another chain +*Note: Should only be called by a colony, if you're trying to call this directly you're doing something wrong* **Parameters** |Name|Type|Description| |---|---|---| -|_destinationChainId|uint256| -|_salt|bytes32| +|_destinationChainId|uint256|The chainId of the chain to create the colony on +|_salt|bytes32|The salt to use for the colony creation ### ▸ `deployTokenAuthority(address _token, address _colony, address[] memory _allowedToTransfer):address _tokenAuthority` @@ -1087,19 +1088,6 @@ Used to track that a user is eligible to claim a reward |_amount|uint256|The amount of CLNY to be awarded -### ▸ `sendClaimShellColonyFunds(address _token, uint256 _balance)` - -Send the claimFunds transaction from the shell to the colony - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_token|address|The token being held by the shell -|_balance|uint256|The shell's current balance of the token - - ### ▸ `setColonyBridgeAddress(address _bridgeAddress)` Called to set the address of the colony bridge contract diff --git a/docs/interfaces/imetacolony.md b/docs/interfaces/imetacolony.md index bf86ee4989..d1253b03c3 100644 --- a/docs/interfaces/imetacolony.md +++ b/docs/interfaces/imetacolony.md @@ -146,6 +146,19 @@ Burn tokens held by the colony. Can only burn tokens held in the root funding po |amount|uint256|The amount of tokens to burn +### ▸ `callProxyNetwork(uint256 _chainId, bytes[] memory _actions)` + +Call (a) function(s) on the proxyColonyNetwork on a different chain + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_chainId|uint256|The chainId of the chain the function is being called on +|_actions|bytes[]|The actions to be called + + ### ▸ `cancelExpenditure(uint256 _id)` Cancels the expenditure and prevents further editing. Can only be called by expenditure owner. @@ -212,6 +225,20 @@ Move any funds received by the colony for a specific domain to that domain's pot ### ▸ `claimExpenditurePayout(uint256 _id, uint256 _slot, address _token)` +This function is deprecated and will be removed in a future version + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_id|uint256|Expenditure identifier +|_slot|uint256|Number of the slot +|_token|address|Address of the token, `0x0` value indicates Ether + + +### ▸ `claimExpenditurePayout(uint256 _id, uint256 _slot, uint256 _chainId, address _token)` + Claim the payout for an expenditure slot. Here the network receives a fee from each payout. @@ -221,6 +248,7 @@ Claim the payout for an expenditure slot. Here the network receives a fee from e |---|---|---| |_id|uint256|Expenditure identifier |_slot|uint256|Number of the slot +|_chainId|uint256|The chainId of the token |_token|address|Address of the token, `0x0` value indicates Ether @@ -241,6 +269,19 @@ Claim the reward payout at `_payoutId`. User needs to provide their reputation a |siblings|bytes32[]|The siblings of the proof +### ▸ `createProxyColony(uint256 _destinationChainId, bytes32 _salt)` + +Create a proxy colony on another chain + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_destinationChainId|uint256|Chain id of the destination chain +|_salt|bytes32|The colony creation salt that was used on creation of the colony + + ### ▸ `deobligateStake(address _user, uint256 _domainId, uint256 _amount)` Deobligate the user some amount of tokens, releasing the stake. @@ -734,6 +775,25 @@ Get the assigned `_token` payouts of pot with id `_potId`. |---|---|---| |payout|uint256|Funding pot payout amount +### ▸ `getFundingPotProxyBalance(uint256 _potId, uint256 _chainId, address _token):uint256 balance` + +Get the balance of a funding pot for a specific token on a specific chain + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_potId|uint256|Id of the funding pot +|_chainId|uint256|Chain id of the token +|_token|address|Address of the token, `0x0` value indicates Ether + +**Return Parameters** + +|Name|Type|Description| +|---|---|---| +|balance|uint256|Balance of the funding pot + ### ▸ `getLocalSkill(uint256 localSkillId):LocalSkill localSkill` Get the local skill @@ -1187,6 +1247,21 @@ Add a new expenditure in the colony. Secured function to authorised members. |---|---|---| |expenditureId|uint256|Identifier of the newly created expenditure +### ▸ `makeProxyArbitraryTransactions(uint256 chainId, address[] memory _destinations, bytes[] memory _actions)` + +Execute arbitrary transactions on behalf of the Colony via a proxy colony on another chain + +*Note: If proxy colony not already deployed, will do nothing* + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|chainId|uint256|The chainId of the proxy colony +|_destinations|address[]|Array of addresses to be targeted +|_actions|bytes[]|Array of Bytes arrays encoding the function calls and arguments + + ### ▸ `makeSingleArbitraryTransaction(address _target, bytes memory _action):bool success` Executes a single arbitrary transaction @@ -1251,6 +1326,27 @@ Move a given amount: `_amount` of `_token` funds from funding pot with id `_from |_token|address|Address of the token, `0x0` value indicates Ether +### ▸ `moveFundsBetweenPots(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, uint256 _fromChildSkillIndex, uint256 _toChildSkillIndex, uint256 _fromPot, uint256 _toPot, uint256 _amount, uint256 _chainId, address _token)` + +Move a given amount: `_amount` of `_token` funds from funding pot with id `_fromPot` to one with id `_toPot`. + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which I have the permission to take this action +|_childSkillIndex|uint256|The child index in _permissionDomainId where I will be taking this action +|_domainId|uint256|The domain where I am taking this action, pointed to by _permissionDomainId and _childSkillIndex +|_fromChildSkillIndex|uint256|In the array of child skills for the skill associated with the domain pointed to by _permissionDomainId + _childSkillIndex, the index of the skill associated with the domain that contains _fromPot +|_toChildSkillIndex|uint256|The same, but for the _toPot which the funds are being moved to +|_fromPot|uint256|Funding pot id providing the funds +|_toPot|uint256|Funding pot id receiving the funds +|_amount|uint256|Amount of funds +|_chainId|uint256|The chainId of the token +|_token|address|Address of the token, `0x0` value indicates Ether + + ### ▸ `moveFundsBetweenPots(uint256 _permissionDomainId, uint256 _fromChildSkillIndex, uint256 _toChildSkillIndex, uint256 _fromPot, uint256 _toPot, uint256 _amount, address _token)` Move a given amount: `_amount` of `_token` funds from funding pot with id `_fromPot` to one with id `_toPot`. @@ -1497,6 +1593,23 @@ Set the token payout on an expenditure slot. Can only be called by expenditure o ### ▸ `setExpenditurePayout(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _id, uint256 _slot, address _token, uint256 _amount)` +This function is deprecated and will be removed in a future version + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which I have the permission to take this action +|_childSkillIndex|uint256|The index that the `_domainId` is relative to `_permissionDomainId` +|_id|uint256|Id of the expenditure +|_slot|uint256|The slot to set the payout +|_token|address|Address of the token, `0x0` value indicates Ether +|_amount|uint256|Payout amount + + +### ▸ `setExpenditurePayout(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _id, uint256 _slot, uint256 _chainId, address _token, uint256 _amount)` + Set the token payout in a given expenditure slot. Can only be called by an Arbitration user. @@ -1508,6 +1621,7 @@ Set the token payout in a given expenditure slot. Can only be called by an Arbit |_childSkillIndex|uint256|The index that the `_domainId` is relative to `_permissionDomainId` |_id|uint256|Id of the expenditure |_slot|uint256|The slot to set the payout +|_chainId|uint256|The chainId of the token |_token|address|Address of the token, `0x0` value indicates Ether |_amount|uint256|Payout amount From 5d32e553204a0232745aa5944a6b797f9ec1b678 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 28 Aug 2024 12:30:35 +0100 Subject: [PATCH 18/72] Get coverage tests working with bridge both ways --- .../bridging/WormholeBridgeForColony.sol | 3 +- contracts/colonyNetwork/ColonyNetwork.sol | 4 +-- .../colonyNetwork/ColonyNetworkDeployer.sol | 10 ++++-- scripts/setup-bridging-contracts.js | 5 ++- .../colony-network-recovery.js | 4 +++ test/cross-chain/cross-chain.js | 36 ++++++++++++------- test/truffle-fixture.js | 6 ++++ 7 files changed, 50 insertions(+), 18 deletions(-) diff --git a/contracts/bridging/WormholeBridgeForColony.sol b/contracts/bridging/WormholeBridgeForColony.sol index 546b7523f2..fb937c22fa 100644 --- a/contracts/bridging/WormholeBridgeForColony.sol +++ b/contracts/bridging/WormholeBridgeForColony.sol @@ -140,7 +140,8 @@ contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { // For wormhole, we prefix the supplied payload with the _evmChainId // This is because wormhole is a generic bridge, and we need to tell it which chain to send to - // We also prefix with the destination chain Id, which we assume is the same as the sender. + // We also prefix with the destination address, which we assume is the same as the sender. + // slither-disable-next-line unused-return try wormhole.publishMessage(0, abi.encode(_evmChainId, _destination, _payload), 0) { return true; } catch { diff --git a/contracts/colonyNetwork/ColonyNetwork.sol b/contracts/colonyNetwork/ColonyNetwork.sol index e1db9b74ec..87985b3a21 100644 --- a/contracts/colonyNetwork/ColonyNetwork.sol +++ b/contracts/colonyNetwork/ColonyNetwork.sol @@ -112,7 +112,7 @@ contract ColonyNetwork is BasicMetaTransaction, ColonyNetworkStorage, Multicall emit ColonyNetworkInitialised(_resolver); } - function bridgeMessage(uint256 _chainId, bytes memory _payload) public calledByColony { + function bridgeMessage(uint256 _chainId, bytes memory _payload) public stoppable calledByColony { require( IColonyBridge(colonyBridgeAddress).sendMessage(_chainId, msg.sender, _payload), "colony-network-bridge-message-failed" @@ -122,7 +122,7 @@ contract ColonyNetwork is BasicMetaTransaction, ColonyNetworkStorage, Multicall function bridgeMessageToNetwork( uint256 _chainId, bytes memory _payload - ) public calledByMetaColony { + ) public stoppable calledByMetaColony { require( IColonyBridge(colonyBridgeAddress).sendMessage(_chainId, address(this), _payload), "colony-network-bridge-message-failed" diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index a9fc321e12..09c847eeaf 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -132,10 +132,16 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage { return (address(token), colonyAddress); } - function createProxyColony(uint256 _destinationChainId, bytes32 _salt) public calledByColony { + function createProxyColony( + uint256 _destinationChainId, + bytes32 _salt + ) public stoppable calledByColony { // TODO: Check if the colony is allowed to use the salt bytes memory payload = abi.encodeWithSignature("createProxyColonyFromBridge(bytes32)", _salt); - IColonyBridge(colonyBridgeAddress).sendMessage(_destinationChainId, address(this), payload); + require( + IColonyBridge(colonyBridgeAddress).sendMessage(_destinationChainId, address(this), payload), + "colony-network-create-proxy-colony-failed" + ); } /** diff --git a/scripts/setup-bridging-contracts.js b/scripts/setup-bridging-contracts.js index ca956fd47e..77500183c7 100644 --- a/scripts/setup-bridging-contracts.js +++ b/scripts/setup-bridging-contracts.js @@ -270,12 +270,15 @@ async function setForeignBridgeData(homeColonyBridgeAddress, foreignColonyBridge // TODO: Figure out a better way of setting / controlling this? console.log("setting foreign colony bridge address", foreignColonyBridgeAddress); + + console.log(ethersForeignSigner); + // process.exit(1); const foreignColonyNetwork = new ethers.Contract(NETWORK_ADDRESS, ProxyColonyNetwork.abi, ethersForeignSigner); tx = await foreignColonyNetwork.setColonyBridgeAddress(foreignColonyBridgeAddress); await tx.wait(); + tx = await foreignColonyNetwork.setHomeChainId(homeChainId); await tx.wait(); - console.log("done"); } async function setHomeBridgeData(homeColonyBridgeAddress, foreignColonyBridgeAddress, ethersHomeSigner, ethersForeignSigner) { diff --git a/test/contracts-network/colony-network-recovery.js b/test/contracts-network/colony-network-recovery.js index 4f4f139972..bc0a0280bc 100644 --- a/test/contracts-network/colony-network-recovery.js +++ b/test/contracts-network/colony-network-recovery.js @@ -201,6 +201,10 @@ contract("Colony Network Recovery", (accounts) => { await checkErrorRevert(colonyNetwork.setReputationRootHashFromBridge(HASHZERO, 0, 0), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.setDomainTokenReceiverResolver(ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.idempotentDeployDomainTokenReceiver(ADDRESS_ZERO), "colony-in-recovery-mode"); + await checkErrorRevert(colonyNetwork.bridgeMessage(1, "0x00000000"), "colony-in-recovery-mode"); + await checkErrorRevert(colonyNetwork.bridgeMessageToNetwork(1, "0x00000000"), "colony-in-recovery-mode"); + await checkErrorRevert(colonyNetwork.createProxyColony(1, HASHZERO), "colony-in-recovery-mode"); + await colonyNetwork.approveExitRecovery(); await colonyNetwork.exitRecoveryMode(); }); diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 66fe6cabf3..ede51022cc 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -30,13 +30,11 @@ const Resolver = artifacts.require("Resolver"); const Token = artifacts.require("Token"); const IColony = artifacts.require("IColony"); const ICreateX = artifacts.require("ICreateX"); -const IReputationMiningCycle = artifacts.require("IReputationMiningCycle"); -const WormholeBridgeForColony = artifacts.require("WormholeBridgeForColony"); const ProxyColonyNetwork = artifacts.require("ProxyColonyNetwork"); const ProxyColony = artifacts.require("ProxyColony"); const MetaTxToken = artifacts.require("MetaTxToken"); // const { assert } = require("console"); -const { setupBridging, deployBridge, setForeignBridgeData, setHomeBridgeData } = require("../../scripts/setup-bridging-contracts"); +const { setupBridging, setForeignBridgeData, setHomeBridgeData } = require("../../scripts/setup-bridging-contracts"); const { MINING_CYCLE_DURATION, @@ -44,8 +42,8 @@ const { ROOT_ROLE, CURR_VERSION, CREATEX_ADDRESS, - UINT256_MAX, - WAD, + FORKED_XDAI_CHAINID, + NETWORK_ADDRESS, } = require("../../helpers/constants"); const { forwardTime, checkErrorRevertEthers, revert, snapshot, evmChainIdToWormholeChainId } = require("../../helpers/test-helper"); const ReputationMinerTestWrapper = require("../../packages/reputation-miner/test/ReputationMinerTestWrapper"); @@ -60,7 +58,6 @@ const contractLoader = new TruffleLoader({ contract("Cross-chain", (accounts) => { let homeColony; - let foreignColony; let homeColonyNetwork; let foreignColonyNetwork; let homeBridge; @@ -73,7 +70,6 @@ contract("Cross-chain", (accounts) => { let homeChainId; let foreignChainId; let wormholeHomeChainId; - let wormholeForeignChainId; let resetRelayer; let homeMetacolony; @@ -109,12 +105,24 @@ contract("Cross-chain", (accounts) => { const ethersHomeSigner2 = new ethers.providers.StaticJsonRpcProvider(homeRpcUrl).getSigner(1); before(async () => { + if (process.env.HARDHAT_FOREIGN === "true") { + // Then we need to deploy the network to the 'home' chain + try { + await exec(`CHAIN_ID=${FORKED_XDAI_CHAINID} npx hardhat deploy --network development2`); + } catch (err) { + console.log(err); + process.exit(1); + } + } + + console.log("execing"); await exec(`PORT=${FOREIGN_PORT} bash ./scripts/setup-foreign-chain.sh`); ({ guardianSpy, resetRelayer, gnosisSafe, zodiacBridge, homeBridge, foreignBridge, foreignColonyBridge, homeColonyBridge } = await setupBridging( homeRpcUrl, foreignRpcUrl, )); + console.log("asdf"); // Add bridge to the foreign colony network // const homeNetworkId = await ethersHomeSigner.provider.send("net_version", []); // Due to limitations, for local testing, our wormhole chainIDs have to be 'real' wormhole chainids. @@ -130,7 +138,11 @@ contract("Cross-chain", (accounts) => { // Deploy shell colonyNetwork to whichever chain truffle hasn't already deployed to. try { - await exec(`CHAIN_ID=${parseInt(foreignChainId, 16)} npx hardhat ensureCreateXDeployed --network development2`); + if (process.env.HARDHAT_FOREIGN === "true") { + await exec(`CHAIN_ID=${parseInt(foreignChainId, 16)} npx hardhat ensureCreateXDeployed --network development`); + } else { + await exec(`CHAIN_ID=${parseInt(foreignChainId, 16)} npx hardhat ensureCreateXDeployed --network development2`); + } const createX = await new ethers.Contract(CREATEX_ADDRESS, ICreateX.abi, ethersForeignSigner); @@ -181,11 +193,11 @@ contract("Cross-chain", (accounts) => { // in to a different location. If we see another chain id, we assume it's non-coverage // truffle and look for the build artifacts in the normal place. - const homeEtherRouterAddress = (await EtherRouter.deployed()).address; - homeColonyNetwork = await new ethers.Contract(homeEtherRouterAddress, IColonyNetwork.abi, ethersHomeSigner); + // const homeEtherRouterAddress = (await EtherRouter.deployed()).address; + homeColonyNetwork = await new ethers.Contract(NETWORK_ADDRESS, IColonyNetwork.abi, ethersHomeSigner); - const foreignEtherRouterAddress = homeEtherRouterAddress; - foreignColonyNetwork = await new ethers.Contract(foreignEtherRouterAddress, ProxyColonyNetwork.abi, ethersForeignSigner); + // const foreignEtherRouterAddress = homeEtherRouterAddress; + foreignColonyNetwork = await new ethers.Contract(NETWORK_ADDRESS, ProxyColonyNetwork.abi, ethersForeignSigner); }); beforeEach(async () => { diff --git a/test/truffle-fixture.js b/test/truffle-fixture.js index bb22dafa57..1a8674f461 100644 --- a/test/truffle-fixture.js +++ b/test/truffle-fixture.js @@ -92,6 +92,12 @@ module.exports = async () => { return; } + const chainId = await getChainId(); + if (chainId !== FORKED_XDAI_CHAINID) { + console.log("Skipping deployment of contracts on non-home chain"); + return; + } + await deployContracts(); await setupColonyNetwork(); await setupColony(); From f49a90924b7cb418b588172346126cc85ec3d21b Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 28 Aug 2024 12:32:48 +0100 Subject: [PATCH 19/72] Update storage-layouts for proxy contracts --- .../bridging/ProxyColony.sol:ProxyColony.json | 79 ++++++++++++ ...yColonyNetwork.sol:ProxyColonyNetwork.json | 91 ++++++++++++++ .../contracts/colony/Colony.sol:Colony.json | 115 +++++++++++++++++- ...action.sol:ColonyArbitraryTransaction.json | 115 +++++++++++++++++- .../ColonyDomains.sol:ColonyDomains.json | 115 +++++++++++++++++- ...lonyExpenditure.sol:ColonyExpenditure.json | 115 +++++++++++++++++- .../ColonyFunding.sol:ColonyFunding.json | 115 +++++++++++++++++- .../ColonyRewards.sol:ColonyRewards.json | 115 +++++++++++++++++- .../colony/ColonyRoles.sol:ColonyRoles.json | 115 +++++++++++++++++- .../ColonyStorage.sol:ColonyStorage.json | 115 +++++++++++++++++- ...ony.sol:FunctionsNotAvailableOnColony.json | 115 +++++++++++++++++- ...LimitSubdomains.sol:NoLimitSubdomains.json | 115 +++++++++++++++++- .../colonyNetwork/ColonyNetworkDeployer.sol | 8 -- 13 files changed, 1310 insertions(+), 18 deletions(-) create mode 100644 .storage-layouts-normalized/contracts/bridging/ProxyColony.sol:ProxyColony.json create mode 100644 .storage-layouts-normalized/contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork.json diff --git a/.storage-layouts-normalized/contracts/bridging/ProxyColony.sol:ProxyColony.json b/.storage-layouts-normalized/contracts/bridging/ProxyColony.sol:ProxyColony.json new file mode 100644 index 0000000000..f020621b32 --- /dev/null +++ b/.storage-layouts-normalized/contracts/bridging/ProxyColony.sol:ProxyColony.json @@ -0,0 +1,79 @@ +{ + "storage": [ + { + "contract": "contracts/bridging/ProxyColony.sol:ProxyColony", + "label": "authority", + "offset": 0, + "slot": "0", + "type": { + "encoding": "inplace", + "label": "contract DSAuthority", + "numberOfBytes": "20" + } + }, + { + "contract": "contracts/bridging/ProxyColony.sol:ProxyColony", + "label": "owner", + "offset": 0, + "slot": "1", + "type": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + } + }, + { + "contract": "contracts/bridging/ProxyColony.sol:ProxyColony", + "label": "resolver", + "offset": 0, + "slot": "2", + "type": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + } + }, + { + "contract": "contracts/bridging/ProxyColony.sol:ProxyColony", + "label": "metatransactionNonces", + "offset": 0, + "slot": "3", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + }, + { + "contract": "contracts/bridging/ProxyColony.sol:ProxyColony", + "label": "tokenBalances", + "offset": 0, + "slot": "4", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + ] +} \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork.json b/.storage-layouts-normalized/contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork.json new file mode 100644 index 0000000000..176e709fd0 --- /dev/null +++ b/.storage-layouts-normalized/contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork.json @@ -0,0 +1,91 @@ +{ + "storage": [ + { + "contract": "contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork", + "label": "authority", + "offset": 0, + "slot": "0", + "type": { + "encoding": "inplace", + "label": "contract DSAuthority", + "numberOfBytes": "20" + } + }, + { + "contract": "contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork", + "label": "owner", + "offset": 0, + "slot": "1", + "type": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + } + }, + { + "contract": "contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork", + "label": "resolver", + "offset": 0, + "slot": "2", + "type": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + } + }, + { + "contract": "contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork", + "label": "colonyBridgeAddress", + "offset": 0, + "slot": "3", + "type": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + } + }, + { + "contract": "contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork", + "label": "homeChainId", + "offset": 0, + "slot": "4", + "type": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + }, + { + "contract": "contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork", + "label": "proxyColonyResolverAddress", + "offset": 0, + "slot": "5", + "type": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + } + }, + { + "contract": "contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork", + "label": "shellColonies", + "offset": 0, + "slot": "6", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => bool)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "bool", + "numberOfBytes": "1" + } + } + } + ] +} \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/Colony.sol:Colony.json b/.storage-layouts-normalized/contracts/colony/Colony.sol:Colony.json index bdaaa40296..d28f85c94b 100644 --- a/.storage-layouts-normalized/contracts/colony/Colony.sol:Colony.json +++ b/.storage-layouts-normalized/contracts/colony/Colony.sol:Colony.json @@ -506,9 +506,71 @@ "label": "uint256", "numberOfBytes": "32" } + }, + { + "contract": "contracts/colony/Colony.sol:Colony", + "label": "chainBalances", + "offset": 0, + "slot": "5", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + }, + { + "contract": "contracts/colony/Colony.sol:Colony", + "label": "chainPayouts", + "offset": 0, + "slot": "6", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ], - "numberOfBytes": "160" + "numberOfBytes": "224" } } }, @@ -1430,6 +1492,57 @@ "numberOfBytes": "32" } } + }, + { + "contract": "contracts/colony/Colony.sol:Colony", + "label": "expenditureSlotChainPayouts", + "offset": 0, + "slot": "40", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(uint256 => mapping(address => uint256))))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(address => uint256)))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyArbitraryTransaction.sol:ColonyArbitraryTransaction.json b/.storage-layouts-normalized/contracts/colony/ColonyArbitraryTransaction.sol:ColonyArbitraryTransaction.json index 44da3bf9d0..79facb58b5 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyArbitraryTransaction.sol:ColonyArbitraryTransaction.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyArbitraryTransaction.sol:ColonyArbitraryTransaction.json @@ -506,9 +506,71 @@ "label": "uint256", "numberOfBytes": "32" } + }, + { + "contract": "contracts/colony/ColonyArbitraryTransaction.sol:ColonyArbitraryTransaction", + "label": "chainBalances", + "offset": 0, + "slot": "5", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + }, + { + "contract": "contracts/colony/ColonyArbitraryTransaction.sol:ColonyArbitraryTransaction", + "label": "chainPayouts", + "offset": 0, + "slot": "6", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ], - "numberOfBytes": "160" + "numberOfBytes": "224" } } }, @@ -1430,6 +1492,57 @@ "numberOfBytes": "32" } } + }, + { + "contract": "contracts/colony/ColonyArbitraryTransaction.sol:ColonyArbitraryTransaction", + "label": "expenditureSlotChainPayouts", + "offset": 0, + "slot": "40", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(uint256 => mapping(address => uint256))))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(address => uint256)))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyDomains.sol:ColonyDomains.json b/.storage-layouts-normalized/contracts/colony/ColonyDomains.sol:ColonyDomains.json index b35aaabbd4..aac0c5e7e1 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyDomains.sol:ColonyDomains.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyDomains.sol:ColonyDomains.json @@ -506,9 +506,71 @@ "label": "uint256", "numberOfBytes": "32" } + }, + { + "contract": "contracts/colony/ColonyDomains.sol:ColonyDomains", + "label": "chainBalances", + "offset": 0, + "slot": "5", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + }, + { + "contract": "contracts/colony/ColonyDomains.sol:ColonyDomains", + "label": "chainPayouts", + "offset": 0, + "slot": "6", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ], - "numberOfBytes": "160" + "numberOfBytes": "224" } } }, @@ -1430,6 +1492,57 @@ "numberOfBytes": "32" } } + }, + { + "contract": "contracts/colony/ColonyDomains.sol:ColonyDomains", + "label": "expenditureSlotChainPayouts", + "offset": 0, + "slot": "40", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(uint256 => mapping(address => uint256))))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(address => uint256)))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyExpenditure.sol:ColonyExpenditure.json b/.storage-layouts-normalized/contracts/colony/ColonyExpenditure.sol:ColonyExpenditure.json index d24e1daa9f..b656fd99dc 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyExpenditure.sol:ColonyExpenditure.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyExpenditure.sol:ColonyExpenditure.json @@ -506,9 +506,71 @@ "label": "uint256", "numberOfBytes": "32" } + }, + { + "contract": "contracts/colony/ColonyExpenditure.sol:ColonyExpenditure", + "label": "chainBalances", + "offset": 0, + "slot": "5", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + }, + { + "contract": "contracts/colony/ColonyExpenditure.sol:ColonyExpenditure", + "label": "chainPayouts", + "offset": 0, + "slot": "6", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ], - "numberOfBytes": "160" + "numberOfBytes": "224" } } }, @@ -1430,6 +1492,57 @@ "numberOfBytes": "32" } } + }, + { + "contract": "contracts/colony/ColonyExpenditure.sol:ColonyExpenditure", + "label": "expenditureSlotChainPayouts", + "offset": 0, + "slot": "40", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(uint256 => mapping(address => uint256))))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(address => uint256)))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyFunding.sol:ColonyFunding.json b/.storage-layouts-normalized/contracts/colony/ColonyFunding.sol:ColonyFunding.json index 3fa29005de..1f874847ef 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyFunding.sol:ColonyFunding.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyFunding.sol:ColonyFunding.json @@ -506,9 +506,71 @@ "label": "uint256", "numberOfBytes": "32" } + }, + { + "contract": "contracts/colony/ColonyFunding.sol:ColonyFunding", + "label": "chainBalances", + "offset": 0, + "slot": "5", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + }, + { + "contract": "contracts/colony/ColonyFunding.sol:ColonyFunding", + "label": "chainPayouts", + "offset": 0, + "slot": "6", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ], - "numberOfBytes": "160" + "numberOfBytes": "224" } } }, @@ -1430,6 +1492,57 @@ "numberOfBytes": "32" } } + }, + { + "contract": "contracts/colony/ColonyFunding.sol:ColonyFunding", + "label": "expenditureSlotChainPayouts", + "offset": 0, + "slot": "40", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(uint256 => mapping(address => uint256))))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(address => uint256)))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyRewards.sol:ColonyRewards.json b/.storage-layouts-normalized/contracts/colony/ColonyRewards.sol:ColonyRewards.json index 82d779f988..cf26a01c95 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyRewards.sol:ColonyRewards.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyRewards.sol:ColonyRewards.json @@ -506,9 +506,71 @@ "label": "uint256", "numberOfBytes": "32" } + }, + { + "contract": "contracts/colony/ColonyRewards.sol:ColonyRewards", + "label": "chainBalances", + "offset": 0, + "slot": "5", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + }, + { + "contract": "contracts/colony/ColonyRewards.sol:ColonyRewards", + "label": "chainPayouts", + "offset": 0, + "slot": "6", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ], - "numberOfBytes": "160" + "numberOfBytes": "224" } } }, @@ -1430,6 +1492,57 @@ "numberOfBytes": "32" } } + }, + { + "contract": "contracts/colony/ColonyRewards.sol:ColonyRewards", + "label": "expenditureSlotChainPayouts", + "offset": 0, + "slot": "40", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(uint256 => mapping(address => uint256))))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(address => uint256)))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyRoles.sol:ColonyRoles.json b/.storage-layouts-normalized/contracts/colony/ColonyRoles.sol:ColonyRoles.json index cffe687525..d25dd781d8 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyRoles.sol:ColonyRoles.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyRoles.sol:ColonyRoles.json @@ -506,9 +506,71 @@ "label": "uint256", "numberOfBytes": "32" } + }, + { + "contract": "contracts/colony/ColonyRoles.sol:ColonyRoles", + "label": "chainBalances", + "offset": 0, + "slot": "5", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + }, + { + "contract": "contracts/colony/ColonyRoles.sol:ColonyRoles", + "label": "chainPayouts", + "offset": 0, + "slot": "6", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ], - "numberOfBytes": "160" + "numberOfBytes": "224" } } }, @@ -1430,6 +1492,57 @@ "numberOfBytes": "32" } } + }, + { + "contract": "contracts/colony/ColonyRoles.sol:ColonyRoles", + "label": "expenditureSlotChainPayouts", + "offset": 0, + "slot": "40", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(uint256 => mapping(address => uint256))))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(address => uint256)))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyStorage.sol:ColonyStorage.json b/.storage-layouts-normalized/contracts/colony/ColonyStorage.sol:ColonyStorage.json index 3c77c7ae35..ac5f587879 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyStorage.sol:ColonyStorage.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyStorage.sol:ColonyStorage.json @@ -506,9 +506,71 @@ "label": "uint256", "numberOfBytes": "32" } + }, + { + "contract": "contracts/colony/ColonyStorage.sol:ColonyStorage", + "label": "chainBalances", + "offset": 0, + "slot": "5", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + }, + { + "contract": "contracts/colony/ColonyStorage.sol:ColonyStorage", + "label": "chainPayouts", + "offset": 0, + "slot": "6", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ], - "numberOfBytes": "160" + "numberOfBytes": "224" } } }, @@ -1430,6 +1492,57 @@ "numberOfBytes": "32" } } + }, + { + "contract": "contracts/colony/ColonyStorage.sol:ColonyStorage", + "label": "expenditureSlotChainPayouts", + "offset": 0, + "slot": "40", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(uint256 => mapping(address => uint256))))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(address => uint256)))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/testHelpers/FunctionsNotAvailableOnColony.sol:FunctionsNotAvailableOnColony.json b/.storage-layouts-normalized/contracts/testHelpers/FunctionsNotAvailableOnColony.sol:FunctionsNotAvailableOnColony.json index 61de74236f..52b584f005 100644 --- a/.storage-layouts-normalized/contracts/testHelpers/FunctionsNotAvailableOnColony.sol:FunctionsNotAvailableOnColony.json +++ b/.storage-layouts-normalized/contracts/testHelpers/FunctionsNotAvailableOnColony.sol:FunctionsNotAvailableOnColony.json @@ -506,9 +506,71 @@ "label": "uint256", "numberOfBytes": "32" } + }, + { + "contract": "contracts/testHelpers/FunctionsNotAvailableOnColony.sol:FunctionsNotAvailableOnColony", + "label": "chainBalances", + "offset": 0, + "slot": "5", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + }, + { + "contract": "contracts/testHelpers/FunctionsNotAvailableOnColony.sol:FunctionsNotAvailableOnColony", + "label": "chainPayouts", + "offset": 0, + "slot": "6", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ], - "numberOfBytes": "160" + "numberOfBytes": "224" } } }, @@ -1430,6 +1492,57 @@ "numberOfBytes": "32" } } + }, + { + "contract": "contracts/testHelpers/FunctionsNotAvailableOnColony.sol:FunctionsNotAvailableOnColony", + "label": "expenditureSlotChainPayouts", + "offset": 0, + "slot": "40", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(uint256 => mapping(address => uint256))))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(address => uint256)))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/testHelpers/NoLimitSubdomains.sol:NoLimitSubdomains.json b/.storage-layouts-normalized/contracts/testHelpers/NoLimitSubdomains.sol:NoLimitSubdomains.json index 5d5e24feb5..74dd6e94ed 100644 --- a/.storage-layouts-normalized/contracts/testHelpers/NoLimitSubdomains.sol:NoLimitSubdomains.json +++ b/.storage-layouts-normalized/contracts/testHelpers/NoLimitSubdomains.sol:NoLimitSubdomains.json @@ -506,9 +506,71 @@ "label": "uint256", "numberOfBytes": "32" } + }, + { + "contract": "contracts/testHelpers/NoLimitSubdomains.sol:NoLimitSubdomains", + "label": "chainBalances", + "offset": 0, + "slot": "5", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + }, + { + "contract": "contracts/testHelpers/NoLimitSubdomains.sol:NoLimitSubdomains", + "label": "chainPayouts", + "offset": 0, + "slot": "6", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ], - "numberOfBytes": "160" + "numberOfBytes": "224" } } }, @@ -1430,6 +1492,57 @@ "numberOfBytes": "32" } } + }, + { + "contract": "contracts/testHelpers/NoLimitSubdomains.sol:NoLimitSubdomains", + "label": "expenditureSlotChainPayouts", + "offset": 0, + "slot": "40", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(uint256 => mapping(address => uint256))))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(uint256 => mapping(address => uint256)))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } + } + } } ] } \ No newline at end of file diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index 09c847eeaf..13c4d17e6a 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -333,12 +333,4 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage { ); return address(etherRouter); } - - function isContract(address addr) internal view returns (bool) { - uint256 size; - assembly { - size := extcodesize(addr) - } - return size > 0; - } } From ef02f1b1e0eb5b516b5f7fc92b91fc636d52820d Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 28 Aug 2024 14:08:17 +0100 Subject: [PATCH 20/72] Continue trying to get a green CircleCI build on branch --- contracts/colony/Colony.sol | 2 +- scripts/check-recovery.js | 2 ++ scripts/check-storage.js | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index c5ea8b14c8..19f9874836 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -324,7 +324,7 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); } - function createProxyColony(uint256 _destinationChainId, bytes32 _salt) public { + function createProxyColony(uint256 _destinationChainId, bytes32 _salt) public stoppable { IColonyNetwork(colonyNetworkAddress).createProxyColony(_destinationChainId, _salt); } diff --git a/scripts/check-recovery.js b/scripts/check-recovery.js index c3bb09cf88..e391b14dc5 100755 --- a/scripts/check-recovery.js +++ b/scripts/check-recovery.js @@ -27,6 +27,8 @@ walkSync("./contracts/").forEach((contractName) => { [ "contracts/bridging/IColonyBridge.sol", "contracts/bridging/WormholeBridgeForColony.sol", + "contracts/bridging/ProxyColony.sol", + "contracts/bridging/ProxyColonyNetwork.sol", "contracts/colony/ColonyAuthority.sol", "contracts/colony/ColonyStorage.sol", "contracts/colony/IColony.sol", diff --git a/scripts/check-storage.js b/scripts/check-storage.js index bf0056bebf..23101b7170 100755 --- a/scripts/check-storage.js +++ b/scripts/check-storage.js @@ -17,6 +17,8 @@ walkSync("./contracts/").forEach((contractName) => { if ( [ "contracts/bridging/WormholeBridgeForColony.sol", + "contracts/bridging/ProxyColony.sol", + "contracts/bridging/ProxyColonyNetwork.sol", "contracts/colony/ColonyAuthority.sol", "contracts/colony/ColonyStorage.sol", "contracts/colonyNetwork/ColonyNetworkAuthority.sol", From 227fd2d93ca0e005852b694d7a6f236c4b526278 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 28 Aug 2024 16:00:46 +0100 Subject: [PATCH 21/72] Vaguely port chainid-based-behaviour tests (needs more work) --- .circleci/config.yml | 3 - .../colonyNetwork/ColonyNetworkMining.sol | 24 ++-- .../colonyNetwork/ColonyNetworkStorage.sol | 25 ++--- helpers/test-data-generator.js | 16 ++- test/cross-chain/cross-chain.js | 104 +++++++++--------- test/deploy-proxy-network-fixture.js | 2 + test/misc/chainid.js | 86 +++------------ test/truffle-fixture.js | 7 +- 8 files changed, 102 insertions(+), 165 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index dfa6dc0a5e..651ce88da3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -327,9 +327,6 @@ jobs: - persist_to_workspace: root: ./ paths: - - coverage-chainid-1 - - coverage-chainid-5 - - coverage-chainid-100 - coverage-chainid-777 coverage-test-bridging: <<: *job_common diff --git a/contracts/colonyNetwork/ColonyNetworkMining.sol b/contracts/colonyNetwork/ColonyNetworkMining.sol index e2fe1417ad..d31ea0c915 100644 --- a/contracts/colonyNetwork/ColonyNetworkMining.sol +++ b/contracts/colonyNetwork/ColonyNetworkMining.sol @@ -41,7 +41,7 @@ contract ColonyNetworkMining is ColonyNetworkStorage { _; } - function setMiningDelegate(address _delegate, bool _allowed) public onlyMiningChain stoppable { + function setMiningDelegate(address _delegate, bool _allowed) public stoppable miningInitialised { if (miningDelegators[_delegate] != address(0x00)) { require( miningDelegators[_delegate] == msgSender(), @@ -69,7 +69,7 @@ contract ColonyNetworkMining is ColonyNetworkStorage { address _colony, uint128 _nUpdates, uint128 _nPreviousUpdates - ) public onlyMiningChain recovery auth { + ) public recovery auth miningInitialised { replacementReputationUpdateLogsExist[_reputationMiningCycle] = true; replacementReputationUpdateLog[_reputationMiningCycle][_id] = ReputationLogEntry( @@ -102,7 +102,7 @@ contract ColonyNetworkMining is ColonyNetworkStorage { bytes32 newHash, uint256 newNLeaves, address[] memory stakers - ) public onlyMiningChain stoppable onlyReputationMiningCycle { + ) public stoppable miningInitialised onlyReputationMiningCycle { reputationRootHash = newHash; reputationRootHashNLeaves = newNLeaves; // Reward stakers @@ -163,7 +163,7 @@ contract ColonyNetworkMining is ColonyNetworkStorage { } // slither-disable-next-line reentrancy-no-eth - function startNextCycle() public onlyMiningChain stoppable { + function startNextCycle() public stoppable miningInitialised { address clnyToken = IMetaColony(metaColony).getToken(); require(clnyToken != address(0x0), "colony-reputation-mining-clny-token-invalid-address"); require(activeReputationMiningCycle == address(0x0), "colony-reputation-mining-still-active"); @@ -263,7 +263,7 @@ contract ColonyNetworkMining is ColonyNetworkStorage { function punishStakers( address[] memory _stakers, uint256 _amount - ) public onlyMiningChain stoppable onlyReputationMiningCycle { + ) public stoppable miningInitialised onlyReputationMiningCycle { address clnyToken = IMetaColony(metaColony).getToken(); uint256 lostStake; // Passing an array so that we don't incur the EtherRouter overhead for each staker if we looped over @@ -285,19 +285,19 @@ contract ColonyNetworkMining is ColonyNetworkStorage { function reward( address _recipient, uint256 _amount - ) public onlyMiningChain stoppable onlyReputationMiningCycle { + ) public stoppable miningInitialised onlyReputationMiningCycle { // TODO: Gain rep? pendingMiningRewards[_recipient] += _amount; } - function claimMiningReward(address _recipient) public onlyMiningChain stoppable { + function claimMiningReward(address _recipient) public miningInitialised stoppable { address clnyToken = IMetaColony(metaColony).getToken(); uint256 amount = pendingMiningRewards[_recipient]; pendingMiningRewards[_recipient] = 0; ITokenLocking(tokenLocking).transfer(clnyToken, amount, _recipient, true); } - function stakeForMining(uint256 _amount) public onlyMiningChainOrDuringSetup stoppable { + function stakeForMining(uint256 _amount) public stoppable miningInitialisedOrDuringSetup { address clnyToken = IMetaColony(metaColony).getToken(); ITokenLocking(tokenLocking).approveStake(msgSender(), _amount, clnyToken); @@ -312,7 +312,7 @@ contract ColonyNetworkMining is ColonyNetworkStorage { miningStakes[msgSender()].amount += _amount; } - function unstakeForMining(uint256 _amount) public onlyMiningChain stoppable { + function unstakeForMining(uint256 _amount) public stoppable miningInitialised { address clnyToken = IMetaColony(metaColony).getToken(); // Prevent those involved in a mining cycle withdrawing stake during the mining process. require( @@ -329,7 +329,7 @@ contract ColonyNetworkMining is ColonyNetworkStorage { function burnUnneededRewards( uint256 _amount - ) public onlyMiningChain stoppable onlyReputationMiningCycle { + ) public stoppable miningInitialised onlyReputationMiningCycle { // If there are no rewards to burn, no need to do anything if (_amount == 0) { return; @@ -346,7 +346,7 @@ contract ColonyNetworkMining is ColonyNetworkStorage { function setReputationMiningCycleReward( uint256 _amount - ) public onlyMiningChain stoppable calledByMetaColony { + ) public stoppable miningInitialised calledByMetaColony { totalMinerRewardPerCycle = _amount; emit ReputationMiningRewardSet(_amount); @@ -380,7 +380,7 @@ contract ColonyNetworkMining is ColonyNetworkStorage { function setMiningResolver( address _miningResolver - ) public stoppable onlyMiningChainOrDuringSetup auth { + ) public stoppable auth miningInitialisedOrDuringSetup { require(_miningResolver != address(0x0), "colony-mining-resolver-cannot-be-zero"); miningCycleResolver = _miningResolver; diff --git a/contracts/colonyNetwork/ColonyNetworkStorage.sol b/contracts/colonyNetwork/ColonyNetworkStorage.sol index cd2f0b7c8f..3e604965de 100644 --- a/contracts/colonyNetwork/ColonyNetworkStorage.sol +++ b/contracts/colonyNetwork/ColonyNetworkStorage.sol @@ -172,30 +172,22 @@ contract ColonyNetworkStorage is _; } - modifier onlyMiningChain() { - if (getMiningChainId() == block.chainid) { - require( - inactiveReputationMiningCycle != address(0x0), - "colony-reputation-mining-not-initialised" - ); - } - require(isMiningChain(), "colony-only-valid-on-mining-chain"); + modifier miningInitialised() { + require( + inactiveReputationMiningCycle != address(0x0), + "colony-reputation-mining-not-initialised" + ); _; } - modifier onlyMiningChainOrDuringSetup() { + modifier miningInitialisedOrDuringSetup() { require( - isMiningChain() || getMiningChainId() == 0, + inactiveReputationMiningCycle != address(0x0) || getMiningChainId() == 0, "colony-only-valid-on-mining-chain-or-during-setup" ); _; } - modifier onlyNotMiningChain() { - require(!isMiningChain(), "colony-only-valid-not-on-mining-chain"); - _; - } - // Internal functions function toRootSkillId(uint256 _chainId) internal pure returns (uint256) { @@ -212,9 +204,6 @@ contract ColonyNetworkStorage is } function getMiningChainId() public view returns (uint256) { - if (reputationMiningChainId == 0 && isXdai()) { - return block.chainid; - } return reputationMiningChainId; } diff --git a/helpers/test-data-generator.js b/helpers/test-data-generator.js index 30deb98ace..78f6d186ae 100644 --- a/helpers/test-data-generator.js +++ b/helpers/test-data-generator.js @@ -3,7 +3,18 @@ const BN = require("bn.js"); const { signTypedData_v4: signTypedData } = require("eth-sig-util"); -const { UINT256_MAX, MANAGER_PAYOUT, EVALUATOR_PAYOUT, WORKER_PAYOUT, INITIAL_FUNDING, SLOT0, SLOT1, SLOT2, ADDRESS_ZERO } = require("./constants"); +const { + UINT256_MAX, + MANAGER_PAYOUT, + EVALUATOR_PAYOUT, + WORKER_PAYOUT, + INITIAL_FUNDING, + SLOT0, + SLOT1, + SLOT2, + ADDRESS_ZERO, + NETWORK_ADDRESS, +} = require("./constants"); const { getTokenArgs, web3GetAccounts, getChildSkillIndex, getChainId } = require("./test-helper"); @@ -226,8 +237,7 @@ exports.unlockCLNYToken = async function unlockCLNYToken(metaColony) { }; exports.setupColonyNetwork = async function setupColonyNetwork() { - const cnAddress = (await EtherRouter.deployed()).address; - const deployedColonyNetwork = await IColonyNetwork.at(cnAddress); + const deployedColonyNetwork = await IColonyNetwork.at(NETWORK_ADDRESS); // Make a new ColonyNetwork const etherRouter = await EtherRouter.new(); diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index ede51022cc..65ac1330b1 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -23,13 +23,10 @@ const { expect } = chai; chai.use(bnChai(web3.utils.BN)); const IColonyNetwork = artifacts.require("IColonyNetwork"); -const EtherRouterCreate3 = artifacts.require("EtherRouterCreate3"); const EtherRouter = artifacts.require("EtherRouter"); const IMetaColony = artifacts.require("IMetaColony"); -const Resolver = artifacts.require("Resolver"); const Token = artifacts.require("Token"); const IColony = artifacts.require("IColony"); -const ICreateX = artifacts.require("ICreateX"); const ProxyColonyNetwork = artifacts.require("ProxyColonyNetwork"); const ProxyColony = artifacts.require("ProxyColony"); const MetaTxToken = artifacts.require("MetaTxToken"); @@ -48,7 +45,6 @@ const { const { forwardTime, checkErrorRevertEthers, revert, snapshot, evmChainIdToWormholeChainId } = require("../../helpers/test-helper"); const ReputationMinerTestWrapper = require("../../packages/reputation-miner/test/ReputationMinerTestWrapper"); const { TruffleLoader } = require("../../packages/package-utils"); -const { setupProxyColonyNetwork, setupEtherRouter } = require("../../helpers/upgradable-contracts"); const UINT256_MAX_ETHERS = ethers.BigNumber.from(2).pow(256).sub(1); @@ -137,56 +133,56 @@ contract("Cross-chain", (accounts) => { wormholeForeignChainId = evmChainIdToWormholeChainId(foreignChainId); // Deploy shell colonyNetwork to whichever chain truffle hasn't already deployed to. - try { - if (process.env.HARDHAT_FOREIGN === "true") { - await exec(`CHAIN_ID=${parseInt(foreignChainId, 16)} npx hardhat ensureCreateXDeployed --network development`); - } else { - await exec(`CHAIN_ID=${parseInt(foreignChainId, 16)} npx hardhat ensureCreateXDeployed --network development2`); - } - - const createX = await new ethers.Contract(CREATEX_ADDRESS, ICreateX.abi, ethersForeignSigner); - - // This is a fake instance of an etherRouter, just so we can call encodeABs - const fakeEtherRouter = await EtherRouterCreate3.at(CREATEX_ADDRESS); - const setOwnerData = fakeEtherRouter.contract.methods.setOwner(accounts[0]).encodeABI(); - - const tx = await createX["deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))"]( - `0xb77d57f4959eafa0339424b83fcfaf9c15407461005e95d52076387600e2c1e9`, - EtherRouterCreate3.bytecode, - setOwnerData, - [0, 0], - { from: accounts[0] }, - ); - - const receipt = await tx.wait(); - - const etherRouter = await new ethers.Contract( - receipt.events.filter((log) => log.event === "ContractCreation")[0].args.newContract, - EtherRouter.abi, - ethersForeignSigner, - ); - let resolver = await new ethers.ContractFactory(Resolver.abi, Resolver.bytecode, ethersForeignSigner).deploy(); - const proxyColonyNetworkImplementation = await new ethers.ContractFactory( - ProxyColonyNetwork.abi, - ProxyColonyNetwork.bytecode, - ethersForeignSigner, - ).deploy(); - - await setupProxyColonyNetwork(etherRouter, proxyColonyNetworkImplementation, resolver); - console.log("**** shell colony network set up"); - - // Set up the resolver for shell colonies - resolver = await new ethers.ContractFactory(Resolver.abi, Resolver.bytecode, ethersForeignSigner).deploy(); - const proxyColonyImplementation = await new ethers.ContractFactory(ProxyColony.abi, ProxyColony.bytecode, ethersForeignSigner).deploy(); - - await setupEtherRouter("bridging", "ProxyColony", { ProxyColony: proxyColonyImplementation.address }, resolver); - const proxyColonyNetwork = new ethers.Contract(etherRouter.address, ProxyColonyNetwork.abi, ethersForeignSigner); - - await proxyColonyNetwork.setProxyColonyResolverAddress(resolver.address); - } catch (err) { - console.log(err); - process.exit(1); - } + // try { + // if (process.env.HARDHAT_FOREIGN === "true") { + // await exec(`CHAIN_ID=${parseInt(foreignChainId, 16)} npx hardhat ensureCreateXDeployed --network development`); + // } else { + // await exec(`CHAIN_ID=${parseInt(foreignChainId, 16)} npx hardhat ensureCreateXDeployed --network development2`); + // } + + // const createX = await new ethers.Contract(CREATEX_ADDRESS, ICreateX.abi, ethersForeignSigner); + + // // This is a fake instance of an etherRouter, just so we can call encodeABs + // const fakeEtherRouter = await EtherRouterCreate3.at(CREATEX_ADDRESS); + // const setOwnerData = fakeEtherRouter.contract.methods.setOwner(accounts[0]).encodeABI(); + + // const tx = await createX["deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))"]( + // `0xb77d57f4959eafa0339424b83fcfaf9c15407461005e95d52076387600e2c1e9`, + // EtherRouterCreate3.bytecode, + // setOwnerData, + // [0, 0], + // { from: accounts[0] }, + // ); + + // const receipt = await tx.wait(); + + // const etherRouter = await new ethers.Contract( + // receipt.events.filter((log) => log.event === "ContractCreation")[0].args.newContract, + // EtherRouter.abi, + // ethersForeignSigner, + // ); + // let resolver = await new ethers.ContractFactory(Resolver.abi, Resolver.bytecode, ethersForeignSigner).deploy(); + // const proxyColonyNetworkImplementation = await new ethers.ContractFactory( + // ProxyColonyNetwork.abi, + // ProxyColonyNetwork.bytecode, + // ethersForeignSigner, + // ).deploy(); + + // await setupProxyColonyNetwork(etherRouter, proxyColonyNetworkImplementation, resolver); + // console.log("**** shell colony network set up"); + + // // Set up the resolver for shell colonies + // resolver = await new ethers.ContractFactory(Resolver.abi, Resolver.bytecode, ethersForeignSigner).deploy(); + // const proxyColonyImplementation = await new ethers.ContractFactory(ProxyColony.abi, ProxyColony.bytecode, ethersForeignSigner).deploy(); + + // await setupEtherRouter("bridging", "ProxyColony", { ProxyColony: proxyColonyImplementation.address }, resolver); + // const proxyColonyNetwork = new ethers.Contract(etherRouter.address, ProxyColonyNetwork.abi, ethersForeignSigner); + + // await proxyColonyNetwork.setProxyColonyResolverAddress(resolver.address); + // } catch (err) { + // console.log(err); + // process.exit(1); + // } // 0x539 is the chain id used by truffle by default (regardless of networkid), and if // we see it in our tests that's the coverage chain, which builds the contract artifacts diff --git a/test/deploy-proxy-network-fixture.js b/test/deploy-proxy-network-fixture.js index 92d23a600a..1b5100041a 100644 --- a/test/deploy-proxy-network-fixture.js +++ b/test/deploy-proxy-network-fixture.js @@ -42,6 +42,8 @@ module.exports = async () => { let resolver = await Resolver.new(); + await Resolver.setAsDeployed(resolver); + await setupProxyColonyNetwork(etherRouter, proxyColonyNetworkImplementation, resolver); // Set up the resolver for shell colonies and register it with the network diff --git a/test/misc/chainid.js b/test/misc/chainid.js index 50d42954fe..d1cdc409ea 100644 --- a/test/misc/chainid.js +++ b/test/misc/chainid.js @@ -24,12 +24,11 @@ const { isXdai, getChainId, } = require("../../helpers/test-helper"); -const { MINING_CYCLE_DURATION, MIN_STAKE, CHALLENGE_RESPONSE_WINDOW_DURATION, WAD, DEFAULT_STAKE, XDAI_CHAINID } = require("../../helpers/constants"); +const { MINING_CYCLE_DURATION, MIN_STAKE, CHALLENGE_RESPONSE_WINDOW_DURATION, WAD, DEFAULT_STAKE } = require("../../helpers/constants"); const { expect } = chai; const ENSRegistry = artifacts.require("ENSRegistry"); const DutchAuction = artifacts.require("DutchAuction"); -const ITokenLocking = artifacts.require("ITokenLocking"); const Token = artifacts.require("Token"); chai.use(bnChai(web3.utils.BN)); @@ -54,18 +53,14 @@ contract("Contract Storage", (accounts) => { ({ metaColony, clnyToken } = await setupMetaColonyWithLockedCLNYToken(colonyNetwork)); const ensRegistry = await ENSRegistry.new(); await setupENSRegistrar(colonyNetwork, ensRegistry, accounts[0]); - if (await isXdai()) { - await giveUserCLNYTokensAndStake(colonyNetwork, MINER1, DEFAULT_STAKE); - await giveUserCLNYTokensAndStake(colonyNetwork, MINER2, DEFAULT_STAKE); - await giveUserCLNYTokensAndStake(colonyNetwork, MINER3, DEFAULT_STAKE); - - await metaColony.initialiseReputationMining(chainId, ethers.constants.HashZero, 0); - } else { - await metaColony.initialiseReputationMining(XDAI_CHAINID, ethers.constants.HashZero, 0); - } + await giveUserCLNYTokensAndStake(colonyNetwork, MINER1, DEFAULT_STAKE); + await giveUserCLNYTokensAndStake(colonyNetwork, MINER2, DEFAULT_STAKE); + await giveUserCLNYTokensAndStake(colonyNetwork, MINER3, DEFAULT_STAKE); + + await metaColony.initialiseReputationMining(chainId, ethers.constants.HashZero, 0); }); - describe("Should behave differently based on the network deployed to", () => { + describe("Should behave appropriately with old per-chain behaviours", () => { it("should be able to get the domain name", async () => { await metaColony.registerColonyLabel("meta", "", { from: accounts[0] }); if (await isMainnet()) { @@ -79,26 +74,7 @@ contract("Contract Storage", (accounts) => { } }); - it("can only stake tokens for mining (and therefore can only mine) on the mining chain", async () => { - await giveUserCLNYTokens(colonyNetwork, MINER1, DEFAULT_STAKE); - const tokenLockingAddress = await colonyNetwork.getTokenLocking(); - const tokenLocking = await ITokenLocking.at(tokenLockingAddress); - await clnyToken.approve(tokenLocking.address, DEFAULT_STAKE, { from: MINER1 }); - await tokenLocking.methods["deposit(address,uint256,bool)"](clnyToken.address, DEFAULT_STAKE, true, { from: MINER1 }); - const tx = colonyNetwork.stakeForMining(DEFAULT_STAKE, { from: MINER1 }); - - if (await isXdai()) { - await tx; - } else { - await checkErrorRevert(tx, "colony-only-valid-on-mining-chain-or-during-setup"); - } - }); - it("should not make 0-value transfers to 'burn' unneeded mining rewards on xdai", async function () { - if (!(await isXdai())) { - // We don't mine anywhere else, so skip - this.skip(); - } await giveUserCLNYTokensAndStake(colonyNetwork, MINER1, MIN_STAKE); await advanceMiningCycleNoContest({ colonyNetwork, test: this }); @@ -117,23 +93,13 @@ contract("Contract Storage", (accounts) => { it("should handle tokens appropriately if auction is initialised for the CLNY token", async () => { await giveUserCLNYTokens(colonyNetwork, colonyNetwork.address, WAD); const supplyBefore = await clnyToken.totalSupply(); - const balanceBefore = await clnyToken.balanceOf(colonyNetwork.address); const tx = await colonyNetwork.startTokenAuction(clnyToken.address); const supplyAfter = await clnyToken.totalSupply(); - const balanceAfter = await clnyToken.balanceOf(colonyNetwork.address); - if (await isMainnet()) { - // tokens should be burned. - expect(supplyBefore.sub(supplyAfter)).to.eq.BN(balanceBefore); - await expectEvent(tx, "Burn(address indexed,uint256)", [colonyNetwork.address, WAD]); - expect(balanceAfter).to.be.zero; - expect(supplyBefore.sub(balanceBefore)).to.eq.BN(supplyAfter); - } else { - // tokens should be transferred to metacolony - expect(supplyBefore).to.eq.BN(supplyAfter); - await expectEvent(tx, "Transfer(address indexed,address indexed,uint256)", [colonyNetwork.address, metaColony.address, WAD]); - } + // tokens should be transferred to metacolony + expect(supplyBefore).to.eq.BN(supplyAfter); + await expectEvent(tx, "Transfer(address indexed,address indexed,uint256)", [colonyNetwork.address, metaColony.address, WAD]); }); it("CLNY raised from auctions is dealt with appropriately", async () => { @@ -153,7 +119,6 @@ contract("Contract Storage", (accounts) => { await clnyToken.approve(tokenAuction.address, clnyNeededForMaxPriceAuctionSellout, { from: accounts[1] }); await tokenAuction.bid(clnyNeededForMaxPriceAuctionSellout, { from: accounts[1] }); - const balanceBefore = await clnyToken.balanceOf(tokenAuction.address); const supplyBefore = await clnyToken.totalSupply(); const receivedTotal = await tokenAuction.receivedTotal(); expect(receivedTotal).to.not.be.zero; @@ -162,34 +127,9 @@ contract("Contract Storage", (accounts) => { const balanceAfter = await clnyToken.balanceOf(tokenAuction.address); expect(balanceAfter).to.be.zero; const supplyAfter = await clnyToken.totalSupply(); - if (await isMainnet()) { - // tokens should be burned. - expect(supplyBefore.sub(supplyAfter)).to.eq.BN(balanceBefore); - await expectEvent(tx, "Burn(address indexed,uint256)", [tokenAuction.address, receivedTotal]); - } else { - // tokens should be transferred to metacolony - expect(supplyBefore).to.eq.BN(supplyAfter); - await expectEvent(tx, "Transfer(address indexed,address indexed,uint256)", [tokenAuction.address, metaColony.address, receivedTotal]); - } - }); - - it("reputation mining chain can be changed on non-mining chain", async function () { - if (await isXdai()) { - // Not appropriate test on xdai - this.skip(); - } - - const oldChainId = await colonyNetwork.getMiningChainId(); - - const otherChainId = oldChainId + 1; - await metaColony.initialiseReputationMining(otherChainId, ethers.constants.HashZero, 0); - - const newChainId = await colonyNetwork.getMiningChainId(); - expect(newChainId).to.eq.BN(oldChainId + 1); - }); - - it.skip("reputation mining chain can be changed on mining chain", async () => { - // Not a test, or contract code, that has been written yet + // tokens should be transferred to metacolony + expect(supplyBefore).to.eq.BN(supplyAfter); + await expectEvent(tx, "Transfer(address indexed,address indexed,uint256)", [tokenAuction.address, metaColony.address, receivedTotal]); }); }); }); diff --git a/test/truffle-fixture.js b/test/truffle-fixture.js index 1a8674f461..785afb9ea1 100644 --- a/test/truffle-fixture.js +++ b/test/truffle-fixture.js @@ -93,8 +93,11 @@ module.exports = async () => { } const chainId = await getChainId(); - if (chainId !== FORKED_XDAI_CHAINID) { - console.log("Skipping deployment of contracts on non-home chain"); + const miningChainId = parseInt(process.env.MINING_CHAIN_ID, 10) || chainId; + + if (chainId !== miningChainId) { + console.log("On non-mining chain, so deploy proxy infrastructure"); + await hre.run("deploy-proxy-network"); return; } From fad615b82654192229d9f047878e648b16b379f2 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Thu, 29 Aug 2024 10:12:54 +0100 Subject: [PATCH 22/72] Streamline deployments for two chains --- test/cross-chain/cross-chain.js | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 65ac1330b1..da307959a0 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -39,7 +39,6 @@ const { ROOT_ROLE, CURR_VERSION, CREATEX_ADDRESS, - FORKED_XDAI_CHAINID, NETWORK_ADDRESS, } = require("../../helpers/constants"); const { forwardTime, checkErrorRevertEthers, revert, snapshot, evmChainIdToWormholeChainId } = require("../../helpers/test-helper"); @@ -101,14 +100,22 @@ contract("Cross-chain", (accounts) => { const ethersHomeSigner2 = new ethers.providers.StaticJsonRpcProvider(homeRpcUrl).getSigner(1); before(async () => { - if (process.env.HARDHAT_FOREIGN === "true") { - // Then we need to deploy the network to the 'home' chain - try { - await exec(`CHAIN_ID=${FORKED_XDAI_CHAINID} npx hardhat deploy --network development2`); - } catch (err) { - console.log(err); - process.exit(1); - } + homeChainId = await ethersHomeSigner.provider.send("eth_chainId", []); + foreignChainId = await ethersForeignSigner.provider.send("eth_chainId", []); + + console.log("home chain id", homeChainId); + console.log("foreign chain id", foreignChainId); + console.log(process.env.HARDHAT_FOREIGN); + // We need to deploy the network to the other chain + try { + await exec( + `CHAIN_ID=${ + process.env.HARDHAT_FOREIGN === "true" ? parseInt(homeChainId, 16) : parseInt(foreignChainId, 16) + } npx hardhat deploy --network development2`, + ); + } catch (err) { + console.log(err); + process.exit(1); } console.log("execing"); From 749a8b8f50190698180424fec17ce22fc4e9f725 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Thu, 29 Aug 2024 11:52:33 +0100 Subject: [PATCH 23/72] ChainID tests still valid for Xdai and Arbitrum --- .circleci/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 651ce88da3..fc01b1b1d1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -319,6 +319,11 @@ jobs: command: CHAIN_ID=100 pnpm run test:contracts:chainid:coverage environment: NODE_OPTIONS: --max-old-space-size=6144 + - run: + name: "Running chainid tests for Arbitrum One" + command: CHAIN_ID=42161 pnpm run test:contracts:chainid:coverage + environment: + NODE_OPTIONS: --max-old-space-size=6144 - run: name: "Running chainid tests with coverage for an unsupported network" command: CHAIN_ID=777 pnpm run test:contracts:chainid:coverage @@ -327,7 +332,10 @@ jobs: - persist_to_workspace: root: ./ paths: + - coverage-chainid-100 + - coverage-chainid-42161 - coverage-chainid-777 + - coverage-chainid-1 coverage-test-bridging: <<: *job_common resource_class: large From df09889f3712ddf8a74557d1732109f8640ced88 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Thu, 29 Aug 2024 12:33:32 +0100 Subject: [PATCH 24/72] More bridging tests --- test/cross-chain/cross-chain.js | 144 ++++++++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 14 deletions(-) diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index da307959a0..fdf33b93bd 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -103,9 +103,6 @@ contract("Cross-chain", (accounts) => { homeChainId = await ethersHomeSigner.provider.send("eth_chainId", []); foreignChainId = await ethersForeignSigner.provider.send("eth_chainId", []); - console.log("home chain id", homeChainId); - console.log("foreign chain id", foreignChainId); - console.log(process.env.HARDHAT_FOREIGN); // We need to deploy the network to the other chain try { await exec( @@ -118,7 +115,6 @@ contract("Cross-chain", (accounts) => { process.exit(1); } - console.log("execing"); await exec(`PORT=${FOREIGN_PORT} bash ./scripts/setup-foreign-chain.sh`); ({ guardianSpy, resetRelayer, gnosisSafe, zodiacBridge, homeBridge, foreignBridge, foreignColonyBridge, homeColonyBridge } = await setupBridging( homeRpcUrl, @@ -218,9 +214,8 @@ contract("Cross-chain", (accounts) => { const proxyMCAddress = await homeColonyNetwork.getMetaColony(); // Not a mistake - they have the same address, and .getMetaColony doesn't exist on ProxyColonyNetwork proxyMetacolony = await new ethers.Contract(proxyMCAddress, IMetaColony.abi, ethersForeignSigner); - console.log("get mc"); + const homeMCAddress = await homeColonyNetwork.getMetaColony(); - console.log("got mc"); homeMetacolony = await new ethers.Contract(homeMCAddress, IMetaColony.abi, ethersHomeSigner); await setForeignBridgeData(homeColonyBridge.address, foreignColonyBridge.address, ethersHomeSigner, ethersForeignSigner); @@ -297,9 +292,8 @@ contract("Cross-chain", (accounts) => { expect(homeColonyNetwork.address).to.equal(foreignColonyNetwork.address); // Check we have colony Network there - this equality is expected because of how we set up the addresses const homeVersionResolver = await homeColonyNetwork.getColonyVersionResolver(CURR_VERSION); - console.log(foreignColonyNetwork.address); - console.log(await ethersForeignProvider.getBlockNumber()); const proxyColonyResolver = await foreignColonyNetwork.proxyColonyResolverAddress(); + expect(homeVersionResolver).to.not.equal(ADDRESS_ZERO); expect(proxyColonyResolver).to.not.equal(ADDRESS_ZERO); }); @@ -1244,6 +1238,29 @@ contract("Cross-chain", (accounts) => { expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); }); + it("Can track native tokens received on foreign chains", async () => { + const tokenAmount = ethers.utils.parseEther("1"); + + await ethersForeignSigner.sendTransaction({ + to: proxyColony.address, + value: tokenAmount, + }); + + // Claim on foreign chain + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + + const tx = await proxyColony.claimTokens(ADDRESS_ZERO); + await tx.wait(); + + const receipt = await p; + expect(receipt.status).to.equal(1); + + // Check bookkeeping on the home chain + + const balance = await colony.getFundingPotProxyBalance(1, foreignChainId, ADDRESS_ZERO); + expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); + }); + it("Can track tokens sent on the foreign chain", async () => { const tokenAmount = ethers.utils.parseEther("100"); @@ -1266,8 +1283,6 @@ contract("Cross-chain", (accounts) => { tx = await colony.setExpenditureRecipient(expenditureId, 1, accounts[0]); await tx.wait(); - console.log("set recipient"); - tx = await colony["setExpenditurePayout(uint256,uint256,uint256,uint256,uint256,address,uint256)"]( 1, UINT256_MAX_ETHERS, @@ -1278,7 +1293,7 @@ contract("Cross-chain", (accounts) => { paymentAmount, ); await tx.wait(); - console.log("set payout"); + const domain1 = await colony.getDomain(1); const expenditure = await colony.getExpenditure(expenditureId); @@ -1314,6 +1329,80 @@ contract("Cross-chain", (accounts) => { expect(colonyBalance.toHexString()).to.equal(ethers.utils.parseEther("70").toHexString()); expect(recipientBalance.toHexString()).to.equal(ethers.utils.parseEther("30").toHexString()); }); + + it("Can track native tokens sent on the foreign chain", async () => { + const tokenAmount = ethers.utils.parseEther("1"); + + let tx = await ethersForeignSigner.sendTransaction({ + to: proxyColony.address, + value: tokenAmount, + }); + await tx.wait(); + + // Claim on the foreign chain + let p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + tx = await proxyColony.claimTokens(ADDRESS_ZERO); + await tx.wait(); + await p; + + // Make a payment that pays out 0.3 + + const paymentAmount = ethers.utils.parseEther("0.3"); + tx = await colony.makeExpenditure(1, UINT256_MAX_ETHERS, 1); + await tx.wait(); + const expenditureId = await colony.getExpenditureCount(); + + tx = await colony.setExpenditureRecipient(expenditureId, 1, accounts[0]); + await tx.wait(); + + tx = await colony["setExpenditurePayout(uint256,uint256,uint256,uint256,uint256,address,uint256)"]( + 1, + UINT256_MAX_ETHERS, + expenditureId, + 1, + foreignChainId, + ADDRESS_ZERO, + paymentAmount, + ); + await tx.wait(); + + const domain1 = await colony.getDomain(1); + const expenditure = await colony.getExpenditure(expenditureId); + + tx = await colony["moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)"]( + 1, + UINT256_MAX_ETHERS, + 1, + UINT256_MAX_ETHERS, + UINT256_MAX_ETHERS, + domain1.fundingPotId, + expenditure.fundingPotId, + paymentAmount, + foreignChainId, + ADDRESS_ZERO, + ); + await tx.wait(); + tx = await colony.finalizeExpenditure(expenditureId); + await tx.wait(); + + const receipientBalanceBefore = await ethersForeignProvider.getBalance(accounts[0]); + + p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + tx = await colony["claimExpenditurePayout(uint256,uint256,uint256,address)"](expenditureId, 1, foreignChainId, ADDRESS_ZERO); + await tx.wait(); + await p; + // Check bookkeeping on the home chain + + const balance1 = await colony.getFundingPotProxyBalance(1, foreignChainId, ADDRESS_ZERO); + expect(balance1.toHexString()).to.equal(ethers.utils.parseEther("0.7").toHexString()); + + // Check actually paid on foreign chain + const colonyBalance = await ethersForeignProvider.getBalance(proxyColony.address); + const recipientBalanceAfter = await ethersForeignProvider.getBalance(accounts[0]); + + expect(colonyBalance.toHexString()).to.equal(ethers.utils.parseEther("0.7").toHexString()); + expect(recipientBalanceAfter.sub(receipientBalanceBefore).toHexString()).to.equal(ethers.utils.parseEther("0.3").toHexString()); + }); }); describe("making arbitrary transactions on another chain", async () => { @@ -1324,7 +1413,6 @@ contract("Cross-chain", (accounts) => { colony = await setupColony(homeColonyNetwork); const events = await homeColonyNetwork.queryFilter(homeColonyNetwork.filters.ColonyAdded()); - // homeColonyNetwork.fil // Deploy a proxy colony on the foreign network const colonyCreationSalt = await homeColonyNetwork.getColonyCreationSalt({ blockTag: events[events.length - 1].blockNumber }); @@ -1355,7 +1443,6 @@ contract("Cross-chain", (accounts) => { await p; const balanceAfter = await foreignToken.balanceOf(proxyColony.address); - console.log(balanceBefore.toHexString(), balanceAfter.toHexString()); expect(balanceAfter.sub(balanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); }); @@ -1373,7 +1460,6 @@ contract("Cross-chain", (accounts) => { await p; const shellBalanceAfter = await foreignToken.balanceOf(proxyColony.address); - console.log(shellBalanceBefore.toHexString(), shellBalanceAfter.toHexString()); expect(shellBalanceAfter.sub(shellBalanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); // Check that the second transaction was successful @@ -1669,4 +1755,34 @@ contract("Cross-chain", (accounts) => { await homeBridge.setVerifyVMResult(true, ""); }); }); + + describe("ProxyColony functions are secure", async () => { + let colony; + let proxyColony; + beforeEach(async () => { + colony = await setupColony(homeColonyNetwork); + + const events = await homeColonyNetwork.queryFilter(homeColonyNetwork.filters.ColonyAdded()); + // Deploy a proxy colony on the foreign network + const colonyCreationSalt = await homeColonyNetwork.getColonyCreationSalt({ blockTag: events[events.length - 1].blockNumber }); + + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + + const tx = await colony.createProxyColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); + await tx.wait(); + + await p; + proxyColony = new ethers.Contract(colony.address, ProxyColony.abi, ethersForeignSigner); + }); + + it("a non-bridge address cannot call transferFromBridge", async () => { + const tx = await proxyColony.transferFromBridge(ADDRESS_ZERO, ADDRESS_ZERO, 0, { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "colony-only-bridge"); + }); + + it("a non-bridge address cannot call makeArbitraryTransactions", async () => { + const tx = await proxyColony.makeArbitraryTransactions([], [], { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "colony-only-bridge"); + }); + }); }); From c5225bb7eee581268541ff6b4bae7e9f32674f1b Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 2 Sep 2024 17:04:05 +0100 Subject: [PATCH 25/72] Continue tidying up proxy contracts --- contracts/bridging/ProxyColony.sol | 14 +-- contracts/bridging/ProxyColonyNetwork.sol | 7 +- contracts/common/CallWithGuards.sol | 17 ++++ packages/wormhole-relayer/index.ts | 5 - scripts/setup-bridging-contracts.js | 54 +++++----- test/cross-chain/cross-chain.js | 114 ++++++++++++++++------ 6 files changed, 137 insertions(+), 74 deletions(-) diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index 3e6ebd3d2a..c148c93d8d 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -19,7 +19,6 @@ pragma solidity 0.8.27; pragma experimental ABIEncoderV2; -import { BasicMetaTransaction } from "./../common/BasicMetaTransaction.sol"; import { CallWithGuards } from "../common/CallWithGuards.sol"; import { DSAuth } from "./../../lib/dappsys/auth.sol"; import { ERC20Extended } from "./../common/ERC20Extended.sol"; @@ -27,7 +26,7 @@ import { Multicall } from "./../common/Multicall.sol"; import { IColonyNetwork } from "./../colonyNetwork/IColonyNetwork.sol"; import { ProxyColonyNetwork } from "./ProxyColonyNetwork.sol"; -contract ProxyColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards { +contract ProxyColony is DSAuth, Multicall, CallWithGuards { // Address of the Resolver contract used by EtherRouter for lookups and routing address resolver; // Storage slot 2 (from DSAuth there is authority and owner at storage slots 0 and 1 respectively) @@ -36,14 +35,6 @@ contract ProxyColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards // Token address => known balance mapping(address => uint256) tokenBalances; - function getMetatransactionNonce(address _user) public view override returns (uint256 _nonce) { - return metatransactionNonces[_user]; - } - - function incrementMetatransactionNonce(address _user) internal override { - metatransactionNonces[_user] += 1; - } - // Events modifier onlyColonyBridge() { @@ -105,7 +96,8 @@ contract ProxyColony is DSAuth, BasicMetaTransaction, Multicall, CallWithGuards require(_targets[i] != bridgeAddress, "colony-cannot-target-bridge"); require(_targets[i] != owner, "colony-cannot-target-network"); - (bool success, ) = _targets[i].call(_payloads[i]); + (bool success, ) = callWithGuards(_targets[i], _payloads[i]); + require(success, "colony-arbitrary-transaction-failed"); } } diff --git a/contracts/bridging/ProxyColonyNetwork.sol b/contracts/bridging/ProxyColonyNetwork.sol index cef56b4d60..dcffaf506e 100644 --- a/contracts/bridging/ProxyColonyNetwork.sol +++ b/contracts/bridging/ProxyColonyNetwork.sol @@ -44,13 +44,8 @@ contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards { /// @param bridgeAddress The address of the bridge contract that will be interacted with event BridgeSet(address bridgeAddress); - modifier self() { - require(address(this) == msgSender(), "colony-not-self"); - _; - } - modifier onlyColony() { - require(shellColonies[msgSender()], "colony-network-caller-must-be-shell-colony"); + require(shellColonies[msgSender()], "colony-network-caller-must-be-proxy-colony"); _; } diff --git a/contracts/common/CallWithGuards.sol b/contracts/common/CallWithGuards.sol index efdf5269ff..9525373760 100644 --- a/contracts/common/CallWithGuards.sol +++ b/contracts/common/CallWithGuards.sol @@ -1,4 +1,21 @@ pragma solidity 0.8.28; +// SPDX-License-Identifier: GPL-3.0-or-later +/* + This file is part of The Colony Network. + + The Colony Network is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The Colony Network is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with The Colony Network. If not, see . +*/ contract CallWithGuards { function isContract(address addr) internal view returns (bool) { diff --git a/packages/wormhole-relayer/index.ts b/packages/wormhole-relayer/index.ts index 10e3940ecd..15eddc05ac 100644 --- a/packages/wormhole-relayer/index.ts +++ b/packages/wormhole-relayer/index.ts @@ -191,7 +191,6 @@ const loader = new TruffleLoader({ colonyBridges[chainId] = new ethers.Contract(colonyBridgeAddress, colonyBridgeContractDef.abi, nonceManager); const networkDetails = await wallet.provider.getNetwork(); - console.log('networkDetails', networkDetails) if (networkDetails.chainId !== config.chains[chainId].evmChainId) { console.log('Network details do not match config for chain', chainId); console.log('Got an evmChainId of', networkDetails.chainId, 'but expected', config.chains[chainId].evmChainId); @@ -311,10 +310,6 @@ const loader = new TruffleLoader({ } const hash = ctx.sourceTxHash; const [destinationEvmChainId, destinationAddress, payload] = (new ethers.utils.AbiCoder).decode(['uint256', 'address', 'bytes'],`0x${vaa.payload.toString('hex')}`); - console.log('destinationEvmChainId', destinationEvmChainId); - console.log('destinationAddress', destinationAddress); - console.log('payload', payload); - console.log( `Got a VAA with sequence: ${vaa.sequence} from with txhash: ${hash}`, diff --git a/scripts/setup-bridging-contracts.js b/scripts/setup-bridging-contracts.js index 77500183c7..4df9626931 100644 --- a/scripts/setup-bridging-contracts.js +++ b/scripts/setup-bridging-contracts.js @@ -51,9 +51,9 @@ async function setupBridging(homeRpcUrl, foreignRpcUrls) { const Token = await loader.load({ contractDir, contractName: "Token" }); const foreignBridgeAddresses = []; - const foreignColonyBridgeAddresses = []; + const remoteColonyBridgeAddresses = []; const foreignBridges = []; - const foreignColonyBridges = []; + const remoteColonyBridges = []; const gnosisSafes = []; const zodiacBridges = []; @@ -133,15 +133,15 @@ async function setupBridging(homeRpcUrl, foreignRpcUrls) { } // Deploy a foreign bridge - const [foreignBridge, foreignColonyBridge] = await deployBridge(ethersForeignSigner); + const [foreignBridge, remoteColonyBridge] = await deployBridge(ethersForeignSigner); const foreignChainId = (await ethersForeignSigner.provider.getNetwork()).chainId; foreignBridgeAddresses.push(foreignBridge.address); - foreignColonyBridgeAddresses.push(foreignColonyBridge.address); + remoteColonyBridgeAddresses.push(remoteColonyBridge.address); console.log(`On chain ${foreignChainId}:`); console.log(`foreign bridge address: ${foreignBridge.address}`); - console.log(`foreign colony bridge address: ${foreignColonyBridge.address}`); + console.log(`foreign colony bridge address: ${remoteColonyBridge.address}`); console.log(`foreign rpc url: ${foreignRpcUrls[0]}`); console.log(`gnosis safe address: ${gnosisSafe.address}`); console.log(`zodiac bridge module address: ${zodiacBridge.address}`); @@ -149,7 +149,7 @@ async function setupBridging(homeRpcUrl, foreignRpcUrls) { console.log(`token address: ${token.address}`); foreignBridges.push(foreignBridge); - foreignColonyBridges.push(foreignColonyBridge); + remoteColonyBridges.push(remoteColonyBridge); gnosisSafes.push(gnosisSafe); zodiacBridges.push(zodiacBridge); } @@ -166,7 +166,7 @@ async function setupBridging(homeRpcUrl, foreignRpcUrls) { homeBridge.address, foreignBridgeAddresses, homeColonyBridge.address, - foreignColonyBridgeAddresses, + remoteColonyBridgeAddresses, ); // eslint-disable-line no-unused-vars // TODO: Start the bridge monitor @@ -190,7 +190,7 @@ async function setupBridging(homeRpcUrl, foreignRpcUrls) { (foreignRpcUrl, index) => ` [${foreignWormholeChainIds[index]}]: { endpoints: ["${foreignRpcUrl}"], - colonyBridgeAddress: "${foreignColonyBridgeAddresses[index]}", + colonyBridgeAddress: "${remoteColonyBridgeAddresses[index]}", payForGas: ${index % 2 === 0}, evmChainId: ${foreignChainIds[index]} },`, @@ -236,31 +236,41 @@ async function setupBridging(homeRpcUrl, foreignRpcUrls) { console.log(`Home bridge address: ${homeBridge.address}`); console.log(`Foreign bridge addresses: ${foreignBridgeAddresses.join(", ")}`); console.log(`Home colony bridge addresses: ${homeColonyBridge.address}`); - console.log(`Foreign colony bridge addresses: ${foreignColonyBridgeAddresses.join(", ")}`); + console.log(`Foreign colony bridge addresses: ${remoteColonyBridgeAddresses.join(", ")}`); // console.log(`Gnosis Safe addresses: ${gnosisSafe.address}`); // console.log(`Zodiac Bridge module addresses: ${zodiacBridge.address}`); // console.log(`ERC721 addresses: ${erc721.address}`); // console.log(`Token addresses: ${token.address}`); for (let i = 0; i < ethersForeignSigners.length; i += 1) { - await setForeignBridgeData(homeColonyBridge.address, foreignColonyBridgeAddresses[i], ethersHomeSigner, ethersForeignSigners[i]); - await setHomeBridgeData(homeColonyBridge.address, foreignColonyBridgeAddresses[i], ethersHomeSigner, ethersForeignSigners[i]); + await setForeignBridgeData(homeColonyBridge.address, remoteColonyBridgeAddresses[i], ethersHomeSigner, ethersForeignSigners[i]); + await setHomeBridgeData(homeColonyBridge.address, remoteColonyBridgeAddresses[i], ethersHomeSigner, ethersForeignSigners[i]); } - return { gnosisSafe, resetRelayer, guardianSpy, zodiacBridge, homeBridge, foreignBridge, homeColonyBridge, foreignColonyBridge }; + // TODO: Return all, and handle appropriately where this is called. + return { + gnosisSafe: gnosisSafes[0], + resetRelayer, + guardianSpy, + zodiacBridge: zodiacBridges[0], + homeBridge, + foreignBridge: foreignBridges[0], + homeColonyBridge, + remoteColonyBridge: remoteColonyBridges[0], + }; } -async function setForeignBridgeData(homeColonyBridgeAddress, foreignColonyBridgeAddress, ethersHomeSigner, ethersForeignSigner) { +async function setForeignBridgeData(homeColonyBridgeAddress, remoteColonyBridgeAddress, ethersHomeSigner, ethersForeignSigner) { const contractDir = path.resolve(__dirname, "..", "artifacts", "contracts", "bridging"); const WormholeBridgeForColony = await loader.load({ contractDir, contractName: "WormholeBridgeForColony" }); const ProxyColonyNetwork = await loader.load({ contractDir, contractName: "ProxyColonyNetwork" }); - const bridge = new ethers.Contract(foreignColonyBridgeAddress, WormholeBridgeForColony.abi, ethersForeignSigner); + const bridge = new ethers.Contract(remoteColonyBridgeAddress, WormholeBridgeForColony.abi, ethersForeignSigner); const homeChainId = (await ethersHomeSigner.provider.getNetwork()).chainId; const foreignChainId = (await ethersForeignSigner.provider.getNetwork()).chainId; - let tx = await bridge.setColonyBridgeAddress(foreignChainId, foreignColonyBridgeAddress); + let tx = await bridge.setColonyBridgeAddress(foreignChainId, remoteColonyBridgeAddress); await tx.wait(); tx = await bridge.setColonyBridgeAddress(homeChainId, homeColonyBridgeAddress); await tx.wait(); @@ -269,19 +279,17 @@ async function setForeignBridgeData(homeColonyBridgeAddress, foreignColonyBridge await tx.wait(); // TODO: Figure out a better way of setting / controlling this? - console.log("setting foreign colony bridge address", foreignColonyBridgeAddress); + console.log("setting foreign colony bridge address", remoteColonyBridgeAddress); - console.log(ethersForeignSigner); - // process.exit(1); - const foreignColonyNetwork = new ethers.Contract(NETWORK_ADDRESS, ProxyColonyNetwork.abi, ethersForeignSigner); - tx = await foreignColonyNetwork.setColonyBridgeAddress(foreignColonyBridgeAddress); + const remoteColonyNetwork = new ethers.Contract(NETWORK_ADDRESS, ProxyColonyNetwork.abi, ethersForeignSigner); + tx = await remoteColonyNetwork.setColonyBridgeAddress(remoteColonyBridgeAddress); await tx.wait(); - tx = await foreignColonyNetwork.setHomeChainId(homeChainId); + tx = await remoteColonyNetwork.setHomeChainId(homeChainId); await tx.wait(); } -async function setHomeBridgeData(homeColonyBridgeAddress, foreignColonyBridgeAddress, ethersHomeSigner, ethersForeignSigner) { +async function setHomeBridgeData(homeColonyBridgeAddress, remoteColonyBridgeAddress, ethersHomeSigner, ethersForeignSigner) { let contractDir = path.resolve(__dirname, "..", "artifacts", "contracts", "bridging"); const WormholeBridgeForColony = await loader.load({ contractDir, contractName: "WormholeBridgeForColony" }); @@ -292,7 +300,7 @@ async function setHomeBridgeData(homeColonyBridgeAddress, foreignColonyBridgeAdd const homeChainId = (await ethersHomeSigner.provider.getNetwork()).chainId; const foreignChainId = (await ethersForeignSigner.provider.getNetwork()).chainId; - let tx = await bridge.setColonyBridgeAddress(foreignChainId, foreignColonyBridgeAddress); + let tx = await bridge.setColonyBridgeAddress(foreignChainId, remoteColonyBridgeAddress); await tx.wait(); tx = await bridge.setColonyBridgeAddress(homeChainId, homeColonyBridgeAddress); await tx.wait(); diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index fdf33b93bd..37dac36333 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -40,6 +40,7 @@ const { CURR_VERSION, CREATEX_ADDRESS, NETWORK_ADDRESS, + HASHZERO, } = require("../../helpers/constants"); const { forwardTime, checkErrorRevertEthers, revert, snapshot, evmChainIdToWormholeChainId } = require("../../helpers/test-helper"); const ReputationMinerTestWrapper = require("../../packages/reputation-miner/test/ReputationMinerTestWrapper"); @@ -54,11 +55,11 @@ const contractLoader = new TruffleLoader({ contract("Cross-chain", (accounts) => { let homeColony; let homeColonyNetwork; - let foreignColonyNetwork; + let remoteColonyNetwork; let homeBridge; let foreignBridge; let homeColonyBridge; - let foreignColonyBridge; + let remoteColonyBridge; let gnosisSafe; let zodiacBridge; let guardianSpy; @@ -116,12 +117,11 @@ contract("Cross-chain", (accounts) => { } await exec(`PORT=${FOREIGN_PORT} bash ./scripts/setup-foreign-chain.sh`); - ({ guardianSpy, resetRelayer, gnosisSafe, zodiacBridge, homeBridge, foreignBridge, foreignColonyBridge, homeColonyBridge } = await setupBridging( + ({ guardianSpy, resetRelayer, gnosisSafe, zodiacBridge, homeBridge, foreignBridge, remoteColonyBridge, homeColonyBridge } = await setupBridging( homeRpcUrl, - foreignRpcUrl, + [foreignRpcUrl], )); - console.log("asdf"); // Add bridge to the foreign colony network // const homeNetworkId = await ethersHomeSigner.provider.send("net_version", []); // Due to limitations, for local testing, our wormhole chainIDs have to be 'real' wormhole chainids. @@ -196,7 +196,7 @@ contract("Cross-chain", (accounts) => { homeColonyNetwork = await new ethers.Contract(NETWORK_ADDRESS, IColonyNetwork.abi, ethersHomeSigner); // const foreignEtherRouterAddress = homeEtherRouterAddress; - foreignColonyNetwork = await new ethers.Contract(NETWORK_ADDRESS, ProxyColonyNetwork.abi, ethersForeignSigner); + remoteColonyNetwork = await new ethers.Contract(NETWORK_ADDRESS, ProxyColonyNetwork.abi, ethersForeignSigner); }); beforeEach(async () => { @@ -218,15 +218,15 @@ contract("Cross-chain", (accounts) => { const homeMCAddress = await homeColonyNetwork.getMetaColony(); homeMetacolony = await new ethers.Contract(homeMCAddress, IMetaColony.abi, ethersHomeSigner); - await setForeignBridgeData(homeColonyBridge.address, foreignColonyBridge.address, ethersHomeSigner, ethersForeignSigner); - await setHomeBridgeData(homeColonyBridge.address, foreignColonyBridge.address, ethersHomeSigner, ethersForeignSigner); + await setForeignBridgeData(homeColonyBridge.address, remoteColonyBridge.address, ethersHomeSigner, ethersForeignSigner); + await setHomeBridgeData(homeColonyBridge.address, remoteColonyBridge.address, ethersHomeSigner, ethersForeignSigner); // Bridge over skills that have been created on the foreign chain - // const latestSkillId = await foreignColonyNetwork.getSkillCount(); + // const latestSkillId = await remoteColonyNetwork.getSkillCount(); // const alreadyBridged = await homeColonyNetwork.getBridgedSkillCounts(foreignChainId); // for (let i = alreadyBridged.add(1); i <= latestSkillId; i = i.add(1)) { // const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); - // tx = await foreignColonyNetwork.bridgeSkillIfNotMiningChain(i); + // tx = await remoteColonyNetwork.bridgeSkillIfNotMiningChain(i); // await tx.wait(); // await p; // } @@ -257,7 +257,7 @@ contract("Cross-chain", (accounts) => { homeColony = await setupColony(homeColonyNetwork); // const p = bridgeMonitor.getPromiseForNextBridgedTransaction(2); - // foreignColony = await setupColony(foreignColonyNetwork); + // remoteColony = await setupColony(remoteColonyNetwork); // await p; }); @@ -289,10 +289,10 @@ contract("Cross-chain", (accounts) => { describe("administrating cross-network bridges", async () => { it("colonyNetwork should have the same address on each chain", async () => { - expect(homeColonyNetwork.address).to.equal(foreignColonyNetwork.address); + expect(homeColonyNetwork.address).to.equal(remoteColonyNetwork.address); // Check we have colony Network there - this equality is expected because of how we set up the addresses const homeVersionResolver = await homeColonyNetwork.getColonyVersionResolver(CURR_VERSION); - const proxyColonyResolver = await foreignColonyNetwork.proxyColonyResolverAddress(); + const proxyColonyResolver = await remoteColonyNetwork.proxyColonyResolverAddress(); expect(homeVersionResolver).to.not.equal(ADDRESS_ZERO); expect(proxyColonyResolver).to.not.equal(ADDRESS_ZERO); @@ -340,7 +340,7 @@ contract("Cross-chain", (accounts) => { const colonyAsEtherRouter = new ethers.Contract(deployedColony.address, EtherRouter.abi, ethersForeignSigner); const resolverAddress = await colonyAsEtherRouter.resolver(); - const expectedResolver = await foreignColonyNetwork.proxyColonyResolverAddress(); + const expectedResolver = await remoteColonyNetwork.proxyColonyResolverAddress(); expect(resolverAddress).to.equal(expectedResolver); }); @@ -352,19 +352,19 @@ contract("Cross-chain", (accounts) => { const networkAddress = await homeColonyBridge.colonyNetwork(); expect(networkAddress).to.equal(homeColonyNetwork.address); - const foreignColonyBridgeAddress = await homeColonyBridge.getColonyBridgeAddress(foreignChainId); - expect(foreignColonyBridgeAddress).to.equal(foreignColonyBridge.address); + const remoteColonyBridgeAddress = await homeColonyBridge.getColonyBridgeAddress(foreignChainId); + expect(remoteColonyBridgeAddress).to.equal(remoteColonyBridge.address); }); it("setColonyBridgeAddress on proxy Network can be called directly by the owner (and not a random address)", async () => { - const owner = await foreignColonyNetwork.owner(); - expect(await foreignColonyNetwork.signer.getAddress()).to.equal(owner); - let tx = await foreignColonyNetwork.setColonyBridgeAddress(foreignColonyBridge.address, { gasLimit: 1000000 }); + const owner = await remoteColonyNetwork.owner(); + expect(await remoteColonyNetwork.signer.getAddress()).to.equal(owner); + let tx = await remoteColonyNetwork.setColonyBridgeAddress(remoteColonyBridge.address, { gasLimit: 1000000 }); await tx.wait(); - const foreignColonyNetwork2 = new ethers.Contract(foreignColonyNetwork.address, IColonyNetwork.abi, ethersForeignSigner2); - expect(await foreignColonyNetwork2.signer.getAddress()).to.not.equal(owner); - tx = await foreignColonyNetwork2.setColonyBridgeAddress(foreignColonyBridge.address, { gasLimit: 1000000, from: accounts[1] }); + const remoteColonyNetwork2 = new ethers.Contract(remoteColonyNetwork.address, IColonyNetwork.abi, ethersForeignSigner2); + expect(await remoteColonyNetwork2.signer.getAddress()).to.not.equal(owner); + tx = await remoteColonyNetwork2.setColonyBridgeAddress(remoteColonyBridge.address, { gasLimit: 1000000, from: accounts[1] }); await checkErrorRevertEthers(tx.wait(), "colony-network-caller-must-be-owner-or-bridge"); }); @@ -374,7 +374,7 @@ contract("Cross-chain", (accounts) => { }); it("callProxyNetwork can only be called by root permissions on the metacolony", async () => { - const payload = foreignColonyNetwork.interface.encodeFunctionData("setColonyBridgeAddress", [ADDRESS_ZERO]); + const payload = remoteColonyNetwork.interface.encodeFunctionData("setColonyBridgeAddress", [ADDRESS_ZERO]); const homeMetacolony2 = new ethers.Contract(homeMetacolony.address, IMetaColony.abi, ethersHomeSigner2); let tx = await homeMetacolony2.callProxyNetwork(foreignChainId, [payload], { gasLimit: 1000000 }); await checkErrorRevertEthers(tx.wait(), "ds-auth-unauthorized"); @@ -397,7 +397,7 @@ contract("Cross-chain", (accounts) => { await p; // Check call was successful - const bridgeAddressAfter = await foreignColonyNetwork.colonyBridgeAddress(); + const bridgeAddressAfter = await remoteColonyNetwork.colonyBridgeAddress(); expect(bridgeAddressAfter).to.equal(ADDRESS_ZERO); // Reset permissions @@ -406,13 +406,13 @@ contract("Cross-chain", (accounts) => { }); it("setColonyBridgeAddress on Proxy Network can be used across the bridge", async () => { - const bridgeAddress = await foreignColonyNetwork.colonyBridgeAddress(); - const payload = foreignColonyNetwork.interface.encodeFunctionData("setColonyBridgeAddress", [ADDRESS_ZERO]); + const bridgeAddress = await remoteColonyNetwork.colonyBridgeAddress(); + const payload = remoteColonyNetwork.interface.encodeFunctionData("setColonyBridgeAddress", [ADDRESS_ZERO]); const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); const tx = await homeMetacolony.callProxyNetwork(foreignChainId, [payload]); await tx.wait(); await p; - const bridgeAddressAfter = await foreignColonyNetwork.colonyBridgeAddress(); + const bridgeAddressAfter = await remoteColonyNetwork.colonyBridgeAddress(); expect(bridgeAddressAfter).to.not.equal(bridgeAddress); }); @@ -1238,6 +1238,27 @@ contract("Cross-chain", (accounts) => { expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); }); + it("Can claim tokens received on foreign chain via cross-chain request", async () => { + const tokenAmount = ethers.utils.parseEther("100"); + + let tx = await foreignToken["mint(address,uint256)"](proxyColony.address, tokenAmount); + await tx.wait(); + + // Claim on the foreign chain + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(2); + // One bridged transaction will be the request across, one will be reporting what was claimed back + + const payload = proxyColony.interface.encodeFunctionData("claimTokens", [foreignToken.address]); + tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [proxyColony.address], [payload]); + await tx.wait(); + await p; + + // Check bookkeeping on the home chain + + const balance = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); + }); + it("Can track native tokens received on foreign chains", async () => { const tokenAmount = ethers.utils.parseEther("1"); @@ -1471,7 +1492,7 @@ contract("Cross-chain", (accounts) => { describe("bridge functions are secure", async () => { it("only the configured colonyNetwork can call `sendMessage`", async () => { - const tx = await foreignColonyBridge.sendMessage(1, ADDRESS_ZERO, "0x00000000", { gasLimit: 1000000 }); + const tx = await remoteColonyBridge.sendMessage(1, ADDRESS_ZERO, "0x00000000", { gasLimit: 1000000 }); await checkErrorRevertEthers(tx.wait(), "wormhole-bridge-only-colony-network"); }); @@ -1746,7 +1767,7 @@ contract("Cross-chain", (accounts) => { homeColonyBridge.address, 0, 0, - foreignColonyNetwork.interface.encodeFunctionData("setProxyColonyResolverAddress", [ADDRESS_ZERO]), + remoteColonyNetwork.interface.encodeFunctionData("setProxyColonyResolverAddress", [ADDRESS_ZERO]), 100, wormholeHomeChainId, ); @@ -1785,4 +1806,39 @@ contract("Cross-chain", (accounts) => { await checkErrorRevertEthers(tx.wait(), "colony-only-bridge"); }); }); + + describe("ProxyColonyNetwork functions are secure", async () => { + let proxyColonyNetwork2; + before(async () => { + proxyColonyNetwork2 = await new ethers.Contract(NETWORK_ADDRESS, ProxyColonyNetwork.abi, ethersForeignSigner2); + }); + + it("only authed accounts can call setProxyColonyResolverAddress", async () => { + let tx = await proxyColonyNetwork2.setProxyColonyResolverAddress(ADDRESS_ZERO, { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "ds-auth-unauthorized"); + + await remoteColonyNetwork.setOwner(accounts[1]); + tx = await proxyColonyNetwork2.setProxyColonyResolverAddress(ADDRESS_ZERO, { gasLimit: 1000000 }); + await tx.wait(); + }); + + it("a non-authed account cannot call setHomeChainId", async () => { + let tx = await proxyColonyNetwork2.setHomeChainId(1, { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "ds-auth-unauthorized"); + await remoteColonyNetwork.setOwner(accounts[1]); + + tx = await proxyColonyNetwork2.setHomeChainId(1, { gasLimit: 1000000 }); + await tx.wait(); + }); + + it("a non-bridge address cannot call createProxyColonyFromBridge", async () => { + const tx = await remoteColonyNetwork.createProxyColonyFromBridge(HASHZERO, { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "colony-network-caller-must-be-colony-bridge"); + }); + + it("a non-proxy-colony address cannot call bridgeMessage", async () => { + const tx = await remoteColonyNetwork.bridgeMessage(HASHZERO, { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "colony-network-caller-must-be-proxy-colony"); + }); + }); }); From e96d641f2fc5915f50bc68ed985561113afcf661 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Tue, 3 Sep 2024 12:43:54 +0100 Subject: [PATCH 26/72] More proxy-colony permission tests --- helpers/test-helper.js | 3 ++ test/cross-chain/cross-chain.js | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/helpers/test-helper.js b/helpers/test-helper.js index ea1ffcb554..b40e975378 100644 --- a/helpers/test-helper.js +++ b/helpers/test-helper.js @@ -244,6 +244,9 @@ exports.checkErrorRevertEthers = async function checkErrorRevertEthers(promise, let receipt; try { receipt = await promise; + if (receipt.status === 0) { + throw receipt; + } } catch (err) { const txid = err.transactionHash; diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 37dac36333..e99fe7fbe1 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -1488,6 +1488,36 @@ contract("Cross-chain", (accounts) => { const colonyBalanceAfter = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); expect(colonyBalanceAfter.sub(colonyBalanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); }); + + it("invalid cross-chain arbitrary transactions are rejected", async () => { + let p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + + const tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], ["0x00000000", "0x00000000"]); + await tx.wait(); + + await checkErrorRevertEthers(p, "colony-targets-and-payloads-length-mismatch"); + + // Check can't target Network + p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const tx2 = await colony.makeProxyArbitraryTransactions(foreignChainId, [remoteColonyNetwork.address], ["0x00000000"]); + await tx2.wait(); + + await checkErrorRevertEthers(p, "colony-cannot-target-network"); + + // Check can't target the bridge + p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const tx3 = await colony.makeProxyArbitraryTransactions(foreignChainId, [remoteColonyBridge.address], ["0x00000000"]); + await tx3.wait(); + + await checkErrorRevertEthers(p, "colony-cannot-target-bridge"); + + // Otherwise valid transaction, it just fails + p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const tx4 = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], ["0x00000000"]); + await tx4.wait(); + + await checkErrorRevertEthers(p, "colony-arbitrary-transaction-failed"); + }); }); describe("bridge functions are secure", async () => { @@ -1841,4 +1871,41 @@ contract("Cross-chain", (accounts) => { await checkErrorRevertEthers(tx.wait(), "colony-network-caller-must-be-proxy-colony"); }); }); + + describe("Invalid interactions with bridging system are handled appropriately", async () => { + it("Can't bridge to a chain that's not supported", async () => { + const tx = await homeColony.makeProxyArbitraryTransactions(111, [ADDRESS_ZERO], ["0x00000000"], { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "colony-bridge-not-known-chain"); + }); + + it("Valid VAAs that aren't from a colony bridge are rejected", async () => { + const vaa = await bridgeMonitor.encodeMockVAA( + homeColonyBridge.address, + 0, + 0, + remoteColonyNetwork.interface.encodeFunctionData("setProxyColonyResolverAddress", [ADDRESS_ZERO]), + 100, + 1, + ); + const tx = await remoteColonyBridge.receiveMessage(vaa, { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "colony-bridge-bridged-tx-only-from-colony-bridge"); + }); + + it("Valid VAAs that aren't for the right chain are rejected", async () => { + const vaa = await bridgeMonitor.encodeMockVAA( + homeColonyBridge.address, + 0, + 0, + new ethers.utils.AbiCoder().encode( + ["uint256", "address", "bytes"], + [7777, ADDRESS_ZERO, remoteColonyNetwork.interface.encodeFunctionData("setProxyColonyResolverAddress", [ADDRESS_ZERO])], + ), + 100, + wormholeHomeChainId, + ); + const tx = await remoteColonyBridge.receiveMessage(vaa, { gasLimit: 1000000 }); + // await tx.wait(); + await checkErrorRevertEthers(tx.wait(), "colony-bridge-destination-chain-id-mismatch"); + }); + }); }); From dc1b4c201cadcb111008cfcb6d18c7e530a6e467 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Tue, 3 Sep 2024 15:41:28 +0100 Subject: [PATCH 27/72] Continue removing code from old cross-chain system --- .../colonyNetwork/ColonyNetworkSkills.sol | 114 ++---------------- .../colonyNetwork/ColonyNetworkStorage.sol | 29 ----- 2 files changed, 11 insertions(+), 132 deletions(-) diff --git a/contracts/colonyNetwork/ColonyNetworkSkills.sol b/contracts/colonyNetwork/ColonyNetworkSkills.sol index 0a03ee1d9d..8af7834855 100644 --- a/contracts/colonyNetwork/ColonyNetworkSkills.sol +++ b/contracts/colonyNetwork/ColonyNetworkSkills.sol @@ -58,11 +58,17 @@ contract ColonyNetworkSkills is ColonyNetworkStorage, Multicall { return; } - if (isMiningChain()) { - appendReputationUpdateLogInternal(_user, _amount, _skillId, msgSender()); - } else { - bridgeReputationUpdateLog(_user, _amount, _skillId); - } + uint128 nParents = skills[_skillId].nParents; + // We only update child skill reputation if the update is negative, otherwise just set nChildren to 0 to save gas + uint128 nChildren = (_amount < 0) ? skills[_skillId].nChildren : 0; + IReputationMiningCycle(inactiveReputationMiningCycle).appendReputationUpdateLog( + _user, + _amount, + _skillId, + msgSender(), + nParents, + nChildren + ); } // Bridging (sending) @@ -109,14 +115,6 @@ contract ColonyNetworkSkills is ColonyNetworkStorage, Multicall { // Internal function addSkillToChainTree(uint256 _parentSkillId, uint256 _skillId) private { - // This indicates a new root local skill bridged from another chain, i.e. 0x{chainId}{0} - // We don't do anything to the tree in this scenario, other than incrementing the skill count, - // which should be/is done where this function is called. - // (this mirrors the behaviour of not calling addSkill() in initialiseRootLocalSkill) - if (_parentSkillId != 0 && _parentSkillId << 128 == 0) { - return; - } - require(_parentSkillId > 0, "colony-network-invalid-parent-skill"); Skill storage parentSkill = skills[_parentSkillId]; @@ -178,94 +176,4 @@ contract ColonyNetworkSkills is ColonyNetworkStorage, Multicall { } } } - - function appendReputationUpdateLogInternal( - address _user, - int256 _amount, - uint256 _skillId, - address _colony - ) internal { - uint128 nParents = skills[_skillId].nParents; - // We only update child skill reputation if the update is negative, otherwise just set nChildren to 0 to save gas - uint128 nChildren = (_amount < 0) ? skills[_skillId].nChildren : 0; - IReputationMiningCycle(inactiveReputationMiningCycle).appendReputationUpdateLog( - _user, - _amount, - _skillId, - _colony, - nParents, - nChildren - ); - } - - function bridgeReputationUpdateLog(address _user, int256 _amount, uint256 _skillId) internal { - // TODO: Maybe force to be set on deployment? - require(colonyBridgeAddress != address(0x0), "colony-network-foreign-bridge-not-set"); - address colonyAddress = msgSender(); - reputationUpdateCount[block.chainid][colonyAddress] += 1; - // Build the transaction we're going to send to the bridge - bytes memory payload = abi.encodeWithSignature( - "addReputationUpdateLogFromBridge(address,address,int256,uint256,uint256)", - colonyAddress, - _user, - _amount, - _skillId, - reputationUpdateCount[block.chainid][colonyAddress] - ); - - bool success = callThroughBridgeWithGuards(payload); - - if (success) { - emit ReputationUpdateSentToBridge( - colonyAddress, - reputationUpdateCount[block.chainid][colonyAddress] - ); - return; - } - - // Store to resend later - PendingReputationUpdate memory pendingReputationUpdate = PendingReputationUpdate( - _user, - _amount, - _skillId, - msgSender(), - block.timestamp - ); - pendingReputationUpdates[block.chainid][colonyAddress][ - reputationUpdateCount[block.chainid][colonyAddress] - ] = pendingReputationUpdate; - - emit ReputationUpdateStored(colonyAddress, reputationUpdateCount[block.chainid][colonyAddress]); - } - - // Mining cycle decay constants - // Note that these values and the mining window size (defined in ReputationMiningCycleCommon) - // need to be consistent with each other, but are not checked, in order for the decay - // rate to be as-expected. - int256 constant DECAY_NUMERATOR = 999679150010889; // 1-hr mining cycle - int256 constant DECAY_DENOMINATOR = 1000000000000000; - uint256 constant DECAY_PERIOD = 1 hours; - - function decayReputation( - int256 _reputation, - uint256 _since - ) internal view returns (int256 decayedReputation) { - uint256 decayEpochs = (block.timestamp - _since) / DECAY_PERIOD; - int256 adjustedNumerator = DECAY_NUMERATOR; - - // This algorithm successively doubles the decay factor while halving the number of epochs - // This allows us to perform the decay in O(log(n)) time - // For example, a decay of 50 epochs would be applied as (k**2)(k**16)(k**32) - while (decayEpochs > 0) { - // slither-disable-next-line weak-prng - if (decayEpochs % 2 >= 1) { - // slither-disable-next-line divide-before-multiply - _reputation = (_reputation * adjustedNumerator) / DECAY_DENOMINATOR; - } - // slither-disable-next-line divide-before-multiply - adjustedNumerator = (adjustedNumerator * adjustedNumerator) / DECAY_DENOMINATOR; - decayEpochs >>= 1; - } - return _reputation; - } } diff --git a/contracts/colonyNetwork/ColonyNetworkStorage.sol b/contracts/colonyNetwork/ColonyNetworkStorage.sol index 3e604965de..14ecdebefe 100644 --- a/contracts/colonyNetwork/ColonyNetworkStorage.sol +++ b/contracts/colonyNetwork/ColonyNetworkStorage.sol @@ -167,11 +167,6 @@ contract ColonyNetworkStorage is _; } - modifier onlyColonyBridge() { - require(msgSender() == colonyBridgeAddress, "colony-network-caller-must-be-colony-bridge"); - _; - } - modifier miningInitialised() { require( inactiveReputationMiningCycle != address(0x0), @@ -206,28 +201,4 @@ contract ColonyNetworkStorage is function getMiningChainId() public view returns (uint256) { return reputationMiningChainId; } - - function getAndCacheReputationMiningChainId() internal returns (uint256) { - if (reputationMiningChainId == 0 && isXdai()) { - reputationMiningChainId = block.chainid; - } - return reputationMiningChainId; - } - - function callThroughBridgeWithGuards(bytes memory payload) internal returns (bool) { - bytes memory bridgePayload = abi.encodeWithSignature( - "sendMessage(uint256,bytes)", - getAndCacheReputationMiningChainId(), - payload - ); - - (bool success, bytes memory returnData) = callWithGuards(colonyBridgeAddress, bridgePayload); - - // If the function call was a success, and it returned true - if (success) { - bool res = abi.decode(returnData, (bool)); - return res; - } - return false; - } } From 88f271de3f00b9a4ee9ae2fd05bce62c0d0af187 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 4 Sep 2024 11:33:30 +0100 Subject: [PATCH 28/72] More proxy-colony permission tests (and others) --- .circleci/config.yml | 5 ++ contracts/bridging/ProxyColony.sol | 8 ++- .../colony/ColonyArbitraryTransaction.sol | 1 + .../colonyNetwork/ColonyNetworkSkills.sol | 1 - .../colonyNetwork/ColonyNetworkStorage.sol | 13 +--- .../colony-network-extensions.js | 7 ++ test/contracts-network/colony-network.js | 12 +--- test/contracts-network/metatx-token.js | 6 ++ .../reputation-basic-functionality.js | 52 +++++++++++++- test/cross-chain/cross-chain.js | 71 ++++++++++++++++++- test/extensions/staked-expenditure.js | 20 ++++++ test/extensions/whitelist.js | 14 ++++ test/misc/chainid.js | 29 ++++++-- 13 files changed, 203 insertions(+), 36 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fc01b1b1d1..d7f873fa3e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -329,6 +329,11 @@ jobs: command: CHAIN_ID=777 pnpm run test:contracts:chainid:coverage environment: NODE_OPTIONS: --max-old-space-size=6144 + - run: + name: "Running chainid tests for Mainnet" + command: CHAIN_ID=1 pnpm run test:contracts:chainid:coverage + environment: + NODE_OPTIONS: --max-old-space-size=6144 - persist_to_workspace: root: ./ paths: diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index c148c93d8d..a275d5e246 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -96,9 +96,13 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards { require(_targets[i] != bridgeAddress, "colony-cannot-target-bridge"); require(_targets[i] != owner, "colony-cannot-target-network"); - (bool success, ) = callWithGuards(_targets[i], _payloads[i]); + (bool success, bytes memory returndata) = callWithGuards(_targets[i], _payloads[i]); - require(success, "colony-arbitrary-transaction-failed"); + // Note that this is not a require because returndata might not be a string, and if we try + // to decode it we'll get a revert. + if (!success) { + revert(abi.decode(returndata, (string))); + } } } } diff --git a/contracts/colony/ColonyArbitraryTransaction.sol b/contracts/colony/ColonyArbitraryTransaction.sol index ae6528db75..279b68aa3c 100644 --- a/contracts/colony/ColonyArbitraryTransaction.sol +++ b/contracts/colony/ColonyArbitraryTransaction.sol @@ -64,6 +64,7 @@ contract ColonyArbitraryTransaction is ColonyStorage { bytes memory payload = abi.encodeWithSignature("multicall(bytes[])", _actions); IColonyNetwork(colonyNetworkAddress).bridgeMessageToNetwork(_chainId, payload); + return true; } function makeArbitraryTransactions( diff --git a/contracts/colonyNetwork/ColonyNetworkSkills.sol b/contracts/colonyNetwork/ColonyNetworkSkills.sol index 8af7834855..18ece72485 100644 --- a/contracts/colonyNetwork/ColonyNetworkSkills.sol +++ b/contracts/colonyNetwork/ColonyNetworkSkills.sol @@ -22,7 +22,6 @@ import "./../reputationMiningCycle/IReputationMiningCycle.sol"; import "./../common/Multicall.sol"; import "./ColonyNetworkStorage.sol"; import { IColonyBridge } from "./../bridging/IColonyBridge.sol"; -import { CallWithGuards } from "../common/CallWithGuards.sol"; contract ColonyNetworkSkills is ColonyNetworkStorage, Multicall { // Skills diff --git a/contracts/colonyNetwork/ColonyNetworkStorage.sol b/contracts/colonyNetwork/ColonyNetworkStorage.sol index 14ecdebefe..c8f493d25b 100644 --- a/contracts/colonyNetwork/ColonyNetworkStorage.sol +++ b/contracts/colonyNetwork/ColonyNetworkStorage.sol @@ -24,18 +24,11 @@ import { CommonStorage } from "./../common/CommonStorage.sol"; import { MultiChain } from "./../common/MultiChain.sol"; import { ERC20Extended } from "./../common/ERC20Extended.sol"; import { ColonyNetworkDataTypes } from "./ColonyNetworkDataTypes.sol"; -import { CallWithGuards } from "../common/CallWithGuards.sol"; // ignore-file-swc-131 // ignore-file-swc-108 -contract ColonyNetworkStorage is - ColonyNetworkDataTypes, - DSMath, - CommonStorage, - MultiChain, - CallWithGuards -{ +contract ColonyNetworkStorage is ColonyNetworkDataTypes, DSMath, CommonStorage, MultiChain { // Number of colonies in the network uint256 colonyCount; // Storage slot 6 // uint256 version number of the latest deployed Colony contract, used in creating new colonies @@ -194,10 +187,6 @@ contract ColonyNetworkStorage is return _skillId >> 128; } - function isMiningChain() internal view returns (bool) { - return block.chainid == getMiningChainId(); - } - function getMiningChainId() public view returns (uint256) { return reputationMiningChainId; } diff --git a/test/contracts-network/colony-network-extensions.js b/test/contracts-network/colony-network-extensions.js index 2f0de76756..73eba4ff02 100644 --- a/test/contracts-network/colony-network-extensions.js +++ b/test/contracts-network/colony-network-extensions.js @@ -305,6 +305,13 @@ contract("Colony Network Extensions", (accounts) => { }); describe("using extensions", () => { + it("extension-managing functions on Network cannot be called by non-colony addresses", async () => { + await checkErrorRevert(colonyNetwork.installExtension(TEST_EXTENSION, 1, { from: ROOT }), "colony-caller-must-be-colony"); + await checkErrorRevert(colonyNetwork.upgradeExtension(TEST_EXTENSION, 2, { from: ROOT }), "colony-caller-must-be-colony"); + await checkErrorRevert(colonyNetwork.deprecateExtension(TEST_EXTENSION, true, { from: ROOT }), "colony-caller-must-be-colony"); + await checkErrorRevert(colonyNetwork.uninstallExtension(TEST_EXTENSION, { from: ROOT }), "colony-caller-must-be-colony"); + }); + it("allows network-managed extensions to lock and unlock tokens", async () => { const tokenLockingAddress = await colonyNetwork.getTokenLocking(); const tokenLocking = await ITokenLocking.at(tokenLockingAddress); diff --git a/test/contracts-network/colony-network.js b/test/contracts-network/colony-network.js index f84384cc4d..f2f1f44dcf 100755 --- a/test/contracts-network/colony-network.js +++ b/test/contracts-network/colony-network.js @@ -26,7 +26,7 @@ const { setStorageSlot, } = require("../../helpers/test-helper"); -const { CURR_VERSION, MIN_STAKE, IPFS_HASH, ADDRESS_ZERO, WAD } = require("../../helpers/constants"); +const { CURR_VERSION, IPFS_HASH, ADDRESS_ZERO, WAD } = require("../../helpers/constants"); const { setupENSRegistrar } = require("../../helpers/upgradable-contracts"); const { expect } = chai; @@ -203,16 +203,6 @@ contract("Colony Network", (accounts) => { await checkErrorRevert(colonyNetwork.startNextCycle(), "colony-reputation-mining-clny-token-invalid-address"); }); - - it('should not allow "punishStakers" to be called from an account that is not the mining cycle', async () => { - const chainId = await getChainId(); - await metaColony.initialiseReputationMining(chainId, ethers.constants.HashZero, 0); - - await checkErrorRevert( - colonyNetwork.punishStakers([accounts[0], accounts[1]], MIN_STAKE), - "colony-reputation-mining-sender-not-active-reputation-cycle", - ); - }); }); describe("when creating new colonies at a specific version", () => { diff --git a/test/contracts-network/metatx-token.js b/test/contracts-network/metatx-token.js index 88139cbf0b..320efaacb4 100644 --- a/test/contracts-network/metatx-token.js +++ b/test/contracts-network/metatx-token.js @@ -289,6 +289,12 @@ contract("MetaTxToken", (accounts) => { const locked = await metaTxToken.locked(); expect(locked).to.be.true; }); + + it("only owner can call setOwner", async () => { + await checkErrorRevert(metaTxToken.setOwner(USER1, { from: USER1 }), "ds-auth-unauthorized"); + await metaTxToken.setOwner(USER1, { from: USER0 }); + await metaTxToken.setOwner(USER0, { from: USER1 }); + }); }); describe("when working with ether transfers", () => { diff --git a/test/contracts-network/reputation-basic-functionality.js b/test/contracts-network/reputation-basic-functionality.js index bed0d2bd07..39b8d77edc 100644 --- a/test/contracts-network/reputation-basic-functionality.js +++ b/test/contracts-network/reputation-basic-functionality.js @@ -6,7 +6,7 @@ const bnChai = require("bn-chai"); const { ethers } = require("ethers"); const { giveUserCLNYTokens, giveUserCLNYTokensAndStake } = require("../../helpers/test-data-generator"); -const { MIN_STAKE, MINING_CYCLE_DURATION, DECAY_RATE, CHALLENGE_RESPONSE_WINDOW_DURATION } = require("../../helpers/constants"); +const { MIN_STAKE, MINING_CYCLE_DURATION, DECAY_RATE, CHALLENGE_RESPONSE_WINDOW_DURATION, ADDRESS_ZERO } = require("../../helpers/constants"); const { forwardTime, checkErrorRevert, getActiveRepCycle, advanceMiningCycleNoContest, getBlockTime } = require("../../helpers/test-helper"); const { expect } = chai; @@ -213,9 +213,23 @@ contract("Reputation mining - basic functionality", (accounts) => { await checkErrorRevert(repCycle.resetWindow(), "colony-reputation-mining-sender-not-network"); }); - it('should not allow "setReputationRootHash" to be called from an account that is not a ReputationMiningCycle', async () => { + it("should not allow reputation-mining functions to be called from an account that is not an active ReputationMiningCycle", async () => { + const inactiveRepCycleAddr = await colonyNetwork.getReputationMiningCycle(false); + + await checkErrorRevert( + colonyNetwork.setReputationRootHash("0x000001", 10, [accounts[0], accounts[1]], { from: inactiveRepCycleAddr }), + "colony-reputation-mining-sender-not-active-reputation-cycle", + ); + await checkErrorRevert( + colonyNetwork.punishStakers([accounts[0], accounts[1]], MIN_STAKE, { from: inactiveRepCycleAddr }), + "colony-reputation-mining-sender-not-active-reputation-cycle", + ); + await checkErrorRevert( + colonyNetwork.reward(accounts[0], MIN_STAKE, { from: inactiveRepCycleAddr }), + "colony-reputation-mining-sender-not-active-reputation-cycle", + ); await checkErrorRevert( - colonyNetwork.setReputationRootHash("0x000001", 10, [accounts[0], accounts[1]]), + colonyNetwork.burnUnneededRewards(0, { from: inactiveRepCycleAddr }), "colony-reputation-mining-sender-not-active-reputation-cycle", ); }); @@ -258,6 +272,38 @@ contract("Reputation mining - basic functionality", (accounts) => { await checkErrorRevert(inactiveRepCycle.initialise(MINER1, MINER2), "colony-reputation-mining-cycle-already-initialised"); }); + + it("functions that should only be after initialisation cannot be called before", async () => { + const before = await web3.eth.getStorageAt(colonyNetwork.address, 17); + const ethersProvider = new ethers.providers.Web3Provider(web3.currentProvider); + await ethersProvider.send("hardhat_setStorageAt", [ + colonyNetwork.address, + ethers.utils.hexZeroPad(ethers.utils.hexlify(17), 32), + ethers.utils.hexZeroPad(ethers.utils.hexlify(0), 32), + ]); + + await checkErrorRevert(colonyNetwork.startNextCycle(), "colony-reputation-mining-not-initialised"); + await checkErrorRevert(colonyNetwork.setMiningDelegate(ADDRESS_ZERO, false), "colony-reputation-mining-not-initialised"); + await checkErrorRevert(colonyNetwork.setReputationRootHash("0x00", 0, [ADDRESS_ZERO]), "colony-reputation-mining-not-initialised"); + await checkErrorRevert(colonyNetwork.punishStakers([ADDRESS_ZERO], 0), "colony-reputation-mining-not-initialised"); + await checkErrorRevert(colonyNetwork.reward(ADDRESS_ZERO, 0), "colony-reputation-mining-not-initialised"); + await checkErrorRevert(colonyNetwork.claimMiningReward(ADDRESS_ZERO), "colony-reputation-mining-not-initialised"); + await checkErrorRevert(colonyNetwork.unstakeForMining(0), "colony-reputation-mining-not-initialised"); + await checkErrorRevert(colonyNetwork.burnUnneededRewards(0), "colony-reputation-mining-not-initialised"); + await checkErrorRevert(colonyNetwork.setReputationMiningCycleReward(0), "colony-reputation-mining-not-initialised"); + + // Put in to recovery mode + await colonyNetwork.enterRecoveryMode(); + + await checkErrorRevert( + colonyNetwork.setReplacementReputationUpdateLogEntry(ADDRESS_ZERO, 0, ADDRESS_ZERO, 0, 0, ADDRESS_ZERO, 0, 0), + "colony-reputation-mining-not-initialised", + ); + await colonyNetwork.approveExitRecovery(); + await colonyNetwork.exitRecoveryMode(); + + await ethersProvider.send("hardhat_setStorageAt", [colonyNetwork.address, ethers.utils.hexZeroPad(ethers.utils.hexlify(17), 32), before]); + }); }); describe("when reading reputation mining constant properties", async () => { diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index e99fe7fbe1..e1e50c66b9 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -373,6 +373,19 @@ contract("Cross-chain", (accounts) => { await checkErrorRevertEthers(tx.wait(), "colony-caller-must-be-meta-colony"); }); + it("callProxyNetwork can only be called through the metacolony", async () => { + const payload = homeColonyNetwork.interface.encodeFunctionData("setColonyBridgeAddress", [ADDRESS_ZERO]); + let tx = await homeColonyNetwork.createColonyForFrontend(ADDRESS_ZERO, "A", "A", 18, CURR_VERSION, "", ""); + await tx.wait(); + + const colonyCount = await homeColonyNetwork.getColonyCount(); + const colonyAddress = await homeColonyNetwork.getColony(colonyCount); + const fakeMetaColony = new ethers.Contract(colonyAddress, IMetaColony.abi, ethersHomeSigner); + + tx = await fakeMetaColony.callProxyNetwork(foreignChainId, [payload], { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "colony-caller-must-be-meta-colony"); + }); + it("callProxyNetwork can only be called by root permissions on the metacolony", async () => { const payload = remoteColonyNetwork.interface.encodeFunctionData("setColonyBridgeAddress", [ADDRESS_ZERO]); const homeMetacolony2 = new ethers.Contract(homeMetacolony.address, IMetaColony.abi, ethersHomeSigner2); @@ -1424,6 +1437,50 @@ contract("Cross-chain", (accounts) => { expect(colonyBalance.toHexString()).to.equal(ethers.utils.parseEther("0.7").toHexString()); expect(recipientBalanceAfter.sub(receipientBalanceBefore).toHexString()).to.equal(ethers.utils.parseEther("0.3").toHexString()); }); + + it("a bookkeeping error will mean that tokens can no longer be claimed until tokens are returned", async () => { + const tokenAmount = ethers.utils.parseEther("100"); + + let tx = await foreignToken["mint(address,uint256)"](proxyColony.address, tokenAmount); + await tx.wait(); + + // Claim on the foreign chain + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + tx = await proxyColony.claimTokens(foreignToken.address); + await tx.wait(); + await p; + + // Now remove the tokens + const balanceSlot = ethers.utils.keccak256( + ethers.utils.concat([ethers.utils.hexZeroPad(proxyColony.address, 32), ethers.utils.hexZeroPad(1, 32)]), + ); + + await ethersForeignProvider.send("hardhat_setStorageAt", [ + foreignToken.address, + balanceSlot, + ethers.utils.hexZeroPad(ethers.utils.parseEther("30").toHexString(), 32), + ]); + + tx = await proxyColony.claimTokens(foreignToken.address, { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "colony-shell-token-bookkeeping-error"); + + // Now return the tokens + await ethersForeignProvider.send("hardhat_setStorageAt", [ + foreignToken.address, + balanceSlot, + ethers.utils.hexZeroPad(ethers.utils.parseEther("100").toHexString(), 32), + ]); + + // Mint some more tokens + tx = await foreignToken["mint(address,uint256)"](proxyColony.address, ethers.utils.parseEther("100")); + await tx.wait(); + + // Can now claim + const p2 = bridgeMonitor.getPromiseForNextBridgedTransaction(); + tx = await proxyColony.claimTokens(foreignToken.address); + await tx.wait(); + await p2; + }); }); describe("making arbitrary transactions on another chain", async () => { @@ -1467,6 +1524,18 @@ contract("Cross-chain", (accounts) => { expect(balanceAfter.sub(balanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); }); + it("arbitrary transactions on the foreign chain must go to contracts", async () => { + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + + const payload = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [proxyColony.address, ethers.utils.parseEther("100")]); + + const tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [accounts[0]], [payload]); + await tx.wait(); + await p; + + await checkErrorRevertEthers(p, "require-execute-call-target-not-contract"); + }); + it("can make multiple arbitrary transactions on the foreign chain in one go", async () => { const shellBalanceBefore = await foreignToken.balanceOf(proxyColony.address); const colonyBalanceBefore = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); @@ -1516,7 +1585,7 @@ contract("Cross-chain", (accounts) => { const tx4 = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], ["0x00000000"]); await tx4.wait(); - await checkErrorRevertEthers(p, "colony-arbitrary-transaction-failed"); + await checkErrorRevertEthers(p, "require-execute-call-reverted-with-no-error"); }); }); diff --git a/test/extensions/staked-expenditure.js b/test/extensions/staked-expenditure.js index 05ca3a257f..e3fe08ca37 100644 --- a/test/extensions/staked-expenditure.js +++ b/test/extensions/staked-expenditure.js @@ -330,6 +330,26 @@ contract("StakedExpenditure", (accounts) => { ); }); + it("cannot slash the stake if the expenditure is already cancelled", async () => { + await stakedExpenditure.makeExpenditureWithStake(1, UINT256_MAX, 1, domain1Key, domain1Value, domain1Mask, domain1Siblings, { from: USER0 }); + const expenditureId = await colony.getExpenditureCount(); + await colony.cancelExpenditure(expenditureId); + + await checkErrorRevert( + stakedExpenditure.cancelAndPunish(1, UINT256_MAX, 1, UINT256_MAX, expenditureId, true, { from: USER1 }), + "staked-expenditure-expenditure-already-cancelled", + ); + }); + + it("cannot slash the stake if the expenditure is still in draft", async () => { + await stakedExpenditure.makeExpenditureWithStake(1, UINT256_MAX, 1, domain1Key, domain1Value, domain1Mask, domain1Siblings, { from: USER0 }); + const expenditureId = await colony.getExpenditureCount(); + + await checkErrorRevert( + stakedExpenditure.cancelAndPunish(1, UINT256_MAX, 1, UINT256_MAX, expenditureId, true, { from: USER1 }), + "staked-expenditure-expenditure-still-draft", + ); + }); it("can cancel the expenditure without penalty", async () => { await stakedExpenditure.makeExpenditureWithStake(1, UINT256_MAX, 1, domain1Key, domain1Value, domain1Mask, domain1Siblings, { from: USER0 }); const expenditureId = await colony.getExpenditureCount(); diff --git a/test/extensions/whitelist.js b/test/extensions/whitelist.js index f85ee604ab..c02cb5ba90 100644 --- a/test/extensions/whitelist.js +++ b/test/extensions/whitelist.js @@ -258,5 +258,19 @@ contract("Whitelist", (accounts) => { status = await whitelist.isApproved(USER1); expect(status).to.be.false; }); + + it("cannot approve users if deprecated", async () => { + await whitelist.initialise(true, ""); + await colony.deprecateExtension(WHITELIST, true, { from: USER0 }); + + await checkErrorRevert(whitelist.approveUsers([USER1], true, { from: USER0 }), "colony-extension-deprecated"); + }); + + it("users can't sign an agreement if deprecated", async () => { + await whitelist.initialise(true, ""); + await colony.deprecateExtension(WHITELIST, true, { from: USER0 }); + + await checkErrorRevert(whitelist.signAgreement(IPFS_HASH, { from: USER0 }), "colony-extension-deprecated"); + }); }); }); diff --git a/test/misc/chainid.js b/test/misc/chainid.js index d1cdc409ea..8dabf35c34 100644 --- a/test/misc/chainid.js +++ b/test/misc/chainid.js @@ -93,13 +93,23 @@ contract("Contract Storage", (accounts) => { it("should handle tokens appropriately if auction is initialised for the CLNY token", async () => { await giveUserCLNYTokens(colonyNetwork, colonyNetwork.address, WAD); const supplyBefore = await clnyToken.totalSupply(); + const balanceBefore = await clnyToken.balanceOf(colonyNetwork.address); const tx = await colonyNetwork.startTokenAuction(clnyToken.address); const supplyAfter = await clnyToken.totalSupply(); + const balanceAfter = await clnyToken.balanceOf(colonyNetwork.address); - // tokens should be transferred to metacolony - expect(supplyBefore).to.eq.BN(supplyAfter); - await expectEvent(tx, "Transfer(address indexed,address indexed,uint256)", [colonyNetwork.address, metaColony.address, WAD]); + if (await isMainnet()) { + // tokens should be burned. + expect(supplyBefore.sub(supplyAfter)).to.eq.BN(balanceBefore); + await expectEvent(tx, "Burn(address indexed,uint256)", [colonyNetwork.address, WAD]); + expect(balanceAfter).to.be.zero; + expect(supplyBefore.sub(balanceBefore)).to.eq.BN(supplyAfter); + } else { + // tokens should be transferred to metacolony + expect(supplyBefore).to.eq.BN(supplyAfter); + await expectEvent(tx, "Transfer(address indexed,address indexed,uint256)", [colonyNetwork.address, metaColony.address, WAD]); + } }); it("CLNY raised from auctions is dealt with appropriately", async () => { @@ -119,6 +129,7 @@ contract("Contract Storage", (accounts) => { await clnyToken.approve(tokenAuction.address, clnyNeededForMaxPriceAuctionSellout, { from: accounts[1] }); await tokenAuction.bid(clnyNeededForMaxPriceAuctionSellout, { from: accounts[1] }); + const balanceBefore = await clnyToken.balanceOf(tokenAuction.address); const supplyBefore = await clnyToken.totalSupply(); const receivedTotal = await tokenAuction.receivedTotal(); expect(receivedTotal).to.not.be.zero; @@ -127,9 +138,15 @@ contract("Contract Storage", (accounts) => { const balanceAfter = await clnyToken.balanceOf(tokenAuction.address); expect(balanceAfter).to.be.zero; const supplyAfter = await clnyToken.totalSupply(); - // tokens should be transferred to metacolony - expect(supplyBefore).to.eq.BN(supplyAfter); - await expectEvent(tx, "Transfer(address indexed,address indexed,uint256)", [tokenAuction.address, metaColony.address, receivedTotal]); + if (await isMainnet()) { + // tokens should be burned. + expect(supplyBefore.sub(supplyAfter)).to.eq.BN(balanceBefore); + await expectEvent(tx, "Burn(address indexed,uint256)", [tokenAuction.address, receivedTotal]); + } else { + // tokens should be transferred to metacolony + expect(supplyBefore).to.eq.BN(supplyAfter); + await expectEvent(tx, "Transfer(address indexed,address indexed,uint256)", [tokenAuction.address, metaColony.address, receivedTotal]); + } }); }); }); From d3dd2becfacabf3caa137867219a3e3b2318cb6e Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 11 Sep 2024 16:41:34 +0100 Subject: [PATCH 29/72] Allow funds to be sent directly to domains --- contracts/colony/ColonyFunding.sol | 9 ++++++- contracts/colonyNetwork/IColonyNetwork.sol | 28 ++++++++++++++++++++++ docs/interfaces/icolonynetwork.md | 18 ++++++++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index 4413236d2d..f79f7f1d69 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -163,7 +163,14 @@ contract ColonyFunding is uint256 remainder = claimAmount - feeToPay; nonRewardPotsTotal[_token] += remainder; - fundingPots[0].balance[_token] += feeToPay; + // fundingPots[0].balance[_token] += feeToPay; + setFundingPotBalance( + 0, + block.chainid, + _token, + getFundingPotBalance(0, block.chainid, _token) + feeToPay + ); + uint256 fundingPotId = domains[_domainId].fundingPotId; uint256 approvedAmount = domainReputationApproval[_domainId]; diff --git a/contracts/colonyNetwork/IColonyNetwork.sol b/contracts/colonyNetwork/IColonyNetwork.sol index 0ecd5f1554..c978b1acd2 100644 --- a/contracts/colonyNetwork/IColonyNetwork.sol +++ b/contracts/colonyNetwork/IColonyNetwork.sol @@ -574,4 +574,32 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery, IBasicMetaTransac /// @param _destinationChainId The chainId of the chain to create the colony on /// @param _salt The salt to use for the colony creation function createProxyColony(uint256 _destinationChainId, bytes32 _salt) external; + + /// @notice Function called by a colony to ensure that a DomainTokenReceiver has been deployed and set up correctly + /// for a particular domain. + /// @dev Should only be called by a colony. + /// @param _domainId The domainId of the domain to check the deployment for + /// @return domainTokenReceiverAddress The address of the DomainTokenReceiver + function checkDomainTokenReceiverDeployed( + uint256 _domainId + ) external returns (address domainTokenReceiverAddress); + + /// @notice Function to set the resolver that should be used by DomainTokenReceivers + /// @dev The next time a claim for a domain is called, they will first be updated to this resolver + /// @param _resolver The address of the resolver to use + function setDomainTokenReceiverResolver(address _resolver) external; + + /// @notice Get the current DomainTokenReceiver resolver + /// @dev Note that some Receivers might be using an old resolver + /// @return resolver The address of the current resolver + function getDomainTokenReceiverResolver() external view returns (address resolver); + + /// @notice Get the DomainTokenReceiver address for a particular domain + /// @param _colonyAddress The address of the colony + /// @param _domainId The domainId of the domain + /// @return domainTokenReceiverAddress The address of the DomainTokenReceiver (which may or may not be deployed currently) + function getDomainTokenReceiverAddress( + address _colonyAddress, + uint256 _domainId + ) external view returns (address domainTokenReceiverAddress); } diff --git a/docs/interfaces/icolonynetwork.md b/docs/interfaces/icolonynetwork.md index efe11b1cf3..fd35d79271 100644 --- a/docs/interfaces/icolonynetwork.md +++ b/docs/interfaces/icolonynetwork.md @@ -124,6 +124,24 @@ Calculate raw miner weight in WADs. |---|---|---| |_minerWeight|uint256|The weight of miner reward +### ▸ `checkDomainTokenReceiverDeployed(uint256 _domainId):address domainTokenReceiverAddress` + +Function called by a colony to ensure that a DomainTokenReceiver has been deployed and set up correctly for a particular domain. + +*Note: Should only be called by a colony.* + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_domainId|uint256|The domainId of the domain to check the deployment for + +**Return Parameters** + +|Name|Type|Description| +|---|---|---| +|domainTokenReceiverAddress|address|The address of the DomainTokenReceiver + ### ▸ `checkNotAdditionalProtectedVariable(uint256 _slot)` Check whether the supplied slot is a protected variable specific to this contract From 97e45ba4554a3580367e1f853ad7b6ed91223d22 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 16 Sep 2024 11:13:33 +0100 Subject: [PATCH 30/72] Respect reward pots for domain-level claims; add event --- test/contracts-network/colony-funding.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/contracts-network/colony-funding.js b/test/contracts-network/colony-funding.js index 5d1880cdd7..d8cbfb57d0 100755 --- a/test/contracts-network/colony-funding.js +++ b/test/contracts-network/colony-funding.js @@ -877,6 +877,8 @@ contract("Colony Funding", (accounts) => { const resolverAfter = await receiverAsEtherRouter.resolver(); expect(resolverAfter).to.not.equal(resolver); expect(resolverAfter).to.equal(newResolver.address); + expect(domainPotBalanceAfter.sub(domainPotBalanceBefore)).to.eq.BN(99); + expect(nonRewardPotsTotalAfter.sub(nonRewardPotsTotalBefore)).to.eq.BN(99); }); ||||||| parent of d7aa9686f (Allow funds to be sent directly to domains) ======= From bb6e6e2a1ff95415631311076009b795b0203890 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 18 Sep 2024 17:16:29 +0100 Subject: [PATCH 31/72] First pass at cross-chain domain-level token swaps --- contracts/bridging/ProxyColony.sol | 32 +++++- contracts/bridging/ProxyColonyNetwork.sol | 20 +++- contracts/colony/ColonyFunding.sol | 55 ++++++++- contracts/colony/IColony.sol | 26 ++++- .../colonyNetwork/ColonyNetworkDeployer.sol | 89 ++++++++++++++- contracts/common/CallWithGuards.sol | 11 +- contracts/common/DomainReceiverManagement.sol | 107 ++++++++++++++++++ contracts/common/IsContract.sol | 28 +++++ contracts/testHelpers/LiFiFacetProxyMock.sol | 63 +++++++++++ docs/interfaces/icolony.md | 33 ++++++ docs/interfaces/imetacolony.md | 33 ++++++ helpers/constants.js | 2 + test/cross-chain/cross-chain.js | 70 ++++++++++++ test/deploy-proxy-network-fixture.js | 8 ++ test/truffle-fixture.js | 5 + 15 files changed, 561 insertions(+), 21 deletions(-) create mode 100644 contracts/common/DomainReceiverManagement.sol create mode 100644 contracts/common/IsContract.sol create mode 100644 contracts/testHelpers/LiFiFacetProxyMock.sol diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index a275d5e246..73418a4a5e 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -25,6 +25,7 @@ import { ERC20Extended } from "./../common/ERC20Extended.sol"; import { Multicall } from "./../common/Multicall.sol"; import { IColonyNetwork } from "./../colonyNetwork/IColonyNetwork.sol"; import { ProxyColonyNetwork } from "./ProxyColonyNetwork.sol"; +import { DomainTokenReceiver } from "./../common/DomainTokenReceiver.sol"; contract ProxyColony is DSAuth, Multicall, CallWithGuards { // Address of the Resolver contract used by EtherRouter for lookups and routing @@ -42,7 +43,7 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards { _; } - event ColonyFundsClaimed(address token, uint256 balance); + event DomainFundsClaimed(address token, uint256 _domainId, uint256 balance); event TransferMade(address token, address user, uint256 amount); // Public functions @@ -58,14 +59,39 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards { tokenBalances[_token] = balance; bytes memory payload = abi.encodeWithSignature( - "recordClaimedFundsFromBridge(uint256,address,uint256)", + "recordClaimedFundsFromBridge(uint256,address,uint256,uint256)", block.chainid, _token, + 1, difference ); ProxyColonyNetwork(owner).bridgeMessage(payload); - emit ColonyFundsClaimed(_token, balance); + emit DomainFundsClaimed(_token, 1, balance); + } + + function claimTokensForDomain(address _token, uint256 _domainId) public { + address domainTokenReceiverAddress = ProxyColonyNetwork(owner).checkDomainTokenReceiverDeployed( + _domainId + ); + + uint256 balance = (_token == address(0x0)) + ? address(domainTokenReceiverAddress).balance + : ERC20Extended(_token).balanceOf(address(domainTokenReceiverAddress)); + + DomainTokenReceiver(domainTokenReceiverAddress).transferToColony(_token); + + bytes memory payload = abi.encodeWithSignature( + "recordClaimedFundsFromBridge(uint256,address,uint256,uint256)", + block.chainid, + _token, + _domainId, + balance + ); + + ProxyColonyNetwork(owner).bridgeMessage(payload); + + emit DomainFundsClaimed(_token, _domainId, balance); } // TODO: secure this function diff --git a/contracts/bridging/ProxyColonyNetwork.sol b/contracts/bridging/ProxyColonyNetwork.sol index dcffaf506e..52761a1696 100644 --- a/contracts/bridging/ProxyColonyNetwork.sol +++ b/contracts/bridging/ProxyColonyNetwork.sol @@ -29,23 +29,23 @@ import { EtherRouterCreate3 } from "./../common/EtherRouterCreate3.sol"; import { ICreateX } from "./../../lib/createx/src/ICreateX.sol"; import { EtherRouter } from "./../common/EtherRouter.sol"; import { IColonyBridge } from "./IColonyBridge.sol"; +import { DomainReceiverManagement } from "./../common/DomainReceiverManagement.sol"; -contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards { +contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards, DomainReceiverManagement { address resolver; // Storage slot 2 (from DSAuth there is authority and owner at storage slots 0 and 1 respectively) - address constant CREATEX_ADDRESS = 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed; - address public colonyBridgeAddress; uint256 public homeChainId; address public proxyColonyResolverAddress; mapping(address => bool) public shellColonies; + address public domainTokenReceiverResolver; /// @notice Event logged when the colony network has data about a bridge contract set. /// @param bridgeAddress The address of the bridge contract that will be interacted with event BridgeSet(address bridgeAddress); modifier onlyColony() { - require(shellColonies[msgSender()], "colony-network-caller-must-be-proxy-colony"); + require(msgSenderIsColony(), "colony-network-caller-must-be-proxy-colony"); _; } @@ -80,6 +80,18 @@ contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards { homeChainId = _homeChainId; } + function getDomainTokenReceiverResolver() public view override returns (address) { + return domainTokenReceiverResolver; + } + + function msgSenderIsColony() internal view override returns (bool) { + return shellColonies[msgSender()]; + } + + function setDomainTokenReceiverResolver(address _resolver) public auth { + domainTokenReceiverResolver = _resolver; + } + function createProxyColonyFromBridge(bytes32 _salt) public onlyColonyBridge { EtherRouter etherRouter = EtherRouter( payable( diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index f79f7f1d69..6e9dbb79cf 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -231,6 +231,57 @@ contract ColonyFunding is return domainReputationApproval[_domainId]; } + address constant LIFI_ADDRESS = 0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE; + function exchangeTokensViaLiFi( + uint256 _permissionDomainId, + uint256 _childSkillIndex, + uint256 _domainId, + bytes memory _txdata, + uint256 _value, + address _token, + uint256 _amount + ) public stoppable authDomain(_permissionDomainId, _childSkillIndex, _domainId) { + // TODO: Colony Network fee + Domain storage domain = domains[_domainId]; + + // Check the domain has enough for what is + if (_token == address(0x0)) { + require( + _value + _amount <= getFundingPotBalance(domain.fundingPotId, _token), + "colony-insufficient-funds" + ); + } else { + require( + _amount <= getFundingPotBalance(domain.fundingPotId, _token), + "colony-insufficient-funds" + ); + require( + _value <= getFundingPotBalance(domain.fundingPotId, _token), + "colony-insufficient-funds" + ); + } + + // Deduct the amount from the domain + setFundingPotBalance( + domain.fundingPotId, + block.chainid, + _token, + getFundingPotBalance(domain.fundingPotId, block.chainid, _token) - _amount + ); + + setFundingPotBalance( + domain.fundingPotId, + block.chainid, + address(0x0), + getFundingPotBalance(domain.fundingPotId, block.chainid, _token) - _value + ); + + ERC20Extended(_token).approve(LIFI_ADDRESS, _amount); + (bool success, ) = LIFI_ADDRESS.call{ value: _value }(_txdata); + + require(success, "colony-exchange-tokens-failed"); + } + function getNonRewardPotsTotal(address _token) public view returns (uint256) { return nonRewardPotsTotal[_token]; } @@ -408,9 +459,11 @@ contract ColonyFunding is function recordClaimedFundsFromBridge( uint256 _chainId, address _token, + uint256 _domainId, uint256 _amount ) public stoppable { - fundingPots[1].chainBalances[_chainId][_token] += _amount; + Domain storage d = domains[_domainId]; + fundingPots[d.fundingPotId].chainBalances[_chainId][_token] += _amount; emit ProxyColonyFundsClaimed(_chainId, _token, _amount); } diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index cad25c6f0e..917951ef57 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -926,11 +926,35 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, /// @return uint256 amount Amount of the token that the domain can receive function getAllowedDomainReputationReceipt(uint256 _domainId) external view returns (uint256); + /// @notice Exchange funds between two tokens, potentially between chains + /// @param _permissionDomainId The domainId in which I have the permission to take this action + /// @param _childSkillIndex The child index in `_permissionDomainId` where we can find `_domainId` + /// @param _domainId Id of the domain + /// @param _txdata Transaction data for the exchange + /// @param _value Value of the transaction + /// @param _token Address of the token, `0x0` value indicates Ether + /// @param _amount Amount of tokens to exchange + function exchangeTokensViaLiFi( + uint256 _permissionDomainId, + uint256 _childSkillIndex, + uint256 _domainId, + bytes memory _txdata, + uint256 _value, + address _token, + uint256 _amount + ) external; + /// @notice Used by the bridge to indicate that funds have been claimed on another chain. /// @param _chainId Chain id of the chain where the funds were claimed /// @param _token Address of the token, `0x0` value indicates Ether + /// @param _domainId Id of the domain where the funds were claimed /// @param _amount Amount of funds claimed - function recordClaimedFundsFromBridge(uint256 _chainId, address _token, uint256 _amount) external; + function recordClaimedFundsFromBridge( + uint256 _chainId, + address _token, + uint256 _domainId, + uint256 _amount + ) external; /// @notice Get the balance of a funding pot for a specific token on a specific chain /// @param _potId Id of the funding pot diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index 13c4d17e6a..aee89b15e5 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -30,10 +30,9 @@ import { ICreateX } from "./../../lib/createx/src/ICreateX.sol"; import { EtherRouterCreate3 } from "./../common/EtherRouterCreate3.sol"; import { IColonyBridge } from "./../bridging/IColonyBridge.sol"; import { DomainTokenReceiver } from "./../common/DomainTokenReceiver.sol"; +import { DomainReceiverManagement } from "./../common/DomainReceiverManagement.sol"; -contract ColonyNetworkDeployer is ColonyNetworkStorage { - address constant CREATEX_ADDRESS = 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed; - +contract ColonyNetworkDeployer is ColonyNetworkStorage, DomainReceiverManagement { function createMetaColony(address _tokenAddress) public stoppable auth { require(metaColony == address(0x0), "colony-meta-colony-exists-already"); @@ -194,10 +193,11 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage { domainReceiverResolverAddress = _resolver; } - function getDomainTokenReceiverResolver() public view returns (address) { + function getDomainTokenReceiverResolver() public view override stoppable returns (address) { return domainReceiverResolverAddress; } +<<<<<<< HEAD function idempotentDeployDomainTokenReceiver( uint256 _domainId ) public stoppable calledByColony returns (address domainTokenReceiverAddress) { @@ -263,6 +263,87 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage { // redeployment protection in createX. // This is intentional, as we want to allow the same receiver to be deployed on different chains return salt; +||||||| parent of 125a5a92 (First pass at cross-chain domain-level token swaps) + function checkDomainTokenReceiverDeployed( + uint256 _domainId + ) public calledByColony returns (address domainTokenReceiverAddress) { + // Calculate the address the domain should be receiving funds at + domainTokenReceiverAddress = getDomainTokenReceiverAddress(msgSender(), _domainId); + + if (!isContract(domainTokenReceiverAddress)) { + // Then deploy the contract + bytes32 salt = getDomainTokenReceiverDeploySalt(msgSender(), _domainId); + ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( + salt, + type(EtherRouterCreate3).creationCode, + abi.encodeWithSignature("setOwner(address)", (address(this))), + ICreateX.Values(0, 0) + ); + } + + // Check it's got the right resolver + try EtherRouter(payable(domainTokenReceiverAddress)).resolver() returns (Resolver resolver) { + if (address(resolver) != domainReceiverResolverAddress) { + EtherRouter(payable(domainTokenReceiverAddress)).setResolver(domainReceiverResolverAddress); + } + } catch { + revert("colony-network-domain-receiver-not-etherrouter"); + } + + // Check it's set up correctly + + if (DomainTokenReceiver(domainTokenReceiverAddress).getColonyAddress() != msgSender()) { + DomainTokenReceiver(domainTokenReceiverAddress).setColonyAddress(msgSender()); + } + + return domainTokenReceiverAddress; + } + + function isContract(address addr) internal view returns (bool) { + uint256 size; + assembly { + size := extcodesize(addr) + } + return size > 0; + } + + function getDomainTokenReceiverAddress( + address _colony, + uint256 _domainId + ) public view returns (address) { + bytes32 salt = getDomainTokenReceiverDeploySalt(_colony, _domainId); + + // To get the correct address, we have to mimic the _guard functionality of CreateX + bytes32 guardedSalt = keccak256(abi.encode(bytes32(uint256(uint160(address(this)))), salt)); + return ICreateX(CREATEX_ADDRESS).computeCreate3Address(guardedSalt); + } + + function getDomainTokenReceiverDeploySalt( + address _colony, + uint256 _domainId + ) internal view returns (bytes32) { + // Calculate the address the domain should be receiving funds at + // We only want Colony Networks to be able to deploy to the same address, + // so we use the permissioned deploy protection feature of CreateX, and set + // the first 160 bits of the salt to the address of this contract. + + bytes32 salt = bytes32(uint256(uint160(address(this)))) << 96; + + bytes32 additionalSalt = keccak256(abi.encode(_colony, _domainId)); + // We use the first 88 bits of the additional salt, which is a function of the colony and domainId, + // to add entropy in the last 88 bits of the salt + salt = salt | (additionalSalt >> 168); + // We have set the first 160 bits, and the last 88 bits of the salt + // Note that this leaves byte 21 of the salt as zero (0x00), which disables cross-chain + // redeployment protection in createX. + // This is intentional, as we want to allow the same receiver to be deployed on different chains + return salt; +======= + function msgSenderIsColony() internal view override returns (bool) { + require(_isColony[msgSender()], "colony-caller-must-be-colony"); + assert(msgSender() == msg.sender); + return true; +>>>>>>> 125a5a92 (First pass at cross-chain domain-level token swaps) } function deployColony(address _tokenAddress, uint256 _version) internal returns (address) { diff --git a/contracts/common/CallWithGuards.sol b/contracts/common/CallWithGuards.sol index 9525373760..10fb0d5543 100644 --- a/contracts/common/CallWithGuards.sol +++ b/contracts/common/CallWithGuards.sol @@ -17,15 +17,10 @@ pragma solidity 0.8.28; along with The Colony Network. If not, see . */ -contract CallWithGuards { - function isContract(address addr) internal view returns (bool) { - uint256 size; - assembly { - size := extcodesize(addr) - } - return size > 0; - } +import { IsContract } from "./IsContract.sol"; + +contract CallWithGuards is IsContract { function callWithGuards( address _target, bytes memory _payload diff --git a/contracts/common/DomainReceiverManagement.sol b/contracts/common/DomainReceiverManagement.sol new file mode 100644 index 0000000000..6e8c9bcbce --- /dev/null +++ b/contracts/common/DomainReceiverManagement.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + This file is part of The Colony Network. + + The Colony Network is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The Colony Network is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with The Colony Network. If not, see . +*/ + +pragma solidity 0.8.27; +pragma experimental "ABIEncoderV2"; + +import { MetaTransactionMsgSender } from "./MetaTransactionMsgSender.sol"; +import { DomainTokenReceiver } from "./DomainTokenReceiver.sol"; +import { ICreateX } from "./../../lib/createx/src/ICreateX.sol"; +import { EtherRouterCreate3 } from "./EtherRouterCreate3.sol"; +import { EtherRouter } from "./EtherRouter.sol"; +import { Resolver } from "./Resolver.sol"; +import { IsContract } from "./IsContract.sol"; + +abstract contract DomainReceiverManagement is MetaTransactionMsgSender, IsContract { + address constant CREATEX_ADDRESS = 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed; + + function getDomainTokenReceiverResolver() public view virtual returns (address); + function msgSenderIsColony() internal view virtual returns (bool); + + function checkDomainTokenReceiverDeployed( + uint256 _domainId + ) public returns (address domainTokenReceiverAddress) { + require(msgSenderIsColony(), "colony-domain-receiver-management-not-colony"); + + // Calculate the address the domain should be receiving funds at + domainTokenReceiverAddress = getDomainTokenReceiverAddress(msgSender(), _domainId); + + if (!isContract(domainTokenReceiverAddress)) { + // Then deploy the contract + bytes32 salt = getDomainTokenReceiverDeploySalt(msgSender(), _domainId); + ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( + salt, + type(EtherRouterCreate3).creationCode, + abi.encodeWithSignature("setOwner(address)", (address(this))), + ICreateX.Values(0, 0) + ); + } + + // Check it's got the right resolver + try EtherRouter(payable(domainTokenReceiverAddress)).resolver() returns (Resolver resolver) { + if (address(resolver) != getDomainTokenReceiverResolver()) { + EtherRouter(payable(domainTokenReceiverAddress)).setResolver( + getDomainTokenReceiverResolver() + ); + } + } catch { + revert("colony-network-domain-receiver-not-etherrouter"); + } + + // Check it's set up correctly + DomainTokenReceiver(domainTokenReceiverAddress).getColonyAddress(); + // if (DomainTokenReceiver(domainTokenReceiverAddress).getColonyAddress() != msgSender()) { + // DomainTokenReceiver(domainTokenReceiverAddress).setColonyAddress(msgSender()); + // } + + // return domainTokenReceiverAddress; + } + + function getDomainTokenReceiverAddress( + address _colony, + uint256 _domainId + ) public view returns (address) { + bytes32 salt = getDomainTokenReceiverDeploySalt(_colony, _domainId); + + // To get the correct address, we have to mimic the _guard functionality of CreateX + bytes32 guardedSalt = keccak256(abi.encode(bytes32(uint256(uint160(address(this)))), salt)); + return ICreateX(CREATEX_ADDRESS).computeCreate3Address(guardedSalt); + } + + function getDomainTokenReceiverDeploySalt( + address _colony, + uint256 _domainId + ) internal view returns (bytes32) { + // Calculate the address the domain should be receiving funds at + // We only want Colony Networks to be able to deploy to the same address, + // so we use the permissioned deploy protection feature of CreateX, and set + // the first 160 bits of the salt to the address of this contract. + + bytes32 salt = bytes32(uint256(uint160(address(this)))) << 96; + + bytes32 additionalSalt = keccak256(abi.encode(_colony, _domainId)); + // We use the first 88 bits of the additional salt, which is a function of the colony and domainId, + // to add entropy in the last 88 bits of the salt + salt = salt | (additionalSalt >> 168); + // We have set the first 160 bits, and the last 88 bits of the salt + // Note that this leaves byte 21 of the salt as zero (0x00), which disables cross-chain + // redeployment protection in createX. + // This is intentional, as we want to allow the same receiver to be deployed on different chains + return salt; + } +} diff --git a/contracts/common/IsContract.sol b/contracts/common/IsContract.sol new file mode 100644 index 0000000000..679bcc1b28 --- /dev/null +++ b/contracts/common/IsContract.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + This file is part of The Colony Network. + + The Colony Network is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The Colony Network is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with The Colony Network. If not, see . +*/ +pragma solidity 0.8.27; + +contract IsContract { + function isContract(address addr) internal view returns (bool) { + uint256 size; + assembly { + size := extcodesize(addr) + } + return size > 0; + } +} diff --git a/contracts/testHelpers/LiFiFacetProxyMock.sol b/contracts/testHelpers/LiFiFacetProxyMock.sol new file mode 100644 index 0000000000..5261d9f965 --- /dev/null +++ b/contracts/testHelpers/LiFiFacetProxyMock.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + This file is part of The Colony Network. + + The Colony Network is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The Colony Network is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with The Colony Network. If not, see . +*/ + +pragma solidity 0.8.27; + +import { ColonyExtensionMeta } from "./../extensions/ColonyExtensionMeta.sol"; +import { IColony } from "../colony/IColony.sol"; +import { ERC20Extended } from "../common/ERC20Extended.sol"; + +contract LiFiFacetProxyMock { + event SwapTokens( + uint256 _fromChainId, + address _fromToken, + uint256 _toChainId, + address _toToken, + address _toAddress, + uint256 _amount + ); + + function swapTokensMock( + uint256 _fromChainId, + address _fromToken, + uint256 _toChainId, + address _toToken, + address _toAddress, + uint256 _amount + ) external payable { + if (_fromToken == address(0)) { + require(msg.value == _amount, "LiFiFacetProxyMock-ether-amount-mismatch"); + } else { + require( + ERC20Extended(_fromToken).transferFrom(msg.sender, address(this), _amount), + "LiFiFacetProxyMock-transferFrom-failed" + ); + } + + // And then emit an event that... maybe some dev utility will listen for? + emit SwapTokens(_fromChainId, _fromToken, _toChainId, _toToken, _toAddress, _amount); + } + + fallback() external payable { + revert("LiFiFacetProxyMock-unimplemented-function"); + } + + receive() external payable { + revert("LiFiFacetProxyMock-unimplemented-function-2"); + } +} diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index 86d51c9513..a303f67671 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -419,6 +419,24 @@ Put colony network mining into recovery mode. Can only be called by user with re +### ▸ `exchangeTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, address _token, uint256 _amount)` + +Exchange funds between two tokens, potentially between chains + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which I have the permission to take this action +|_childSkillIndex|uint256|The child index in `_permissionDomainId` where we can find `_domainId` +|_domainId|uint256|Id of the domain +|_txdata|bytes|Transaction data for the exchange +|_value|uint256|Value of the transaction +|_token|address|Address of the token, `0x0` value indicates Ether +|_amount|uint256|Amount of tokens to exchange + + ### ▸ `executeMetaTransaction(address userAddress, bytes memory payload, bytes32 sigR, bytes32 sigS, uint8 sigV):bytes returnData` Executes a metatransaction targeting this contract @@ -1370,6 +1388,21 @@ Get the owner of the contract |---|---|---| |owner|address|The owner of the contract +### ▸ `recordClaimedFundsFromBridge(uint256 _chainId, address _token, uint256 _domainId, uint256 _amount)` + +Used by the bridge to indicate that funds have been claimed on another chain. + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_chainId|uint256|Chain id of the chain where the funds were claimed +|_token|address|Address of the token, `0x0` value indicates Ether +|_domainId|uint256|Id of the domain where the funds were claimed +|_amount|uint256|Amount of funds claimed + + ### ▸ `registerColonyLabel(string memory colonyName, string memory orbitdb)` Register colony's ENS label. diff --git a/docs/interfaces/imetacolony.md b/docs/interfaces/imetacolony.md index d1253b03c3..00c59df475 100644 --- a/docs/interfaces/imetacolony.md +++ b/docs/interfaces/imetacolony.md @@ -455,6 +455,24 @@ Put colony network mining into recovery mode. Can only be called by user with re +### ▸ `exchangeTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, address _token, uint256 _amount)` + +Exchange funds between two tokens, potentially between chains + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which I have the permission to take this action +|_childSkillIndex|uint256|The child index in `_permissionDomainId` where we can find `_domainId` +|_domainId|uint256|Id of the domain +|_txdata|bytes|Transaction data for the exchange +|_value|uint256|Value of the transaction +|_token|address|Address of the token, `0x0` value indicates Ether +|_amount|uint256|Amount of tokens to exchange + + ### ▸ `executeMetaTransaction(address userAddress, bytes memory payload, bytes32 sigR, bytes32 sigS, uint8 sigV):bytes returnData` Executes a metatransaction targeting this contract @@ -1421,6 +1439,21 @@ Get the owner of the contract |---|---|---| |owner|address|The owner of the contract +### ▸ `recordClaimedFundsFromBridge(uint256 _chainId, address _token, uint256 _domainId, uint256 _amount)` + +Used by the bridge to indicate that funds have been claimed on another chain. + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_chainId|uint256|Chain id of the chain where the funds were claimed +|_token|address|Address of the token, `0x0` value indicates Ether +|_domainId|uint256|Id of the domain where the funds were claimed +|_amount|uint256|Amount of funds claimed + + ### ▸ `registerColonyLabel(string memory colonyName, string memory orbitdb)` Register colony's ENS label. diff --git a/helpers/constants.js b/helpers/constants.js index a13f2cbbd9..279e1a4bda 100644 --- a/helpers/constants.js +++ b/helpers/constants.js @@ -82,6 +82,7 @@ const MAINNET_CHAINID = 1; const FORKED_MAINNET_CHAINID = 2656691; const CREATEX_ADDRESS = "0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed"; +const LIFI_ADDRESS = "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE"; const NETWORK_ADDRESS = "0x777760996135F0791E2e1a74aFAa060711197777"; module.exports = { @@ -142,4 +143,5 @@ module.exports = { FORKED_MAINNET_CHAINID, CREATEX_ADDRESS, NETWORK_ADDRESS, + LIFI_ADDRESS, }; diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index e1e50c66b9..e9198cac47 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -30,7 +30,9 @@ const IColony = artifacts.require("IColony"); const ProxyColonyNetwork = artifacts.require("ProxyColonyNetwork"); const ProxyColony = artifacts.require("ProxyColony"); const MetaTxToken = artifacts.require("MetaTxToken"); +const LiFiFacetProxyMock = artifacts.require("LiFiFacetProxyMock"); // const { assert } = require("console"); + const { setupBridging, setForeignBridgeData, setHomeBridgeData } = require("../../scripts/setup-bridging-contracts"); const { @@ -41,6 +43,7 @@ const { CREATEX_ADDRESS, NETWORK_ADDRESS, HASHZERO, + LIFI_ADDRESS, } = require("../../helpers/constants"); const { forwardTime, checkErrorRevertEthers, revert, snapshot, evmChainIdToWormholeChainId } = require("../../helpers/test-helper"); const ReputationMinerTestWrapper = require("../../packages/reputation-miner/test/ReputationMinerTestWrapper"); @@ -1481,6 +1484,73 @@ contract("Cross-chain", (accounts) => { await tx.wait(); await p2; }); + + it("can exchange tokens in a domain on one chain for tokens in a domain on another chain via LiFi", async () => { + const homeTokenFactory = new ethers.ContractFactory(MetaTxToken.abi, MetaTxToken.bytecode, ethersHomeSigner); + const homeToken = await homeTokenFactory.deploy("Test Token", "TT", 18); + + expect(homeToken.address).to.not.equal(foreignToken.address); + await (await homeToken.unlock()).wait(); + await (await foreignToken.unlock()).wait(); + + await homeToken["mint(address,uint256)"](homeColony.address, ethers.utils.parseEther("100")); + await homeColony.claimColonyFunds(homeToken.address); + await homeColony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); + const domain1 = await homeColony.getDomain(1); + const domain2 = await homeColony.getDomain(2); + + let tx = await homeColony["moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)"]( + 1, + UINT256_MAX_ETHERS, + 1, + UINT256_MAX_ETHERS, + 0, + domain1.fundingPotId, + domain2.fundingPotId, + ethers.utils.parseEther("50"), + homeChainId, + homeToken.address, + ); + await tx.wait(); + + const domain2ReceiverAddress = await homeColonyNetwork.getDomainTokenReceiverAddress(proxyColony.address, 2); + + const lifi = new ethers.Contract(LIFI_ADDRESS, LiFiFacetProxyMock.abi, ethersHomeSigner); + + const txdata = lifi.interface.encodeFunctionData("swapTokensMock(uint256,address,uint256,address,address,uint256)", [ + homeChainId, + homeToken.address, + foreignChainId, + foreignToken.address, + domain2ReceiverAddress, + ethers.utils.parseEther("50"), + ]); + + tx = await homeColony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, homeToken.address, ethers.utils.parseEther("50")); + + const receipt = await tx.wait(); + const swapEvent = receipt.events + .filter((e) => e.address === LIFI_ADDRESS) + .map((e) => lifi.interface.parseLog(e)) + .filter((e) => e.name === "SwapTokens")[0]; + expect(swapEvent).to.not.be.undefined; + + // Okay, so we saw the SwapTokens event. Let's do vaguely what it said for the test, + // but in practise this would be the responsibility of whatever entity we've paid to do it + // through LiFi. + await foreignToken["mint(address,uint256)"](swapEvent.args._toAddress, swapEvent.args._amount); // Implicit 1:1 exchange rate + + // Now claim the tokens on the foreign chain + const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + tx = await proxyColony.claimTokensForDomain(foreignToken.address, 2, { gasLimit: 1000000 }); + await tx.wait(); + await p; + + // See if bookkeeping was tracked correctly + const domain = await colony.getDomain(2); + const balance = await colony.getFundingPotProxyBalance(domain.fundingPotId, foreignChainId, foreignToken.address); + expect(balance.toHexString()).to.equal(ethers.utils.parseEther("50").toHexString()); + }); }); describe("making arbitrary transactions on another chain", async () => { diff --git a/test/deploy-proxy-network-fixture.js b/test/deploy-proxy-network-fixture.js index 1b5100041a..1e3b5b44ab 100644 --- a/test/deploy-proxy-network-fixture.js +++ b/test/deploy-proxy-network-fixture.js @@ -3,6 +3,7 @@ const EtherRouter = artifacts.require("EtherRouter"); const EtherRouterCreate3 = artifacts.require("EtherRouterCreate3"); const Resolver = artifacts.require("Resolver"); +const DomainTokenReceiver = artifacts.require("DomainTokenReceiver"); const truffleContract = require("@truffle/contract"); const createXABI = require("../lib/createx/artifacts/src/ICreateX.sol/ICreateX.json"); @@ -55,4 +56,11 @@ module.exports = async () => { const proxyColonyNetwork = await ProxyColonyNetwork.at(etherRouter.address); await proxyColonyNetwork.setProxyColonyResolverAddress(resolver.address); + + // Set up the resolver for DomainTokenReceiver + + resolver = await Resolver.new(); + const domainTokenReceiverImplementation = await DomainTokenReceiver.new(); + await setupEtherRouter("common", "DomainTokenReceiver", { DomainTokenReceiver: domainTokenReceiverImplementation.address }, resolver); + await proxyColonyNetwork.setDomainTokenReceiverResolver(resolver.address); }; diff --git a/test/truffle-fixture.js b/test/truffle-fixture.js index 785afb9ea1..f43e1536bd 100644 --- a/test/truffle-fixture.js +++ b/test/truffle-fixture.js @@ -27,6 +27,7 @@ const ColonyNetworkSkills = artifacts.require("ColonyNetworkSkills"); const IColonyNetwork = artifacts.require("IColonyNetwork"); const ENSRegistry = artifacts.require("ENSRegistry"); +const LiFiFacetProxyMock = artifacts.require("LiFiFacetProxyMock"); const ReputationMiningCycle = artifacts.require("ReputationMiningCycle"); const ReputationMiningCycleRespond = artifacts.require("ReputationMiningCycleRespond"); @@ -67,6 +68,7 @@ const assert = require("assert"); const ethers = require("ethers"); const { soliditySha3 } = require("web3-utils"); const truffleContract = require("@truffle/contract"); +const { setCode } = require("@nomicfoundation/hardhat-network-helpers"); const createXABI = require("../lib/createx/artifacts/src/ICreateX.sol/ICreateX.json"); const { resetAlreadyDeployedVersionTracking } = require("../scripts/deployOldUpgradeableVersion"); @@ -190,6 +192,9 @@ async function setupColonyNetwork() { const etherRouter = await EtherRouterCreate3.at(tx.logs.filter((log) => log.event === "ContractCreation")[0].args.newContract); EtherRouter.setAsDeployed(etherRouter); + // Deploy LiFiMock to LiFi address + await setCode("0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE", LiFiFacetProxyMock.deployedBytecode); + await setupUpgradableColonyNetwork( etherRouter, resolver, From 0cbc6827e54a14aeae8befe3d41925d559c4c621 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Thu, 19 Sep 2024 14:29:39 +0100 Subject: [PATCH 32/72] Fixes post-rebase --- packages/wormhole-relayer/index.ts | 216 ------- scripts/mockGuardianSpy.ts | 102 ++- test/cross-chain/cross-chain.js | 990 +---------------------------- 3 files changed, 76 insertions(+), 1232 deletions(-) diff --git a/packages/wormhole-relayer/index.ts b/packages/wormhole-relayer/index.ts index 15eddc05ac..2285266ce3 100644 --- a/packages/wormhole-relayer/index.ts +++ b/packages/wormhole-relayer/index.ts @@ -114,7 +114,6 @@ const loader = new TruffleLoader({ let destinationBridge; -<<<<<<< HEAD if (vaa.emitterChain === CHAIN_ID_ARBITRUM_SEPOLIA) { destinationBridge = colonyBridges[CHAIN_ID_SEPOLIA]; } else if (vaa.emitterChain === CHAIN_ID_SEPOLIA) { @@ -122,82 +121,6 @@ const loader = new TruffleLoader({ } else { console.log("Unknown chain", vaa.emitterChain); return next(); -||||||| parent of 45f052f5 (setup-bridging-contracts can handle multiple remote chains) - app.spy("localhost:7073"); - // app.useStorage(store); - // app.logger(console); - - // Set up middleware - // app.use(logging(console)); // <-- logging middleware - app.use(providers(config)); - // app.use(stagingArea()); - // app.use(sourceTx()); - - - // const colonyBridgeAddresses = { - // [CHAIN_ID_ARBITRUM_SEPOLIA]: "0x633899227A3BC1f79de097149E1E3C8097c07b1a", - // [CHAIN_ID_SEPOLIA]: "0x161944B5601a7d3004E20d4Ca823F710838Ea1be", - // }; - - const { TruffleLoader } = require("../package-utils"); - const path = require("path"); - const loader = new TruffleLoader({ - contractRoot: path.resolve(__dirname, "..", "..", "artifacts", "contracts") - }); - - const colonyBridges = {}; - const colonyBridgeContractDef = await loader.load({ contractDir: "bridging", contractName: "WormholeBridgeForColony" }); - const ethers = require('ethers'); - const privateKey = "0xfe6066af949ec3c2c88ac10f47907c6d4e200c37b28b5af49e7d0ffd5c301c5c"; - for (const chainId of Object.keys(config.chains)) { - const colonyBridgeAddress = config.chains[chainId].colonyBridgeAddress; - const providerAddress = config.chains[chainId].endpoints[0]; - const wallet = new ethers.Wallet(privateKey, new RetryProvider(providerAddress)); - const nonceManager = new NonceManager(wallet); - - colonyBridges[chainId] = new ethers.Contract(colonyBridgeAddress, colonyBridgeContractDef.abi, nonceManager); -======= - app.spy("localhost:7073"); - // app.useStorage(store); - // app.logger(console); - - // Set up middleware - // app.use(logging(console)); // <-- logging middleware - app.use(providers(config)); - // app.use(stagingArea()); - // app.use(sourceTx()); - - - // const colonyBridgeAddresses = { - // [CHAIN_ID_ARBITRUM_SEPOLIA]: "0x633899227A3BC1f79de097149E1E3C8097c07b1a", - // [CHAIN_ID_SEPOLIA]: "0x161944B5601a7d3004E20d4Ca823F710838Ea1be", - // }; - - const { TruffleLoader } = require("../package-utils"); - const path = require("path"); - const loader = new TruffleLoader({ - contractRoot: path.resolve(__dirname, "..", "..", "artifacts", "contracts") - }); - - const colonyBridges = {}; - const colonyBridgeContractDef = await loader.load({ contractDir: "bridging", contractName: "WormholeBridgeForColony" }); - const ethers = require('ethers'); - const privateKey = "0xfe6066af949ec3c2c88ac10f47907c6d4e200c37b28b5af49e7d0ffd5c301c5c"; - for (const chainId of Object.keys(config.chains)) { - const colonyBridgeAddress = config.chains[chainId].colonyBridgeAddress; - const providerAddress = config.chains[chainId].endpoints[0]; - const wallet = new ethers.Wallet(privateKey, new RetryProvider(providerAddress)); - const nonceManager = new NonceManager(wallet); - - colonyBridges[chainId] = new ethers.Contract(colonyBridgeAddress, colonyBridgeContractDef.abi, nonceManager); - const networkDetails = await wallet.provider.getNetwork(); - if (networkDetails.chainId !== config.chains[chainId].evmChainId) { - console.log('Network details do not match config for chain', chainId); - console.log('Got an evmChainId of', networkDetails.chainId, 'but expected', config.chains[chainId].evmChainId); - console.log('Exiting'); - process.exit(1); - } ->>>>>>> 45f052f5 (setup-bridging-contracts can handle multiple remote chains) } try { @@ -224,145 +147,6 @@ const loader = new TruffleLoader({ // add and configure any other middleware .. -<<<<<<< HEAD // start app, blocks until unrecoverable error or process is stopped await app.listen(); })(); -||||||| parent of 45f052f5 (setup-bridging-contracts can handle multiple remote chains) - // add a filter with a callback that will be - // invoked on finding a VAA that matches the filter - - const colonyBridgeAddresses: { - [chainid: string]: string - } = {}; - - Object.keys(config.chains).forEach((chainid) => colonyBridgeAddresses[chainid] = config.chains[chainid].colonyBridgeAddress); - app.multiple( colonyBridgeAddresses, - async (ctx, next) => { - // console.log('callback'); - const vaa = ctx.vaa; - if (!vaa) { - return next(); - } - const hash = ctx.sourceTxHash; - // console.log(vaa); - console.log( - `Got a VAA with sequence: ${vaa.sequence} from with txhash: ${hash}`, - ); - - let destinationBridge; - - if (vaa.emitterChain === CHAIN_ID_ARBITRUM_SEPOLIA) { - destinationBridge = colonyBridges[CHAIN_ID_SEPOLIA]; - } else if (vaa.emitterChain === CHAIN_ID_SEPOLIA) { - destinationBridge = colonyBridges[CHAIN_ID_ARBITRUM_SEPOLIA]; - } else { - console.log('Unknown chain', vaa.emitterChain); - return next(); - } - - try { - // TODO: Explicit gas limit is a nod to tests... - const tx = await destinationBridge.receiveMessage(ctx.vaaBytes, { gasLimit: 1000000 }); - const r = await tx.wait(); - console.log('bridged with txhash' + tx.hash) - } catch(err) { - console.log("ERROR", err); - console.log('trying estimate gas with', err.transaction.to, err.transaction.data); - try { - - const errEst = await destinationBridge.provider.estimateGas({ - to: err.transaction.to, - data: err.transaction.data - }); - console.log('errEst', errEst.toString()); - } catch(err2){ - console.log('ERROR2', err2); - } - } - - next(); - }, - ); - - // add and configure any other middleware .. - - // start app, blocks until unrecoverable error or process is stopped - await app.listen(); - - })(); - -======= - // add a filter with a callback that will be - // invoked on finding a VAA that matches the filter - - const colonyBridgeAddresses: { - [chainid: string]: string - } = {}; - - Object.keys(config.chains).forEach((chainid) => colonyBridgeAddresses[chainid] = config.chains[chainid].colonyBridgeAddress); - app.multiple( colonyBridgeAddresses, - async (ctx, next) => { - // console.log('callback'); - const vaa = ctx.vaa; - if (!vaa) { - return next(); - } - const hash = ctx.sourceTxHash; - const [destinationEvmChainId, destinationAddress, payload] = (new ethers.utils.AbiCoder).decode(['uint256', 'address', 'bytes'],`0x${vaa.payload.toString('hex')}`); - - console.log( - `Got a VAA with sequence: ${vaa.sequence} from with txhash: ${hash}`, - ); - - const destinationChainConfig = Object.values(config.chains).find((c) => c.evmChainId === destinationEvmChainId.toNumber()) - if (!destinationChainConfig) { - console.log('No destination chain config found for chain id', destinationEvmChainId.toNumber()); - return next(); - } - - if (!destinationChainConfig.payForGas) { - console.log('We do not pay for gas on destination chain. Skipping'); - return next(); - } - - const destinationWormholeId = Object.keys(config.chains).find((wormholeChainId) => { return config.chains[wormholeChainId].evmChainId === destinationEvmChainId.toNumber() }); - if (!destinationWormholeId) { - console.log('No wormhole chain id found for destination chain id', destinationEvmChainId.toNumber()); - return next(); - } - - const destinationBridge = colonyBridges[destinationWormholeId]; - - try { - // TODO: Explicit gas limit is a nod to tests... - const tx = await destinationBridge.receiveMessage(ctx.vaaBytes, { gasLimit: 1000000 }); - const r = await tx.wait(); - console.log('bridged with txhash' + tx.hash) - } catch(err) { - console.log("ERROR", err); - console.log('trying estimate gas with', err.transaction.to, err.transaction.data); - try { - - const errEst = await destinationBridge.provider.estimateGas({ - to: err.transaction.to, - data: err.transaction.data - }); - console.log('errEst', errEst.toString()); - } catch(err2){ - console.log('ERROR2', err2); - } - } - - next(); - }, - ); - - // add and configure any other middleware .. - - // start app, blocks until unrecoverable error or process is stopped - await app.listen(); - - })(); - ->>>>>>> 45f052f5 (setup-bridging-contracts can handle multiple remote chains) diff --git a/scripts/mockGuardianSpy.ts b/scripts/mockGuardianSpy.ts index 949d26394a..819e256e33 100644 --- a/scripts/mockGuardianSpy.ts +++ b/scripts/mockGuardianSpy.ts @@ -1,67 +1,65 @@ -/* eslint-disable import/no-extraneous-dependencies */ - import { Server, ServerCredentials } from "@grpc/grpc-js"; -import { ServerWritableStreamImpl } from "@grpc/grpc-js/build/src/server-call"; - -import { ethers } from "ethers"; -import { RetryProvider } from "../packages/package-utils"; +import { Contract, Signer, ContractReceipt, ethers } from "ethers"; +import { ServerWritableStreamImpl } from "@grpc/grpc-js/build/src/server-call"; import { FilterEntry, SpyRPCServiceService, SubscribeSignedVAARequest, SubscribeSignedVAAResponse, } from "../lib/wormhole/sdk/js-proto-node/src/spy/v1/spy"; -import { evmChainIdToWormholeChainId } from "../helpers/test-helper"; +// const { RetryProvider } = require("../packages/package-utils"); +import { RetryProvider } from "../packages/package-utils"; // Random key +// eslint-disable-next-line import/no-unresolved import { abi as bridgeAbi } from "../artifacts/contracts/testHelpers/WormholeMock.sol/WormholeMock.json"; +// eslint-disable-next-line import/no-unresolved import { abi as wormholeBridgeForColonyAbi } from "../artifacts/contracts/bridging/WormholeBridgeForColony.sol/WormholeBridgeForColony.json"; function ethereumAddressToWormholeAddress(address: string) { return ethers.utils.hexZeroPad(ethers.utils.hexStripZeros(ethers.utils.hexlify(address)), 32); } -type QueueEntry = [ethers.Contract, string, number, number, string, number, number]; +type QueueEntry = [Contract, string, number, number, string, number, number]; class MockGuardianSpy { homeRpc: string; - foreignRpc: string; + foreignRpcs: string[]; homeBridgeAddress: string; - foreignBridgeAddress: string; + foreignBridgeAddresses: string[]; homeColonyBridgeAddress: string; - foreignColonyBridgeAddress: string; + foreignColonyBridgeAddresses: string[]; - homeBridge: ethers.Contract; + homeBridge: Contract; - foreignBridge: ethers.Contract; + foreignBridges: Contract[]; - homeWormholeBridgeForColony: ethers.Contract; + homeWormholeBridgeForColony: Contract; - foreignWormholeBridgeForColony: ethers.Contract; + foreignWormholeBridgesForColony: Contract[]; - skipCount = 0; + skipCount: number = 0; queue: QueueEntry[] = []; skipped: QueueEntry[] = []; - locked = false; - - bridgingPromiseCount = 0; + locked: boolean = false; - resolveBridgingPromise: (tx: ethers.Transaction) => void; + bridgingPromiseCount: number = 0; - signerHome: ethers.Signer; + resolveBridgingPromise: (tx: ContractReceipt) => void; - signerForeign: ethers.Signer; + signerHome: Signer; + // signerForeign: any; server: Server; subscription: ServerWritableStreamImpl; @@ -83,11 +81,11 @@ class MockGuardianSpy { */ constructor( homeRpc: string, - foreignRpc: string, + foreignRpcs: string[], homeBridgeAddress: string, - foreignBridgeAddress: string, + foreignBridgeAddresses: string[], homeColonyBridgeAddress: string, - foreignColonyBridgeAddress: string, + foreignColonyBridgeAddresses: string[], ) { this.homeRpc = homeRpc; this.foreignRpcs = foreignRpcs; @@ -124,7 +122,7 @@ class MockGuardianSpy { }); } - static async getTransactionFromAddressWithNonce(provider: ethers.providers.Provider, address: string, nonce: number) { + static async getTransactionFromAddressWithNonce(provider: Contract["provider"], address: string, nonce: number) { const currentBlock = await provider.getBlockNumber(); for (let i = currentBlock; i > 0; i -= 1) { const block = await provider.getBlock(i); @@ -146,6 +144,7 @@ class MockGuardianSpy { const timestamp = Math.floor(Date.now() / 1000); const emitterChainId = chainId; const emitterAddress = ethereumAddressToWormholeAddress(sender); + // let signatures: any[] = []; // const vaa = await this.homeBridge.buildVM( // version, @@ -175,6 +174,7 @@ class MockGuardianSpy { "7777000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007777"; return vaaHeader + vaaBody.toString("hex").slice(2); + // return signatures.toString('hex').slice(2); } setupForeignBridges(foreignRpc, foreignBridgeAddress, foreignColonyBridgeAddress) { @@ -186,7 +186,6 @@ class MockGuardianSpy { this.foreignWormholeBridgesForColony.push(foreignWormholeBridgeForColony); } - async getColonyBridgeWithChainId(chainId) { if ((await this.homeBridge.provider.getNetwork()).chainId === chainId) { return this.homeBridge; @@ -197,18 +196,20 @@ class MockGuardianSpy { } } throw new Error("No bridge found for chainId"); - }; + } - getWormholeChainId(chainId) { + static getWormholeChainId(chainId) { // Due to limitations, for local testing, our wormhole chainIDs have to be 'real' wormhole chainids. // So I've decreed that for chainId 256669100, we use 10003 (which is really arbitrum sepolia) // and for chainId 256669101, we use 10002 (which is really sepolia). // This isn't ideal, but it's the best solution I have for now if (chainId === 265669100) { return 10003; - } else if (chainId === 265669101) { + } + if (chainId === 265669101) { return 10002; - } else if (chainId === 265669102) { + } + if (chainId === 265669102) { return 10005; } throw new Error("Unsupported chainId"); @@ -224,13 +225,8 @@ class MockGuardianSpy { } } - this.signerHome = new RetryProvider(this.homeRpc).getSigner(); - this.signerForeign = new RetryProvider(this.foreignRpc).getSigner(); - - this.homeBridge = new ethers.Contract(this.homeBridgeAddress, bridgeAbi, this.signerHome); - this.foreignBridge = new ethers.Contract(this.foreignBridgeAddress, bridgeAbi, this.signerForeign); - this.homeWormholeBridgeForColony = new ethers.Contract(this.homeColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerHome); - this.foreignWormholeBridgeForColony = new ethers.Contract(this.foreignColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerForeign); + this.foreignBridges = []; + this.foreignWormholeBridgesForColony = []; this.signerHome = new RetryProvider(this.homeRpc).getSigner(); // this.signerForeign = new RetryProvider(this.foreignRpc).getSigner(); @@ -238,7 +234,7 @@ class MockGuardianSpy { // this.foreignBridge = new ethers.Contract(this.foreignBridgeAddress, bridgeAbi, this.signerForeign); this.homeWormholeBridgeForColony = new ethers.Contract(this.homeColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerHome); // this.foreignWormholeBridgeForColony = new ethers.Contract(this.foreignColonyBridgeAddress, wormholeBridgeForColonyAbi, this.signerForeign); - for (let i = 0; i < this.foreignRpcs.length; i++) { + for (let i = 0; i < this.foreignRpcs.length; i += 1) { this.setupForeignBridges(this.foreignRpcs[i], this.foreignBridgeAddresses[i], this.foreignColonyBridgeAddresses[i]); } this.skipCount = 0; @@ -247,15 +243,12 @@ class MockGuardianSpy { this.skipped = []; this.locked = false; this.homeBridge.on("LogMessagePublished", async (sender, sequence, nonce, payload, consistencyLevel) => { - const { chainId } = await this.signerHome.provider.getNetwork(); - // Due to limitations, for local testing, our wormhole chainIDs have to be 'real' wormhole chainids. - // So I've decreed that for chainId 256669100, we use 10003 (which is really arbitrum sepolia) - // and for chainId 256669101, we use 10002 (which is really sepolia). - // This isn't ideal, but it's the best solution I have for now - const wormholeChainId = evmChainIdToWormholeChainId(chainId); + try { + const { chainId } = await this.signerHome.provider.getNetwork(); + const [destinationEvmChainId] = new ethers.utils.AbiCoder().decode(["uint256", "address", "bytes"], `${payload.toString("hex")}`); const destinationBridge = await this.getColonyBridgeWithChainId(destinationEvmChainId.toNumber()); - const wormholeChainId = this.getWormholeChainId(chainId); + const wormholeChainId = MockGuardianSpy.getWormholeChainId(chainId); if (this.skipCount > 0) { this.skipped.push([destinationBridge, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]); @@ -270,19 +263,16 @@ class MockGuardianSpy { } }); - this.foreignBridge.on("LogMessagePublished", async (sender, sequence, nonce, payload, consistencyLevel) => { - const { chainId } = await this.signerForeign.provider.getNetwork(); - // Due to limitations, for local testing, our wormhole chainIDs have to be 'real' wormhole chainids. - // So I've decreed that for chainId 256669100, we use 10003 (which is really arbitrum sepolia) - // and for chainId 256669101, we use 10002 (which is really sepolia). - // This isn't ideal, but it's the best solution I have for now - const wormholeChainId = evmChainIdToWormholeChainId(chainId); + for (const foreignBridge of this.foreignBridges) { + foreignBridge.on("LogMessagePublished", async (sender, sequence, nonce, payload, consistencyLevel) => { + const { chainId } = await foreignBridge.provider.getNetwork(); + const [destinationEvmChainId] = new ethers.utils.AbiCoder().decode(["uint256", "address", "bytes"], `${payload.toString("hex")}`); if (destinationEvmChainId.toNumber() !== 265669100) { throw new Error("Unsupported chainId - change assumptions in mockGuardianSpy.ts"); } - const wormholeChainId = this.getWormholeChainId(chainId); + const wormholeChainId = MockGuardianSpy.getWormholeChainId(chainId); if (this.skipCount > 0) { this.skipped.push([this.homeWormholeBridgeForColony, sender, sequence, nonce, payload, consistencyLevel, wormholeChainId]); @@ -328,12 +318,15 @@ class MockGuardianSpy { const relayerNonce = await bridge.provider.getTransactionCount(this.relayerAddress, "pending"); this.subscription.write({ vaaBytes: Buffer.from(vaa.slice(2), "hex") }); + let newRelayerNonce = -1; while (newRelayerNonce <= relayerNonce) { newRelayerNonce = await bridge.provider.getTransactionCount(this.relayerAddress, "pending"); } tx = await MockGuardianSpy.getTransactionFromAddressWithNonce(bridge.provider, this.relayerAddress, relayerNonce); + } else { + console.log("not sending, didnt pass filter"); } this.bridgingPromiseCount -= 1; @@ -366,7 +359,6 @@ class MockGuardianSpy { const relayerNonce = await bridge.provider.getTransactionCount(this.relayerAddress, "pending"); this.subscription.write({ vaaBytes: Buffer.from(vaa.slice(2), "hex") }); - let newRelayerNonce = -1; while (newRelayerNonce <= relayerNonce) { newRelayerNonce = await bridge.provider.getTransactionCount(this.relayerAddress, "pending"); diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index e9198cac47..de8a054f58 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -69,6 +69,7 @@ contract("Cross-chain", (accounts) => { let homeChainId; let foreignChainId; let wormholeHomeChainId; + // let wormholeForeignChainId; let resetRelayer; let homeMetacolony; @@ -136,7 +137,7 @@ contract("Cross-chain", (accounts) => { // const foreignNetworkId = await ethersForeignSigner.provider.send("net_version", []); foreignChainId = await ethersForeignSigner.provider.send("eth_chainId", []); - wormholeForeignChainId = evmChainIdToWormholeChainId(foreignChainId); + // wormholeForeignChainId = evmChainIdToWormholeChainId(foreignChainId); // Deploy shell colonyNetwork to whichever chain truffle hasn't already deployed to. // try { @@ -228,7 +229,7 @@ contract("Cross-chain", (accounts) => { // const latestSkillId = await remoteColonyNetwork.getSkillCount(); // const alreadyBridged = await homeColonyNetwork.getBridgedSkillCounts(foreignChainId); // for (let i = alreadyBridged.add(1); i <= latestSkillId; i = i.add(1)) { - // const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + // const p = guardianSpy.getPromiseForNextBridgedTransaction(); // tx = await remoteColonyNetwork.bridgeSkillIfNotMiningChain(i); // await tx.wait(); // await p; @@ -259,7 +260,7 @@ contract("Cross-chain", (accounts) => { // Set up a colony on the home chain. That may or may not be the truffle chain... homeColony = await setupColony(homeColonyNetwork); - // const p = bridgeMonitor.getPromiseForNextBridgedTransaction(2); + // const p = guardianSpy.getPromiseForNextBridgedTransaction(2); // remoteColony = await setupColony(remoteColonyNetwork); // await p; }); @@ -326,7 +327,7 @@ contract("Cross-chain", (accounts) => { // Now have the colony request a deployment of a shell on the other chain. - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p = guardianSpy.getPromiseForNextBridgedTransaction(); const deployedColony = new ethers.Contract(colonyAddress, IColony.abi, ethersHomeSigner); @@ -407,7 +408,7 @@ contract("Cross-chain", (accounts) => { await tx.wait(); // Can now call - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p = guardianSpy.getPromiseForNextBridgedTransaction(); const tx3 = await homeMetacolony2.callProxyNetwork(foreignChainId, [payload]); await tx3.wait(); await p; @@ -424,7 +425,7 @@ contract("Cross-chain", (accounts) => { it("setColonyBridgeAddress on Proxy Network can be used across the bridge", async () => { const bridgeAddress = await remoteColonyNetwork.colonyBridgeAddress(); const payload = remoteColonyNetwork.interface.encodeFunctionData("setColonyBridgeAddress", [ADDRESS_ZERO]); - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p = guardianSpy.getPromiseForNextBridgedTransaction(); const tx = await homeMetacolony.callProxyNetwork(foreignChainId, [payload]); await tx.wait(); await p; @@ -534,674 +535,6 @@ contract("Cross-chain", (accounts) => { }); }); - describe.skip("when adding skills on another chain", async () => { - it("can create a skill on another chain and it's reflected on the home chain", async () => { - // See skills on home chain - const beforeCount = await homeColonyNetwork.getBridgedSkillCounts("0x0fd5c9ed"); - - const p = guardianSpy.getPromiseForNextBridgedTransaction(); - - // Create a skill on foreign chain - // await foreignColony.addDomain(1); - const foreignBeforeCount = await foreignColonyNetwork.getSkillCount(); - const tx = await foreignColony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); - await tx.wait(); - - const foreignAfterCount = await foreignColonyNetwork.getSkillCount(); - expect(foreignBeforeCount.add(1).toHexString()).to.equal(foreignAfterCount.toHexString()); - await p; - - // Check reflected on home chain - const afterCount = await homeColonyNetwork.getBridgedSkillCounts("0x0fd5c9ed"); - expect(beforeCount.add(1).toHexString()).to.equal(afterCount.toHexString()); - }); - - it("addSkillFromBridge cannot be called by a non-bridge address", async () => { - const tx = await homeColonyNetwork.addSkillFromBridge(0, 0, { - gasLimit: 1000000, - }); - await checkErrorRevertEthers(tx.wait(), "colony-network-caller-must-be-colony-bridge"); - }); - - it("addPendingSkill doesn't create skills that haven't been bridged", async () => { - const homeSkillCount = await homeColonyNetwork.getBridgedSkillCounts(foreignChainId); - const tx = await homeColonyNetwork.addPendingSkill(homeSkillCount.add(1), { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-no-such-bridged-skill"); - }); - - it("if a skill is bridged out-of-order, it's added to the pending mapping", async () => { - guardianSpy.skipCount = 1; - // Create a skill on the foreign chain - let tx = await foreignColony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); - await tx.wait(); - - await guardianSpy.waitUntilSkipped(); - - const foreignDomain = await foreignColony.getDomain(1); - - let p = guardianSpy.getPromiseForNextBridgedTransaction(); - - // Create another skill on the foreign chain - // Bridge the latter without bridging the former - tx = await foreignColony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); - await tx.wait(); - const foreignSkillCount = await foreignColonyNetwork.getSkillCount(); - - await p; - - // Check it's pending - const pendingAddition = await homeColonyNetwork.getPendingSkillAddition(foreignChainId, foreignSkillCount); - - expect(pendingAddition.toHexString()).to.equal(foreignDomain.skillId.toHexString()); - - // Need to clean up - p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await foreignColonyNetwork.bridgeSkillIfNotMiningChain(foreignSkillCount.sub(1)); - await tx.wait(); - await p; - tx = await homeColonyNetwork.addPendingSkill(foreignSkillCount, { gasLimit: 1000000 }); - await tx.wait(); - }); - - it("if a skill is bridged out-of-order, it can be added once the earlier skills are bridged ", async () => { - guardianSpy.skipCount = 1; - // Create a skill on the foreign chain - let tx = await foreignColony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); - await tx.wait(); - await guardianSpy.waitUntilSkipped(); - - let p = guardianSpy.getPromiseForNextBridgedTransaction(); - // Create another skill on the foreign chain - // Bridge the latter without bridging the former - tx = await foreignColony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); - await tx.wait(); - const foreignSkillCount = await foreignColonyNetwork.getSkillCount(); - await p; - - // Try to add - tx = await homeColonyNetwork.addPendingSkill(foreignSkillCount, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-not-next-bridged-skill"); - - // Bridge the next skill - p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await foreignColonyNetwork.bridgeSkillIfNotMiningChain(foreignSkillCount.sub(1)); - await tx.wait(); - await p; - - // Add the pending skill - tx = await homeColonyNetwork.addPendingSkill(foreignSkillCount, { gasLimit: 1000000 }); - await tx.wait(); - - // Check it was added - const homeSkillCount = await homeColonyNetwork.getBridgedSkillCounts(foreignChainId); - expect(homeSkillCount.toHexString()).to.equal(foreignSkillCount.toHexString()); - - // And removed from pending - const pendingAddition = await homeColonyNetwork.getPendingSkillAddition(foreignChainId, foreignSkillCount); - expect(pendingAddition.toHexString()).to.equal("0x00"); - }); - - it("if a skill that was pending is repeatedly bridged, the resuling transaction fails after the first time", async () => { - guardianSpy.skipCount = 1; - // Create a skill on the foreign chain - let tx = await foreignColony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); - await tx.wait(); - await guardianSpy.waitUntilSkipped(); - - let p = guardianSpy.getPromiseForNextBridgedTransaction(); - // Create another skill on the foreign chain - // Bridge the latter without bridging the former - tx = await foreignColony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); - await tx.wait(); - const foreignSkillCount = await foreignColonyNetwork.getSkillCount(); - await p; - - // Try to add - tx = await homeColonyNetwork.addPendingSkill(foreignSkillCount, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-not-next-bridged-skill"); - - // Bridge the next skill - p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await foreignColonyNetwork.bridgeSkillIfNotMiningChain(foreignSkillCount.sub(1)); - await tx.wait(); - await p; - - // Add the pending skill - tx = await homeColonyNetwork.addPendingSkill(foreignSkillCount, { gasLimit: 1000000 }); - await tx.wait(); - - // Adding again doesn't work - tx = await homeColonyNetwork.addPendingSkill(foreignSkillCount, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-not-next-bridged-skill"); - - // And bridging again doesn't work - p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await foreignColonyNetwork.bridgeSkillIfNotMiningChain(foreignSkillCount); - await tx.wait(); - await p; - - const pendingAddition = await homeColonyNetwork.getPendingSkillAddition(foreignChainId, foreignSkillCount); - expect(pendingAddition.toHexString()).to.equal("0x00"); - - const homeSkillCount = await homeColonyNetwork.getBridgedSkillCounts(foreignChainId); - expect(homeSkillCount.toHexString()).to.equal(foreignSkillCount.toHexString()); - }); - - it("can't bridge a skill that doesn't exist", async () => { - const skillCount = await foreignColonyNetwork.getSkillCount(); - const nonExistentSkillId = skillCount.add(10000000); - const tx = await foreignColonyNetwork.bridgeSkillIfNotMiningChain(nonExistentSkillId, { - gasLimit: 1000000, - }); - await checkErrorRevertEthers(tx.wait(), "colony-invalid-skill-id"); - }); - - it("if bridge is broken, bridging skill transaction doesn't revert (allowing e.g. domains to be created)", async () => { - let tx = await foreignBridge.setBridgeEnabled(false); - await tx.wait(); - const skillCount = await foreignColonyNetwork.getSkillCount(); - - tx = await foreignColonyNetwork.bridgeSkillIfNotMiningChain(skillCount, { - gasLimit: 1000000, - }); - let receipt = await tx.wait(); - expect(receipt.status).to.equal(1); - - tx = await foreignColony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); - receipt = await tx.wait(); - - let events = receipt.logs.map(function (log) { - try { - return foreignColonyNetwork.interface.parseLog(log); - } catch (e) { - // Return nothing - } - return null; - }); - events = events.filter((x) => x != null && x.eventFragment.name === "SkillCreationStored"); - expect(events.length).to.equal(1); - const event = events[0]; - expect(event.args[0].toString()).to.equal(skillCount.add(1).toString()); - }); - - it("colony root local skill structures end up the same on both chains", async () => { - const homeColonyRootLocalSkillId = await homeColony.getRootLocalSkill(); - let homeColonyRootLocalSkill = await homeColonyNetwork.getSkill(homeColonyRootLocalSkillId); - - const foreignColonyRootLocalSkillId = await foreignColony.getRootLocalSkill(); - let foreignColonyRootLocalSkill = await foreignColonyNetwork.getSkill(foreignColonyRootLocalSkillId); - - expect(homeColonyRootLocalSkill.nParents.toString()).to.equal(foreignColonyRootLocalSkill.nParents.toString()); - expect(homeColonyRootLocalSkill.nChildren.toString()).to.equal(foreignColonyRootLocalSkill.nChildren.toString()); - - let tx = await homeColony.addLocalSkill(); - await tx.wait(); - - const p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await foreignColony.addLocalSkill(); - await tx.wait(); - await p; - homeColonyRootLocalSkill = await homeColonyNetwork.getSkill(homeColonyRootLocalSkillId); - foreignColonyRootLocalSkill = await foreignColonyNetwork.getSkill(foreignColonyRootLocalSkillId); - - expect(homeColonyRootLocalSkill.nParents.toString()).to.equal(foreignColonyRootLocalSkill.nParents.toString()); - expect(homeColonyRootLocalSkill.nChildren.toString()).to.equal(foreignColonyRootLocalSkill.nChildren.toString()); - - let zeroSkill = await foreignColonyNetwork.getSkill(ethers.BigNumber.from(foreignChainId).mul(ethers.BigNumber.from(2).pow(128))); - expect(zeroSkill.nChildren.toNumber()).to.equal(0); - - zeroSkill = await homeColonyNetwork.getSkill(ethers.BigNumber.from(foreignChainId).mul(ethers.BigNumber.from(2).pow(128))); - expect(zeroSkill.nChildren.toNumber()).to.equal(0); - - zeroSkill = await homeColonyNetwork.getSkill(0); - expect(zeroSkill.nChildren.toNumber()).to.equal(0); - }); - }); - - describe.skip("while earning reputation on another chain", async () => { - it("reputation awards are ultimately reflected", async () => { - let p = guardianSpy.getPromiseForNextBridgedTransaction(); - // Emit reputation - await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1337"); - // See that it's bridged to the inactive log - await p; - const logAddress = await homeColonyNetwork.getReputationMiningCycle(false); - const reputationMiningCycleInactive = new ethers.Contract(logAddress, IReputationMiningCycle.abi, ethersHomeSigner); - - const len = await reputationMiningCycleInactive.getReputationUpdateLogLength(); - - const entry = await reputationMiningCycleInactive.getReputationUpdateLogEntry(len.sub(1)); - - expect(entry.amount.toHexString()).to.equal("0x1337"); - expect(entry.user).to.equal(accounts[0]); - expect(entry.colony).to.equal(foreignColony.address); - - const domain = await foreignColony.getDomain(1); - - expect(entry.skillId.toHexString()).to.equal(domain.skillId.toHexString()); - - // Advance mining cycle twice - await forwardTime(MINING_CYCLE_DURATION + CHALLENGE_RESPONSE_WINDOW_DURATION, undefined, web3HomeProvider); - await client.addLogContentsToReputationTree(); - await client.submitRootHash(); - await client.confirmNewHash(); - - await forwardTime(MINING_CYCLE_DURATION + CHALLENGE_RESPONSE_WINDOW_DURATION, undefined, web3HomeProvider); - await client.addLogContentsToReputationTree(); - await client.submitRootHash(); - await client.confirmNewHash(); - - // Check in state - const key = await ReputationMinerTestWrapper.getKey(foreignColony.address, entry.skillId, accounts[0]); - expect(client.reputations[key]).to.not.equal(undefined); - expect(ethers.BigNumber.from(client.reputations[key].slice(0, 66)).toHexString()).to.equal("0x1337"); - - // Bridge it - - p = guardianSpy.getPromiseForNextBridgedTransaction(); - const tx = await homeColonyNetwork.bridgeCurrentRootHash(foreignChainId); - await tx.wait(); - await p; - - // Check state bridged to host chain - const foreignChainRootHash = await foreignColonyNetwork.getReputationRootHash(); - const foreignNLeaves = await foreignColonyNetwork.getReputationRootHashNNodes(); - const homeChainRootHash = await homeColonyNetwork.getReputationRootHash(); - const homeNLeaves = await homeColonyNetwork.getReputationRootHashNNodes(); - - expect(foreignChainRootHash).to.equal(homeChainRootHash); - expect(homeNLeaves.toHexString()).to.equal(foreignNLeaves.toHexString()); - }); - - it("if bridge disabled, reputation emissions are stored to be reemitted later", async () => { - let tx = await foreignBridge.setBridgeEnabled(false); - await tx.wait(); - const bridgedReputationUpdateCountBefore = await foreignColonyNetwork.getBridgedReputationUpdateCount(foreignChainId, foreignColony.address); - tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1337"); - await tx.wait(); - - // See it was stored for later - const bridgedReputationUpdateCountAfter = await foreignColonyNetwork.getBridgedReputationUpdateCount(foreignChainId, foreignColony.address); - expect(bridgedReputationUpdateCountAfter.sub(bridgedReputationUpdateCountBefore).toNumber()).to.equal(1); - }); - - it("if bridge disabled, cannot bridge current state", async () => { - let tx = await homeBridge.setBridgeEnabled(false); - await tx.wait(); - tx = await homeColonyNetwork.bridgeCurrentRootHash(foreignChainId, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-mining-bridge-call-failed"); - }); - - it("if bridge not set, cannot bridge current state", async () => { - let tx = await homeMetacolony.setColonyBridgeAddress(ADDRESS_ZERO); - await tx.wait(); - tx = await homeColonyNetwork.bridgeCurrentRootHash(foreignChainId, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-bridge-not-set"); - }); - - it("if bridge unknown, cannot bridge current state", async () => { - const tx = await homeColonyNetwork.bridgeCurrentRootHash(ADDRESS_ZERO, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-bridge-not-known-chain"); - }); - - it("stored reputation emissions can be emitted later", async () => { - let tx = await foreignBridge.setBridgeEnabled(false); - await tx.wait(); - tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1338"); - await tx.wait(); - - const bridgedReputationUpdateCount = await foreignColonyNetwork.getBridgedReputationUpdateCount(foreignChainId, foreignColony.address); - - tx = await foreignBridge.setBridgeEnabled(true); - await tx.wait(); - const p = guardianSpy.getPromiseForNextBridgedTransaction(); - - tx = await foreignColonyNetwork.bridgePendingReputationUpdate(foreignColony.address, bridgedReputationUpdateCount); - await tx.wait(); - - await p; - - // See that it's bridged to the inactive log - const logAddress = await homeColonyNetwork.getReputationMiningCycle(false); - const reputationMiningCycleInactive = await new ethers.Contract(logAddress, IReputationMiningCycle.abi, ethersHomeSigner); - - const len = await reputationMiningCycleInactive.getReputationUpdateLogLength(); - - const entry = await reputationMiningCycleInactive.getReputationUpdateLogEntry(len.sub(1)); - - expect(entry.amount.toHexString()).to.equal("0x1338"); - expect(entry.user).to.equal(accounts[0]); - expect(entry.colony).to.equal(foreignColony.address); - - const domain = await foreignColony.getDomain(1); - - expect(entry.skillId.toHexString()).to.equal(domain.skillId.toHexString()); - }); - - it("stored reputation emissions on the foreign chain can be bridged later, and are decayed if required", async () => { - let tx = await foreignBridge.setBridgeEnabled(false); - await tx.wait(); - tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1338"); - await tx.wait(); - - const bridgedReputationUpdateCount = await foreignColonyNetwork.getBridgedReputationUpdateCount(foreignChainId, foreignColony.address); - - tx = await foreignBridge.setBridgeEnabled(true); - await tx.wait(); - - await forwardTime(MINING_CYCLE_DURATION * 10, undefined, web3HomeProvider); - await forwardTime(MINING_CYCLE_DURATION * 10, undefined, web3ForeignProvider); - - const p = guardianSpy.getPromiseForNextBridgedTransaction(); - - tx = await foreignColonyNetwork.bridgePendingReputationUpdate(foreignColony.address, bridgedReputationUpdateCount); - await tx.wait(); - - await p; - - // See that it's bridged to the inactive log - const logAddress = await homeColonyNetwork.getReputationMiningCycle(false); - const reputationMiningCycleInactive = await new ethers.Contract(logAddress, IReputationMiningCycle.abi, ethersHomeSigner); - - const len = await reputationMiningCycleInactive.getReputationUpdateLogLength(); - - const entry = await reputationMiningCycleInactive.getReputationUpdateLogEntry(len.sub(1)); - - expect(entry.amount.toHexString()).to.equal("0x1327"); // Decayed - expect(entry.user).to.equal(accounts[0]); - expect(entry.colony).to.equal(foreignColony.address); - - const domain = await foreignColony.getDomain(1); - - expect(entry.skillId.toHexString()).to.equal(domain.skillId.toHexString()); - }); - - it("stored reputation emissions have to be emitted in order, but only per-colony", async () => { - let p = guardianSpy.getPromiseForNextBridgedTransaction(2); - const foreignColony2 = await setupColony(foreignColonyNetwork); - await p; - - let tx = await foreignBridge.setBridgeEnabled(false); - await tx.wait(); - tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1338"); - await tx.wait(); - tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1339"); - await tx.wait(); - tx = await foreignColony2.emitDomainReputationReward(1, accounts[0], "0x1340"); - await tx.wait(); - - tx = await foreignBridge.setBridgeEnabled(true); - await tx.wait(); - const bridgedReputationUpdateCountColony1 = await foreignColonyNetwork.getBridgedReputationUpdateCount(foreignChainId, foreignColony.address); - - const logAddress = await homeColonyNetwork.getReputationMiningCycle(false); - const reputationMiningCycleInactive = await new ethers.Contract(logAddress, IReputationMiningCycle.abi, ethersHomeSigner); - const logLengthBefore = await reputationMiningCycleInactive.getReputationUpdateLogLength(); - - // We cannot emit the second bridged - tx = await foreignColonyNetwork.bridgePendingReputationUpdate(foreignColony.address, bridgedReputationUpdateCountColony1, { - gasLimit: 1000000, - }); - await checkErrorRevertEthers(tx.wait(), "colony-network-not-next-pending-update"); - - p = guardianSpy.getPromiseForNextBridgedTransaction(); - // We can emit the third (which was another colony) - const bridgedReputationUpdateCountColony2 = await foreignColonyNetwork.getBridgedReputationUpdateCount(foreignChainId, foreignColony2.address); - tx = await foreignColonyNetwork.bridgePendingReputationUpdate(foreignColony2.address, bridgedReputationUpdateCountColony2); - await tx.wait(); - await p; - - p = guardianSpy.getPromiseForNextBridgedTransaction(); - // We can emit the first - tx = await foreignColonyNetwork.bridgePendingReputationUpdate(foreignColony.address, bridgedReputationUpdateCountColony1.sub(1)); - await tx.wait(); - await p; - - p = guardianSpy.getPromiseForNextBridgedTransaction(); - // And now we can emit the second - tx = await foreignColonyNetwork.bridgePendingReputationUpdate(foreignColony.address, bridgedReputationUpdateCountColony1); - await tx.wait(); - await p; - - const logLengthAfter = await reputationMiningCycleInactive.getReputationUpdateLogLength(); - expect(logLengthAfter.sub(logLengthBefore).toNumber()).to.equal(3); - }); - - it("if a bridged reputation emission isn't the next one, it's stored on the mining chain to be added to the log later", async () => { - const logAddress = await homeColonyNetwork.getReputationMiningCycle(false); - const reputationMiningCycleInactive = await new ethers.Contract(logAddress, IReputationMiningCycle.abi, ethersHomeSigner); - const logLengthBefore = await reputationMiningCycleInactive.getReputationUpdateLogLength(); - - let p = guardianSpy.getPromiseForNextBridgedTransaction(2); - const foreignColony2 = await setupColony(foreignColonyNetwork); - await p; - - guardianSpy.skipCount = 1; - - // Bridge skills - - // This one is skipped - let tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1338"); - await tx.wait(); - await guardianSpy.waitUntilSkipped(); - - // These are bridged and added to the pending log - p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1339"); - await tx.wait(); - await p; - - p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1340"); - await tx.wait(); - await p; - - // This gets added to the log after being bridged, as it is another colony - p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await foreignColony2.emitDomainReputationReward(1, accounts[0], "0x1341"); - await tx.wait(); - await p; - - // The log entry for foreignColony2 has been added to the reputation mining cycle contract - const logLengthAfterBridging = await reputationMiningCycleInactive.getReputationUpdateLogLength(); - expect(logLengthAfterBridging.sub(logLengthBefore).toNumber()).to.equal(1); - - // The two log entries have been added to the pending log - let count = await homeColonyNetwork.getBridgedReputationUpdateCount(foreignChainId, foreignColony.address); - let pending1 = await homeColonyNetwork.getPendingReputationUpdate(foreignChainId, foreignColony.address, count.add(2)); - expect(pending1.amount.toHexString()).to.equal("0x1339"); - expect(pending1.user).to.equal(accounts[0]); - expect(pending1.colony).to.equal(foreignColony.address); - - let pending2 = await homeColonyNetwork.getPendingReputationUpdate(foreignChainId, foreignColony.address, count.add(3)); - expect(pending2.amount.toHexString()).to.equal("0x1340"); - expect(pending2.user).to.equal(accounts[0]); - expect(pending2.colony).to.equal(foreignColony.address); - - // We can't emit those yet because we still haven't bridged the one that was skipped - tx = await homeColonyNetwork.addPendingReputationUpdate(foreignChainId, foreignColony.address, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-next-update-does-not-exist"); - - // If we bridge over the original one that was skipped, then we can emit the two pending ones - p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - await p; - count = await homeColonyNetwork.getBridgedReputationUpdateCount(foreignChainId, foreignColony.address); - - tx = await homeColonyNetwork.addPendingReputationUpdate(foreignChainId, foreignColony.address); - await tx.wait(); - tx = await homeColonyNetwork.addPendingReputationUpdate(foreignChainId, foreignColony.address); - await tx.wait(); - - // And now they're on the pending log - const logLengthAfterAdditionalBridging = await reputationMiningCycleInactive.getReputationUpdateLogLength(); - expect(logLengthAfterAdditionalBridging.sub(logLengthAfterBridging).toNumber()).to.equal(3); - - // And removed from the colony network - - pending1 = await homeColonyNetwork.getPendingReputationUpdate(foreignChainId, foreignColony.address, count.add(2)); - expect(pending1.amount.toHexString()).to.equal("0x00"); - expect(pending1.user).to.equal(ADDRESS_ZERO); - expect(pending1.colony).to.equal(ADDRESS_ZERO); - - pending2 = await homeColonyNetwork.getPendingReputationUpdate(foreignChainId, foreignColony.address, count.add(3)); - expect(pending2.amount.toHexString()).to.equal("0x00"); - expect(pending2.user).to.equal(ADDRESS_ZERO); - expect(pending2.colony).to.equal(ADDRESS_ZERO); - }); - - it(`if a bridged reputation emission isn't the next one, it's stored on the mining chain to be added to the log later - and decayed if required`, async () => { - let tx = await foreignBridge.setBridgeEnabled(false); - await tx.wait(); - tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1338"); - await tx.wait(); - - const bridgedReputationUpdateCount = await foreignColonyNetwork.getBridgedReputationUpdateCount(foreignChainId, foreignColony.address); - - tx = await foreignBridge.setBridgeEnabled(true); - await tx.wait(); - - let p = guardianSpy.getPromiseForNextBridgedTransaction(); - await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1339"); - await p; - - p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await foreignColonyNetwork.bridgePendingReputationUpdate(foreignColony.address, bridgedReputationUpdateCount); - await tx.wait(); - await p; - - const pending1 = await homeColonyNetwork.getPendingReputationUpdate(foreignChainId, foreignColony.address, bridgedReputationUpdateCount.add(1)); - expect(pending1.amount.toHexString()).to.equal("0x1339"); - expect(pending1.user).to.equal(accounts[0]); - expect(pending1.colony).to.equal(foreignColony.address); - - await forwardTime(MINING_CYCLE_DURATION * 10, undefined, web3HomeProvider); - await forwardTime(MINING_CYCLE_DURATION * 10, undefined, web3ForeignProvider); - tx = await homeColonyNetwork.addPendingReputationUpdate(foreignChainId, foreignColony.address); - await tx; - - // See that it's bridged to the pending log, but decayed - - const logAddress = await homeColonyNetwork.getReputationMiningCycle(false); - const reputationMiningCycleInactive = await new ethers.Contract(logAddress, IReputationMiningCycle.abi, ethersHomeSigner); - - const len = await reputationMiningCycleInactive.getReputationUpdateLogLength(); - - const entry = await reputationMiningCycleInactive.getReputationUpdateLogEntry(len.sub(1)); - - expect(entry.amount.toHexString()).to.equal("0x1328"); - expect(entry.user).to.equal(accounts[0]); - expect(entry.colony).to.equal(foreignColony.address); - - const domain = await foreignColony.getDomain(1); - - expect(entry.skillId.toHexString()).to.equal(domain.skillId.toHexString()); - }); - - it(`if a bridged reputation emission is for a skill that hasn't been bridged, - it's stored on the mining chain to be added to the log later`, async () => { - const logAddress = await homeColonyNetwork.getReputationMiningCycle(false); - const reputationMiningCycleInactive = await new ethers.Contract(logAddress, IReputationMiningCycle.abi, ethersHomeSigner); - - guardianSpy.skipCount = 2; - const foreignColony2 = await setupColony(foreignColonyNetwork); - await guardianSpy.waitUntilSkipped(); - - // Bridge skills - let p = guardianSpy.getPromiseForNextBridgedTransaction(); - let tx = await foreignColony2.emitDomainReputationReward(1, accounts[0], "0x1338"); - await tx.wait(); - await p; - - // A log entries have been added to the pending log - const count = await homeColonyNetwork.getBridgedReputationUpdateCount(foreignChainId, foreignColony2.address); - let pending = await homeColonyNetwork.getPendingReputationUpdate(foreignChainId, foreignColony2.address, count.add(1)); - expect(pending.amount.toHexString()).to.equal("0x1338"); - expect(pending.user).to.equal(accounts[0]); - expect(pending.colony).to.equal(foreignColony2.address); - - // We can't emit it yet, because the skill still hasn't been bridged - tx = await homeColonyNetwork.addPendingReputationUpdate(foreignChainId, foreignColony2.address, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-invalid-skill-id"); - - const logLength1 = await reputationMiningCycleInactive.getReputationUpdateLogLength(); - - // Bridge over the skill creation - p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - await p; - p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - await p; - - // Now try to emit the pending reputation emission - tx = await homeColonyNetwork.addPendingReputationUpdate(foreignChainId, foreignColony2.address); - await tx.wait(); - - // And now it's on the mining cycle contract - const logLength2 = await reputationMiningCycleInactive.getReputationUpdateLogLength(); - expect(logLength2.sub(logLength1).toNumber()).to.equal(1); - - // And removed from the colony network - - pending = await homeColonyNetwork.getPendingReputationUpdate(foreignChainId, foreignColony2.address, count.add(1)); - expect(pending.amount.toHexString()).to.equal("0x00"); - expect(pending.user).to.equal(ADDRESS_ZERO); - expect(pending.colony).to.equal(ADDRESS_ZERO); - }); - - it("addReputationUpdateLogFromBridge cannot be called by a non-bridge address", async () => { - const tx = await homeColonyNetwork.addReputationUpdateLogFromBridge(ADDRESS_ZERO, ADDRESS_ZERO, 0, 0, 0, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-caller-must-be-colony-bridge"); - }); - - it("bridgePendingReputationUpdate can only be called if the bridge is set", async () => { - // Set bridge to an address that's not a contract, causing the reputation update we subsequently emit to be stored - await setForeignBridgeData(accounts[0]); - let tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1338"); - - const bridgedReputationUpdateCount = await foreignColonyNetwork.getBridgedReputationUpdateCount(foreignChainId, foreignColony.address); - - await setForeignBridgeData(ADDRESS_ZERO); - - tx = await foreignColonyNetwork.bridgePendingReputationUpdate(foreignColony.address, bridgedReputationUpdateCount, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-foreign-bridge-not-set"); - await setForeignBridgeData(foreignColonyBridge.address); - }); - - it("bridgePendingReputationUpdate can only bridge an update that exists", async () => { - const tx = await foreignColonyNetwork.bridgePendingReputationUpdate(foreignColony.address, 1000, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-update-does-not-exist"); - }); - - it("bridgePendingReputationUpdate can be called again if the bridging transaction fails, or the bridge isn't a contract", async () => { - // Set bridge to an address that's not a contract, causing the reputation update we subsequently emit to be stored - await setForeignBridgeData(accounts[0]); - let tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1338"); - await tx.wait(); - - const bridgedReputationUpdateCount = await foreignColonyNetwork.getBridgedReputationUpdateCount(foreignChainId, foreignColony.address); - // Bridge isn't a contract - tx = await foreignColonyNetwork.bridgePendingReputationUpdate(foreignColony.address, bridgedReputationUpdateCount, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-bridging-tx-unsuccessful"); - await setForeignBridgeData(foreignColonyBridge.address); - - // Bridge is now right address, but disable it. - tx = await foreignBridge.setBridgeEnabled(false); - await tx.wait(); - - tx = await foreignColonyNetwork.bridgePendingReputationUpdate(foreignColony.address, bridgedReputationUpdateCount, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-bridging-tx-unsuccessful"); - - tx = await foreignBridge.setBridgeEnabled(true); - await tx.wait(); - - tx = await foreignColonyNetwork.bridgePendingReputationUpdate(foreignColony.address, bridgedReputationUpdateCount, { gasLimit: 1000000 }); - await tx.wait(); - }); - }); - describe("collecting and paying out tokens on another chain", async () => { let foreignToken; let colony; @@ -1215,7 +548,7 @@ contract("Cross-chain", (accounts) => { const colonyCreationSalt = await homeColonyNetwork.getColonyCreationSalt({ blockTag: events[events.length - 1].blockNumber }); - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p = guardianSpy.getPromiseForNextBridgedTransaction(); const tx = await colony.createProxyColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); await tx.wait(); @@ -1240,7 +573,7 @@ contract("Cross-chain", (accounts) => { // Claim on the foreign chain - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p = guardianSpy.getPromiseForNextBridgedTransaction(); tx = await proxyColony.claimTokens(foreignToken.address); await tx.wait(); @@ -1261,7 +594,7 @@ contract("Cross-chain", (accounts) => { await tx.wait(); // Claim on the foreign chain - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(2); + const p = guardianSpy.getPromiseForNextBridgedTransaction(2); // One bridged transaction will be the request across, one will be reporting what was claimed back const payload = proxyColony.interface.encodeFunctionData("claimTokens", [foreignToken.address]); @@ -1284,7 +617,7 @@ contract("Cross-chain", (accounts) => { }); // Claim on foreign chain - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p = guardianSpy.getPromiseForNextBridgedTransaction(); const tx = await proxyColony.claimTokens(ADDRESS_ZERO); await tx.wait(); @@ -1305,7 +638,7 @@ contract("Cross-chain", (accounts) => { await tx.wait(); // Claim on the foreign chain - let p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + let p = guardianSpy.getPromiseForNextBridgedTransaction(); tx = await proxyColony.claimTokens(foreignToken.address); await tx.wait(); await p; @@ -1350,7 +683,7 @@ contract("Cross-chain", (accounts) => { tx = await colony.finalizeExpenditure(expenditureId); await tx.wait(); - p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + p = guardianSpy.getPromiseForNextBridgedTransaction(); tx = await colony["claimExpenditurePayout(uint256,uint256,uint256,address)"](expenditureId, 1, foreignChainId, foreignToken.address); await tx.wait(); await p; @@ -1377,7 +710,7 @@ contract("Cross-chain", (accounts) => { await tx.wait(); // Claim on the foreign chain - let p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + let p = guardianSpy.getPromiseForNextBridgedTransaction(); tx = await proxyColony.claimTokens(ADDRESS_ZERO); await tx.wait(); await p; @@ -1424,7 +757,7 @@ contract("Cross-chain", (accounts) => { const receipientBalanceBefore = await ethersForeignProvider.getBalance(accounts[0]); - p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + p = guardianSpy.getPromiseForNextBridgedTransaction(); tx = await colony["claimExpenditurePayout(uint256,uint256,uint256,address)"](expenditureId, 1, foreignChainId, ADDRESS_ZERO); await tx.wait(); await p; @@ -1448,7 +781,7 @@ contract("Cross-chain", (accounts) => { await tx.wait(); // Claim on the foreign chain - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p = guardianSpy.getPromiseForNextBridgedTransaction(); tx = await proxyColony.claimTokens(foreignToken.address); await tx.wait(); await p; @@ -1479,7 +812,7 @@ contract("Cross-chain", (accounts) => { await tx.wait(); // Can now claim - const p2 = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p2 = guardianSpy.getPromiseForNextBridgedTransaction(); tx = await proxyColony.claimTokens(foreignToken.address); await tx.wait(); await p2; @@ -1541,7 +874,7 @@ contract("Cross-chain", (accounts) => { await foreignToken["mint(address,uint256)"](swapEvent.args._toAddress, swapEvent.args._amount); // Implicit 1:1 exchange rate // Now claim the tokens on the foreign chain - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p = guardianSpy.getPromiseForNextBridgedTransaction(); tx = await proxyColony.claimTokensForDomain(foreignToken.address, 2, { gasLimit: 1000000 }); await tx.wait(); await p; @@ -1565,7 +898,7 @@ contract("Cross-chain", (accounts) => { const colonyCreationSalt = await homeColonyNetwork.getColonyCreationSalt({ blockTag: events[events.length - 1].blockNumber }); - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p = guardianSpy.getPromiseForNextBridgedTransaction(); const tx = await colony.createProxyColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); await tx.wait(); @@ -1582,7 +915,7 @@ contract("Cross-chain", (accounts) => { it("can make arbitrary transactions on the foreign chain", async () => { const balanceBefore = await foreignToken.balanceOf(proxyColony.address); - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p = guardianSpy.getPromiseForNextBridgedTransaction(); const payload = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [proxyColony.address, ethers.utils.parseEther("100")]); @@ -1595,7 +928,7 @@ contract("Cross-chain", (accounts) => { }); it("arbitrary transactions on the foreign chain must go to contracts", async () => { - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p = guardianSpy.getPromiseForNextBridgedTransaction(); const payload = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [proxyColony.address, ethers.utils.parseEther("100")]); @@ -1610,7 +943,7 @@ contract("Cross-chain", (accounts) => { const shellBalanceBefore = await foreignToken.balanceOf(proxyColony.address); const colonyBalanceBefore = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(2); + const p = guardianSpy.getPromiseForNextBridgedTransaction(2); const payload1 = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [proxyColony.address, ethers.utils.parseEther("100")]); const payload2 = proxyColony.interface.encodeFunctionData("claimTokens(address)", [foreignToken.address]); @@ -1629,7 +962,7 @@ contract("Cross-chain", (accounts) => { }); it("invalid cross-chain arbitrary transactions are rejected", async () => { - let p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + let p = guardianSpy.getPromiseForNextBridgedTransaction(); const tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], ["0x00000000", "0x00000000"]); await tx.wait(); @@ -1637,21 +970,21 @@ contract("Cross-chain", (accounts) => { await checkErrorRevertEthers(p, "colony-targets-and-payloads-length-mismatch"); // Check can't target Network - p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + p = guardianSpy.getPromiseForNextBridgedTransaction(); const tx2 = await colony.makeProxyArbitraryTransactions(foreignChainId, [remoteColonyNetwork.address], ["0x00000000"]); await tx2.wait(); await checkErrorRevertEthers(p, "colony-cannot-target-network"); // Check can't target the bridge - p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + p = guardianSpy.getPromiseForNextBridgedTransaction(); const tx3 = await colony.makeProxyArbitraryTransactions(foreignChainId, [remoteColonyBridge.address], ["0x00000000"]); await tx3.wait(); await checkErrorRevertEthers(p, "colony-cannot-target-bridge"); // Otherwise valid transaction, it just fails - p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + p = guardianSpy.getPromiseForNextBridgedTransaction(); const tx4 = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], ["0x00000000"]); await tx4.wait(); @@ -1665,271 +998,6 @@ contract("Cross-chain", (accounts) => { await checkErrorRevertEthers(tx.wait(), "wormhole-bridge-only-colony-network"); }); - it("setReputationRootHashFromBridge can only be called by the colonyBridge contract", async () => { - const [, unknownColonyBridge] = await deployBridge(ethersForeignSigner); - await unknownColonyBridge.setColonyNetworkAddress(foreignColonyNetwork.address); - await unknownColonyBridge.setColonyBridgeAddress(homeChainId, homeColonyBridge.address); - const vaa = await guardianSpy.encodeMockVAA( - homeColonyBridge.address, - 0, - 0, - foreignColonyNetwork.interface.encodeFunctionData("setReputationRootHashFromBridge", [ethers.utils.hexZeroPad("0xdeadbeef", 32), 0, 1]), - 100, - wormholeHomeChainId, - ); - const tx = await unknownColonyBridge.receiveMessage(vaa, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-caller-must-be-colony-bridge"); - }); - - it("setReputationRootHashFromBridge reverts if bridged transaction did not originate from colonyBridge", async () => { - const vaa = await guardianSpy.encodeMockVAA( - ADDRESS_ZERO, - 0, - 0, - foreignColonyNetwork.interface.encodeFunctionData("setReputationRootHashFromBridge", [ethers.utils.hexZeroPad("0xdeadbeef", 32), 0, 1]), - 100, - wormholeForeignChainId, - ); - - const tx = await foreignColonyBridge.receiveMessage(vaa, { gasLimit: 1000000 }); - - await checkErrorRevertEthers(tx.wait(), "colony-bridge-bridged-tx-only-from-colony-bridge"); - - const hash = await foreignColonyNetwork.getReputationRootHash(); - expect(hash).to.not.equal(ethers.utils.hexZeroPad("0xdeadbeef", 32)); - }); - - it("setReputationRootHashFromBridge does not allow transactions to be replayed (if not enforced by bridge)", async () => { - await homeColony.emitDomainReputationReward(1, accounts[0], "0x1337"); - - // Advance mining cycle twice - await forwardTime(MINING_CYCLE_DURATION + CHALLENGE_RESPONSE_WINDOW_DURATION, undefined, web3HomeProvider); - await client.addLogContentsToReputationTree(); - await client.submitRootHash(); - await client.confirmNewHash(); - - await forwardTime(MINING_CYCLE_DURATION + CHALLENGE_RESPONSE_WINDOW_DURATION, undefined, web3HomeProvider); - await client.addLogContentsToReputationTree(); - await client.submitRootHash(); - await client.confirmNewHash(); - - const homeRootHash1 = await homeColonyNetwork.getReputationRootHash(); - - guardianSpy.skipCount = 1; - // Bridge root hash - let tx = await homeColonyNetwork.bridgeCurrentRootHash(foreignChainId); - await tx.wait(); - await guardianSpy.waitUntilSkipped(); - - const skippedTx = guardianSpy.skipped[0]; - - let p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - await p; - - const foreignRootHash1 = await foreignColonyNetwork.getReputationRootHash(); - - expect(homeRootHash1).to.equal(foreignRootHash1); - - // Advance mining cycle twice - await forwardTime(MINING_CYCLE_DURATION + CHALLENGE_RESPONSE_WINDOW_DURATION, undefined, web3HomeProvider); - await client.addLogContentsToReputationTree(); - await client.submitRootHash(); - await client.confirmNewHash(); - - await forwardTime(MINING_CYCLE_DURATION + CHALLENGE_RESPONSE_WINDOW_DURATION, undefined, web3HomeProvider); - await client.addLogContentsToReputationTree(); - await client.submitRootHash(); - await client.confirmNewHash(); - - const homeRootHash2 = await homeColonyNetwork.getReputationRootHash(); - - p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await homeColonyNetwork.bridgeCurrentRootHash(foreignChainId); - await tx.wait(); - await p; - - const foreignRootHash2 = await foreignColonyNetwork.getReputationRootHash(); - expect(foreignRootHash2).to.equal(homeRootHash2); - - // Try and replay - guardianSpy.skipped = [skippedTx]; - - p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - const bridgingTx = await p; - await checkErrorRevertEthers(bridgingTx.wait(), "colony-mining-bridge-invalid-nonce"); - - // Had no effect - - const foreignRootHash3 = await foreignColonyNetwork.getReputationRootHash(); - - expect(foreignRootHash3).to.equal(foreignRootHash2); - expect(foreignRootHash3).to.not.equal(foreignRootHash1); - }); - - it("addSkillFromBridge can only be called by the colonyBridge contract", async () => { - const skillCountBefore = await homeColonyNetwork.getSkillCount(); - - const [, unknownColonyBridge] = await deployBridge(ethersHomeSigner); - await unknownColonyBridge.setColonyBridgeAddress(foreignChainId, foreignColonyBridge.address); - await unknownColonyBridge.setColonyNetworkAddress(homeColonyNetwork.address); - const vaa = await guardianSpy.encodeMockVAA( - foreignColonyBridge.address, - 0, - 0, - homeColonyNetwork.interface.encodeFunctionData("addSkillFromBridge", [1, 2]), - 100, - wormholeForeignChainId, - ); - const tx = await unknownColonyBridge.receiveMessage(vaa, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-caller-must-be-colony-bridge"); - - const skillCountAfter = await homeColonyNetwork.getSkillCount(); - expect(skillCountAfter.toHexString()).to.be.equal(skillCountBefore.toHexString()); - }); - - it("addSkillFromBridge reverts if bridged transaction did not originate from colonyNetwork", async () => { - const skillCountBefore = await homeColonyNetwork.getSkillCount(); - - const vaa = await guardianSpy.encodeMockVAA( - ADDRESS_ZERO, - 0, - 0, - foreignColonyNetwork.interface.encodeFunctionData("setReputationRootHashFromBridge", [ethers.utils.hexZeroPad("0xdeadbeef", 32), 0, 1]), - 100, - wormholeForeignChainId, - ); - - const tx = await foreignColonyBridge.receiveMessage(vaa, { gasLimit: 1000000 }); - - await checkErrorRevertEthers(tx.wait(), "colony-bridge-bridged-tx-only-from-colony-bridge"); - - const skillCountAfter = await homeColonyNetwork.getSkillCount(); - expect(skillCountAfter.toHexString()).to.be.equal(skillCountBefore.toHexString()); - }); - - it("addSkillFromBridge does not allow transactions to be replayed (if not enforced by bridge)", async () => { - guardianSpy.skipCount = 2; - - // Create a skill on foreign chain - let tx = await foreignColony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); - await tx.wait(); - - // Create another - tx = await foreignColony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); - await tx.wait(); - await guardianSpy.waitUntilSkipped(); - const skippedTx1 = guardianSpy.skipped[0]; - const skippedTx2 = guardianSpy.skipped[1]; - - // Bridge out of order - guardianSpy.skipped = [skippedTx2]; - let p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - let bridgingTx = await p; - await bridgingTx.wait(); - - // Replay - guardianSpy.skipped = [skippedTx2]; - p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - bridgingTx = await p; - await checkErrorRevertEthers(bridgingTx.wait(), "colony-network-skill-already-pending"); - - // Bridge first tx - guardianSpy.skipped = [skippedTx1]; - p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - bridgingTx = await p; - await bridgingTx.wait(); - - // Replay first tx - guardianSpy.skipped = [skippedTx1]; - p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - bridgingTx = await p; - await checkErrorRevertEthers(bridgingTx.wait(), "colony-network-skill-already-added"); - }); - - // addReputationUpdateLogFromBridge - it("addReputationUpdateLogFromBridge can only be called by the colonyBridge contract", async () => { - const [, unknownColonyBridge] = await deployBridge(ethersHomeSigner); - await unknownColonyBridge.setColonyNetworkAddress(homeColonyNetwork.address); - await unknownColonyBridge.setColonyBridgeAddress(foreignChainId, foreignColonyBridge.address); - const vaa = await guardianSpy.encodeMockVAA( - foreignColonyBridge.address, - 0, - 0, - homeColonyNetwork.interface.encodeFunctionData("addReputationUpdateLogFromBridge", [ADDRESS_ZERO, ADDRESS_ZERO, 0, 0, 0]), - 100, - wormholeForeignChainId, - ); - const tx = await unknownColonyBridge.receiveMessage(vaa, { gasLimit: 1000000 }); - await checkErrorRevertEthers(tx.wait(), "colony-network-caller-must-be-colony-bridge"); - - // const tx = await homeColonyNetwork.addReputationUpdateLogFromBridge(ADDRESS_ZERO, ADDRESS_ZERO, 0, 0, 0, { gasLimit: 1000000 }); - // await checkErrorRevertEthers(tx.wait(), "colony-network-not-known-bridge"); - }); - - it("addReputationUpdateLogFromBridge reverts if bridged transaction did not originate from colonyNetwork", async () => { - const vaa = await guardianSpy.encodeMockVAA( - ADDRESS_ZERO, - 0, - 0, - foreignColonyNetwork.interface.encodeFunctionData("addReputationUpdateLogFromBridge", [ADDRESS_ZERO, ADDRESS_ZERO, 0, 0, 0]), - 100, - wormholeForeignChainId, - ); - - const tx = await foreignColonyBridge.receiveMessage(vaa, { gasLimit: 1000000 }); - - await checkErrorRevertEthers(tx.wait(), "colony-bridge-bridged-tx-only-from-colony-bridge"); - }); - - it("addReputationUpdateLogFromBridge does not allow transactions to be replayed (if not enforced by bridge)", async () => { - guardianSpy.skipCount = 2; - - // Emit reputation on foreign chain - let tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1337"); - await tx.wait(); - - // Emit more reputation - tx = await foreignColony.emitDomainReputationReward(1, accounts[0], "0x1337"); - await tx.wait(); - await guardianSpy.waitUntilSkipped(); - const skippedTx1 = guardianSpy.skipped[0]; - const skippedTx2 = guardianSpy.skipped[1]; - - // Bridge out of order - guardianSpy.skipped = [skippedTx2]; - let p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - let bridgingTx = await p; - await bridgingTx.wait(); - - // Replay - guardianSpy.skipped = [skippedTx2]; - p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - bridgingTx = await p; - await checkErrorRevertEthers(bridgingTx.wait(), "colony-network-update-already-pending"); - - // Bridge first tx - guardianSpy.skipped = [skippedTx1]; - p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - bridgingTx = await p; - await bridgingTx.wait(); - - // Replay first tx - guardianSpy.skipped = [skippedTx1]; - p = guardianSpy.getPromiseForNextBridgedTransaction(); - await guardianSpy.bridgeSkipped(); - bridgingTx = await p; - await checkErrorRevertEthers(bridgingTx.wait(), "colony-network-update-already-added"); - }); - it("an invalid VM is respected", async () => { await homeBridge.setVerifyVMResult(false, "some-good-reason"); const vaa = await guardianSpy.encodeMockVAA( @@ -1956,7 +1024,7 @@ contract("Cross-chain", (accounts) => { // Deploy a proxy colony on the foreign network const colonyCreationSalt = await homeColonyNetwork.getColonyCreationSalt({ blockTag: events[events.length - 1].blockNumber }); - const p = bridgeMonitor.getPromiseForNextBridgedTransaction(); + const p = guardianSpy.getPromiseForNextBridgedTransaction(); const tx = await colony.createProxyColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); await tx.wait(); @@ -2018,7 +1086,7 @@ contract("Cross-chain", (accounts) => { }); it("Valid VAAs that aren't from a colony bridge are rejected", async () => { - const vaa = await bridgeMonitor.encodeMockVAA( + const vaa = await guardianSpy.encodeMockVAA( homeColonyBridge.address, 0, 0, @@ -2031,7 +1099,7 @@ contract("Cross-chain", (accounts) => { }); it("Valid VAAs that aren't for the right chain are rejected", async () => { - const vaa = await bridgeMonitor.encodeMockVAA( + const vaa = await guardianSpy.encodeMockVAA( homeColonyBridge.address, 0, 0, From 301081f7fb77ee1a685d8152531fac61acf19b5f Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 16 Sep 2024 11:13:33 +0100 Subject: [PATCH 33/72] Respect reward pots for domain-level claims; add event --- ...yColonyNetwork.sol:ProxyColonyNetwork.json | 11 +++ contracts/colony/ColonyFunding.sol | 2 +- contracts/common/DomainReceiverManagement.sol | 15 ++-- test/contracts-network/colony-funding.js | 88 +++---------------- test/truffle-fixture.js | 13 ++- 5 files changed, 45 insertions(+), 84 deletions(-) diff --git a/.storage-layouts-normalized/contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork.json b/.storage-layouts-normalized/contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork.json index 176e709fd0..9002f3042e 100644 --- a/.storage-layouts-normalized/contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork.json +++ b/.storage-layouts-normalized/contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork.json @@ -86,6 +86,17 @@ "numberOfBytes": "1" } } + }, + { + "contract": "contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork", + "label": "domainTokenReceiverResolver", + "offset": 0, + "slot": "7", + "type": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + } } ] } \ No newline at end of file diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index 6e9dbb79cf..5321658b52 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -276,7 +276,7 @@ contract ColonyFunding is getFundingPotBalance(domain.fundingPotId, block.chainid, _token) - _value ); - ERC20Extended(_token).approve(LIFI_ADDRESS, _amount); + require(ERC20Extended(_token).approve(LIFI_ADDRESS, _amount), "colony-approve-failed"); (bool success, ) = LIFI_ADDRESS.call{ value: _value }(_txdata); require(success, "colony-exchange-tokens-failed"); diff --git a/contracts/common/DomainReceiverManagement.sol b/contracts/common/DomainReceiverManagement.sol index 6e8c9bcbce..a026d26010 100644 --- a/contracts/common/DomainReceiverManagement.sol +++ b/contracts/common/DomainReceiverManagement.sol @@ -44,12 +44,16 @@ abstract contract DomainReceiverManagement is MetaTransactionMsgSender, IsContra if (!isContract(domainTokenReceiverAddress)) { // Then deploy the contract bytes32 salt = getDomainTokenReceiverDeploySalt(msgSender(), _domainId); - ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( + address newContract = ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( salt, type(EtherRouterCreate3).creationCode, abi.encodeWithSignature("setOwner(address)", (address(this))), ICreateX.Values(0, 0) ); + require( + newContract == domainTokenReceiverAddress, + "colony-network-domain-receiver-deploy-wrong-address" + ); } // Check it's got the right resolver @@ -64,12 +68,11 @@ abstract contract DomainReceiverManagement is MetaTransactionMsgSender, IsContra } // Check it's set up correctly - DomainTokenReceiver(domainTokenReceiverAddress).getColonyAddress(); - // if (DomainTokenReceiver(domainTokenReceiverAddress).getColonyAddress() != msgSender()) { - // DomainTokenReceiver(domainTokenReceiverAddress).setColonyAddress(msgSender()); - // } + if (DomainTokenReceiver(domainTokenReceiverAddress).getColonyAddress() != msgSender()) { + DomainTokenReceiver(domainTokenReceiverAddress).setColonyAddress(msgSender()); + } - // return domainTokenReceiverAddress; + return domainTokenReceiverAddress; } function getDomainTokenReceiverAddress( diff --git a/test/contracts-network/colony-funding.js b/test/contracts-network/colony-funding.js index d8cbfb57d0..4a6eaa6f92 100755 --- a/test/contracts-network/colony-funding.js +++ b/test/contracts-network/colony-funding.js @@ -28,6 +28,7 @@ const { } = require("../../helpers/test-data-generator"); const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit, expectEvent, rolesToBytes32 } = require("../../helpers/test-helper"); const { setupDomainTokenReceiverResolver } = require("../../helpers/upgradable-contracts"); +<<<<<<< HEAD ||||||| parent of d7aa9686f (Allow funds to be sent directly to domains) const { fundColonyWithTokens, setupRandomColony, makeExpenditure, setupFundedExpenditure } = require("../../helpers/test-data-generator"); const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit } = require("../../helpers/test-helper"); @@ -36,6 +37,17 @@ const { fundColonyWithTokens, setupRandomColony, makeExpenditure, setupFundedExp const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit } = require("../../helpers/test-helper"); const { setupDomainTokenReceiverResolver } = require("../../helpers/upgradable-contracts"); >>>>>>> d7aa9686f (Allow funds to be sent directly to domains) +||||||| parent of c39f5a928 (Respect reward pots for domain-level claims; add event) +======= +||||||| parent of 632eb6ca (Respect reward pots for domain-level claims; add event) +const { fundColonyWithTokens, setupRandomColony, makeExpenditure, setupFundedExpenditure } = require("../../helpers/test-data-generator"); +const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit, expectEvent } = require("../../helpers/test-helper"); +======= +const { fundColonyWithTokens, setupRandomColony, makeExpenditure, setupFundedExpenditure } = require("../../helpers/test-data-generator"); +const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit, expectEvent } = require("../../helpers/test-helper"); +const { setupDomainTokenReceiverResolver } = require("../../helpers/upgradable-contracts"); +>>>>>>> 632eb6ca (Respect reward pots for domain-level claims; add event) +>>>>>>> c39f5a928 (Respect reward pots for domain-level claims; add event) const { expect } = chai; chai.use(bnChai(web3.utils.BN)); @@ -578,7 +590,6 @@ contract("Colony Funding", (accounts) => { expect(colonyRewardPotBalance).to.eq.BN(3); expect(nonRewardPotsTotal).to.eq.BN(297); }); -<<<<<<< HEAD it("should allow native coins to be directly sent to a domain", async () => { // Get address for domain 2 @@ -880,80 +891,5 @@ contract("Colony Funding", (accounts) => { expect(domainPotBalanceAfter.sub(domainPotBalanceBefore)).to.eq.BN(99); expect(nonRewardPotsTotalAfter.sub(nonRewardPotsTotalBefore)).to.eq.BN(99); }); -||||||| parent of d7aa9686f (Allow funds to be sent directly to domains) -======= - - it("should allow native coins to be directly sent to a domain", async () => { - // Get address for domain 2 - await colony.addDomain(1, UINT256_MAX, 1); - const receiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); - - // Send 100 wei - await web3.eth.sendTransaction({ from: MANAGER, to: receiverAddress, value: 100, gas: 1000000 }); - - const domain = await colony.getDomain(2); - const domainPotBalanceBefore = await colony.getFundingPotBalance(domain.fundingPotId, ethers.constants.AddressZero); - - // Claim the funds - await colony.claimDomainFunds(ethers.constants.AddressZero, 2); - - const domainPotBalanceAfter = await colony.getFundingPotBalance(domain.fundingPotId, ethers.constants.AddressZero); - - // Check the balance of the domain - expect(domainPotBalanceAfter.sub(domainPotBalanceBefore)).to.eq.BN(100); - }); - - it("should allow a token to be directly sent to a domain", async () => { - // Get address for domain 2 - await colony.addDomain(1, UINT256_MAX, 1); - const receiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); - - // Send 100 wei - await otherToken.mint(receiverAddress, 100); - - const domain = await colony.getDomain(2); - const domainPotBalanceBefore = await colony.getFundingPotBalance(domain.fundingPotId, otherToken.address); - - // Claim the funds - await colony.claimDomainFunds(otherToken.address, 2); - - const domainPotBalanceAfter = await colony.getFundingPotBalance(domain.fundingPotId, otherToken.address); - - // Check the balance of the domain - expect(domainPotBalanceAfter.sub(domainPotBalanceBefore)).to.eq.BN(100); - }); - - it("should not be able to claim funds for a domain that does not exist", async () => { - await checkErrorRevert(colony.claimDomainFunds(ethers.constants.AddressZero, 2), "colony-funding-domain-does-not-exist"); - }); - - it("only a colony can call idempotentDeployDomainTokenReceiver on Network", async () => { - await checkErrorRevert(colonyNetwork.idempotentDeployDomainTokenReceiver(2), "colony-caller-must-be-colony"); - }); - - it("If the receiver resolver is updated, then the resolver is updated at the next claim", async () => { - await colony.addDomain(1, UINT256_MAX, 1); - const receiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); - // Send 100 wei - await otherToken.mint(receiverAddress, 100); - await colony.claimDomainFunds(otherToken.address, 2); - - const receiverAsEtherRouter = await EtherRouter.at(receiverAddress); - const resolver = await receiverAsEtherRouter.resolver(); - - // Update the resolver - const newResolver = await Resolver.new(); - const domainTokenReceiver = await DomainTokenReceiver.new(); - - await setupDomainTokenReceiverResolver(colonyNetwork, domainTokenReceiver, newResolver); - - await otherToken.mint(receiverAddress, 50); - await colony.claimDomainFunds(otherToken.address, 2); - - const resolverAfter = await receiverAsEtherRouter.resolver(); - expect(resolverAfter).to.not.equal(resolver); - expect(resolverAfter).to.equal(newResolver.address); - }); ->>>>>>> d7aa9686f (Allow funds to be sent directly to domains) }); }); diff --git a/test/truffle-fixture.js b/test/truffle-fixture.js index f43e1536bd..22cf279556 100644 --- a/test/truffle-fixture.js +++ b/test/truffle-fixture.js @@ -193,7 +193,18 @@ async function setupColonyNetwork() { EtherRouter.setAsDeployed(etherRouter); // Deploy LiFiMock to LiFi address - await setCode("0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE", LiFiFacetProxyMock.deployedBytecode); + try { + await setCode("0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE", LiFiFacetProxyMock.deployedBytecode); + } catch (error) { + if (error.message.includes("OnlyHardhatNetworkError")) { + await new Promise(function (resolve) { + web3.provider.send( + { method: "evm_setCode", params: ["0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE", LiFiFacetProxyMock.deployedBytecode] }, + resolve, + ); + }); + } + } await setupUpgradableColonyNetwork( etherRouter, From 18ab587ffb13ef1cbc4c50563c1d7c1e00d9fd24 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 20 Sep 2024 10:57:51 +0100 Subject: [PATCH 34/72] Fix up build checks --- contracts/bridging/ProxyColonyNetwork.sol | 4 ++++ contracts/colonyNetwork/ColonyNetworkDeployer.sol | 4 ++++ contracts/common/DomainReceiverManagement.sol | 2 ++ scripts/check-recovery.js | 3 +++ 4 files changed, 13 insertions(+) diff --git a/contracts/bridging/ProxyColonyNetwork.sol b/contracts/bridging/ProxyColonyNetwork.sol index 52761a1696..cccbdd74aa 100644 --- a/contracts/bridging/ProxyColonyNetwork.sol +++ b/contracts/bridging/ProxyColonyNetwork.sol @@ -92,6 +92,10 @@ contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards, DomainReceiver domainTokenReceiverResolver = _resolver; } + function isStopped() internal pure override returns (bool) { + return false; + } + function createProxyColonyFromBridge(bytes32 _salt) public onlyColonyBridge { EtherRouter etherRouter = EtherRouter( payable( diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index aee89b15e5..82eae174d6 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -346,6 +346,10 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage, DomainReceiverManagement >>>>>>> 125a5a92 (First pass at cross-chain domain-level token swaps) } + function isStopped() internal view override returns (bool) { + return recoveryMode; + } + function deployColony(address _tokenAddress, uint256 _version) internal returns (address) { require(_tokenAddress != address(0x0), "colony-token-invalid-address"); require(colonyVersionResolver[_version] != address(0x00), "colony-network-invalid-version"); diff --git a/contracts/common/DomainReceiverManagement.sol b/contracts/common/DomainReceiverManagement.sol index a026d26010..5accf61da8 100644 --- a/contracts/common/DomainReceiverManagement.sol +++ b/contracts/common/DomainReceiverManagement.sol @@ -32,11 +32,13 @@ abstract contract DomainReceiverManagement is MetaTransactionMsgSender, IsContra function getDomainTokenReceiverResolver() public view virtual returns (address); function msgSenderIsColony() internal view virtual returns (bool); + function isStopped() internal view virtual returns (bool); function checkDomainTokenReceiverDeployed( uint256 _domainId ) public returns (address domainTokenReceiverAddress) { require(msgSenderIsColony(), "colony-domain-receiver-management-not-colony"); + require(!isStopped(), "colony-domain-receiver-management-stopped"); // Calculate the address the domain should be receiving funds at domainTokenReceiverAddress = getDomainTokenReceiverAddress(msgSender(), _domainId); diff --git a/scripts/check-recovery.js b/scripts/check-recovery.js index e391b14dc5..fca6818671 100755 --- a/scripts/check-recovery.js +++ b/scripts/check-recovery.js @@ -51,6 +51,8 @@ walkSync("./contracts/").forEach((contractName) => { "contracts/common/Multicall.sol", "contracts/common/Resolver.sol", "contracts/common/TokenAuthority.sol", // Imported from colonyToken repo + "contracts/common/DomainReceiverManagement.sol", // Is used, but in multiple places and has its own implementation for now + "contracts/common/DomainTokenReceiver.sol", "contracts/ens/ENS.sol", "contracts/ens/ENSRegistry.sol", "contracts/extensions/IColonyExtension.sol", @@ -93,6 +95,7 @@ walkSync("./contracts/").forEach((contractName) => { "contracts/testHelpers/ERC721Mock.sol", "contracts/testHelpers/NoLimitSubdomains.sol", "contracts/testHelpers/GasGuzzler.sol", + "contracts/testHelpers/LiFiFacetProxyMock.sol", "contracts/testHelpers/TasksPayments.sol", "contracts/testHelpers/ToggleableToken.sol", "contracts/testHelpers/FunctionsNotAvailableOnColony.sol", From db22e3d1ffb1d860c9aafb7c0675cf48210d249d Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 20 Sep 2024 11:41:23 +0100 Subject: [PATCH 35/72] More tests for DomainTokenReceiver --- .circleci/config.yml | 5 -- test/contracts-network/colony-funding.js | 89 ++++++++++++++++-------- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d7f873fa3e..fc01b1b1d1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -329,11 +329,6 @@ jobs: command: CHAIN_ID=777 pnpm run test:contracts:chainid:coverage environment: NODE_OPTIONS: --max-old-space-size=6144 - - run: - name: "Running chainid tests for Mainnet" - command: CHAIN_ID=1 pnpm run test:contracts:chainid:coverage - environment: - NODE_OPTIONS: --max-old-space-size=6144 - persist_to_workspace: root: ./ paths: diff --git a/test/contracts-network/colony-funding.js b/test/contracts-network/colony-funding.js index 4a6eaa6f92..a74aa7cb66 100755 --- a/test/contracts-network/colony-funding.js +++ b/test/contracts-network/colony-funding.js @@ -18,7 +18,6 @@ const { ADDRESS_ZERO, } = require("../../helpers/constants"); -<<<<<<< HEAD const { fundColonyWithTokens, setupRandomColony, @@ -28,26 +27,6 @@ const { } = require("../../helpers/test-data-generator"); const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit, expectEvent, rolesToBytes32 } = require("../../helpers/test-helper"); const { setupDomainTokenReceiverResolver } = require("../../helpers/upgradable-contracts"); -<<<<<<< HEAD -||||||| parent of d7aa9686f (Allow funds to be sent directly to domains) -const { fundColonyWithTokens, setupRandomColony, makeExpenditure, setupFundedExpenditure } = require("../../helpers/test-data-generator"); -const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit } = require("../../helpers/test-helper"); -======= -const { fundColonyWithTokens, setupRandomColony, makeExpenditure, setupFundedExpenditure } = require("../../helpers/test-data-generator"); -const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit } = require("../../helpers/test-helper"); -const { setupDomainTokenReceiverResolver } = require("../../helpers/upgradable-contracts"); ->>>>>>> d7aa9686f (Allow funds to be sent directly to domains) -||||||| parent of c39f5a928 (Respect reward pots for domain-level claims; add event) -======= -||||||| parent of 632eb6ca (Respect reward pots for domain-level claims; add event) -const { fundColonyWithTokens, setupRandomColony, makeExpenditure, setupFundedExpenditure } = require("../../helpers/test-data-generator"); -const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit, expectEvent } = require("../../helpers/test-helper"); -======= -const { fundColonyWithTokens, setupRandomColony, makeExpenditure, setupFundedExpenditure } = require("../../helpers/test-data-generator"); -const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit, expectEvent } = require("../../helpers/test-helper"); -const { setupDomainTokenReceiverResolver } = require("../../helpers/upgradable-contracts"); ->>>>>>> 632eb6ca (Respect reward pots for domain-level claims; add event) ->>>>>>> c39f5a928 (Respect reward pots for domain-level claims; add event) const { expect } = chai; chai.use(bnChai(web3.utils.BN)); @@ -56,15 +35,10 @@ const EtherRouter = artifacts.require("EtherRouter"); const IColonyNetwork = artifacts.require("IColonyNetwork"); const IMetaColony = artifacts.require("IMetaColony"); const Token = artifacts.require("Token"); -<<<<<<< HEAD const Resolver = artifacts.require("Resolver"); const DomainTokenReceiver = artifacts.require("DomainTokenReceiver"); const TokenAuthority = artifacts.require("contracts/common/TokenAuthority.sol:TokenAuthority"); -||||||| parent of d7aa9686f (Allow funds to be sent directly to domains) -======= -const Resolver = artifacts.require("Resolver"); -const DomainTokenReceiver = artifacts.require("DomainTokenReceiver"); ->>>>>>> d7aa9686f (Allow funds to be sent directly to domains) +const ToggleableToken = artifacts.require("ToggleableToken"); contract("Colony Funding", (accounts) => { const MANAGER = accounts[0]; @@ -888,8 +862,65 @@ contract("Colony Funding", (accounts) => { const resolverAfter = await receiverAsEtherRouter.resolver(); expect(resolverAfter).to.not.equal(resolver); expect(resolverAfter).to.equal(newResolver.address); - expect(domainPotBalanceAfter.sub(domainPotBalanceBefore)).to.eq.BN(99); - expect(nonRewardPotsTotalAfter.sub(nonRewardPotsTotalBefore)).to.eq.BN(99); + }); + + it("should not be able to claim funds for a domain that does not exist", async () => { + await checkErrorRevert(colony.claimDomainFunds(ethers.constants.AddressZero, 2), "colony-funding-domain-does-not-exist"); + }); + + it("only a colony can call checkDomainTokenReceiverDeployed on Network", async () => { + await checkErrorRevert(colonyNetwork.checkDomainTokenReceiverDeployed(2), "colony-caller-must-be-colony"); + }); + + it("only the owner (which should be colonyNetwork) can call setColonyAddress on DomainTokenReceiver", async () => { + await colony.addDomain(1, UINT256_MAX, 1); + await colony.claimDomainFunds(ethers.constants.AddressZero, 2); + + const receiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); + const receiverAsEtherRouter = await EtherRouter.at(receiverAddress); + const receiver = await DomainTokenReceiver.at(receiverAddress); + const owner = await receiverAsEtherRouter.owner(); + expect(owner).to.equal(colonyNetwork.address); + + await checkErrorRevert(receiver.setColonyAddress(colony.address), "ds-auth-unauthorized"); + await receiver.setColonyAddress.estimateGas(colony.address, { from: colonyNetwork.address }); + }); + + it("If transfer fails from receiver, then the funds are not claimed", async () => { + await colony.addDomain(1, UINT256_MAX, 1); + const receiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); + + const toggleableToken = await ToggleableToken.new(200); + await toggleableToken.mint(receiverAddress, 100); + + await toggleableToken.toggleLock(); + + // Try to claim the funds + await checkErrorRevert(colony.claimDomainFunds(toggleableToken.address, 2), "domain-token-receiver-transfer-failed"); + }); + + it("If the receiver resolver is updated, then the resolver is updated at the next claim", async () => { + await colony.addDomain(1, UINT256_MAX, 1); + const receiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); + // Send 100 wei + await otherToken.mint(receiverAddress, 100); + await colony.claimDomainFunds(otherToken.address, 2); + + const receiverAsEtherRouter = await EtherRouter.at(receiverAddress); + const resolver = await receiverAsEtherRouter.resolver(); + + // Update the resolver + const newResolver = await Resolver.new(); + const domainTokenReceiver = await DomainTokenReceiver.new(); + + await setupDomainTokenReceiverResolver(colonyNetwork, domainTokenReceiver, newResolver); + + await otherToken.mint(receiverAddress, 50); + await colony.claimDomainFunds(otherToken.address, 2); + + const resolverAfter = await receiverAsEtherRouter.resolver(); + expect(resolverAfter).to.not.equal(resolver); + expect(resolverAfter).to.equal(newResolver.address); }); }); }); From a9e4748beacbe670bb3ea3bd2afcdd8d382192b6 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 20 Sep 2024 15:14:59 +0100 Subject: [PATCH 36/72] Extra tests, mostly around permissions --- contracts/common/DomainReceiverManagement.sol | 2 +- .../colony-arbitrary-transactions.js | 13 +++++++++++++ test/contracts-network/colony-expenditure.js | 13 +++++++++++++ .../colony-network-recovery.js | 1 + test/contracts-network/colony-recovery.js | 4 ++++ test/contracts-network/colony.js | 18 ++++++++++++++++++ test/cross-chain/cross-chain.js | 18 ++++++++++++++++++ 7 files changed, 68 insertions(+), 1 deletion(-) diff --git a/contracts/common/DomainReceiverManagement.sol b/contracts/common/DomainReceiverManagement.sol index 5accf61da8..f6a540c1d1 100644 --- a/contracts/common/DomainReceiverManagement.sol +++ b/contracts/common/DomainReceiverManagement.sol @@ -37,8 +37,8 @@ abstract contract DomainReceiverManagement is MetaTransactionMsgSender, IsContra function checkDomainTokenReceiverDeployed( uint256 _domainId ) public returns (address domainTokenReceiverAddress) { - require(msgSenderIsColony(), "colony-domain-receiver-management-not-colony"); require(!isStopped(), "colony-domain-receiver-management-stopped"); + require(msgSenderIsColony(), "colony-domain-receiver-management-not-colony"); // Calculate the address the domain should be receiving funds at domainTokenReceiverAddress = getDomainTokenReceiverAddress(msgSender(), _domainId); diff --git a/test/contracts-network/colony-arbitrary-transactions.js b/test/contracts-network/colony-arbitrary-transactions.js index 26cd844c4f..9ea62b42fd 100644 --- a/test/contracts-network/colony-arbitrary-transactions.js +++ b/test/contracts-network/colony-arbitrary-transactions.js @@ -49,6 +49,19 @@ contract("Colony Arbitrary Transactions", (accounts) => { expect(balancePost.sub(balancePre)).to.eq.BN(WAD); }); + it("makeSingleArbitraryTransaction can only be called by the colony on itself", async () => { + const action = await encodeTxData(token, "mint", [WAD]); + await checkErrorRevert(colony.makeSingleArbitraryTransaction(token.address, action, { from: USER0 }), "colony-not-self"); + }); + + it("makeArbitraryTransactions must be called with the same number of addresses and actions", async () => { + const action = await encodeTxData(token, "mint", [WAD]); + await checkErrorRevert( + colony.makeArbitraryTransactions([token.address, token.address], [action], true), + "colony-targets-and-actions-length-mismatch", + ); + }); + it("should be able to make multiple arbitrary transactions", async () => { const action = await encodeTxData(token, "mint", [WAD]); const action2 = await encodeTxData(token, "mint", [WAD.muln(2)]); diff --git a/test/contracts-network/colony-expenditure.js b/test/contracts-network/colony-expenditure.js index f34d185ae5..5a56c0f645 100644 --- a/test/contracts-network/colony-expenditure.js +++ b/test/contracts-network/colony-expenditure.js @@ -234,6 +234,10 @@ contract("Colony Expenditure", (accounts) => { it("should allow arbitrators to update the metadata", async () => { const setExpenditureMetadata = colony.methods["setExpenditureMetadata(uint256,uint256,uint256,string)"]; + + // Try with a bad proof + await checkErrorRevert(setExpenditureMetadata(1, 0, expenditureId, IPFS_HASH, { from: ARBITRATOR }), "ds-auth-invalid-domain-inheritance"); + const tx = await setExpenditureMetadata(1, UINT256_MAX, expenditureId, IPFS_HASH, { from: ARBITRATOR }); await expectEvent(tx, "ExpenditureMetadataSet", [ARBITRATOR, expenditureId, IPFS_HASH]); @@ -642,6 +646,15 @@ contract("Colony Expenditure", (accounts) => { await checkErrorRevert(colony.finalizeExpenditure(expenditureId, { from: ADMIN }), "colony-expenditure-not-draft-or-locked"); }); + it("should not allow expenditures to be finalized if they are not fully funded", async () => { + await colony.setExpenditurePayout(expenditureId, SLOT0, token.address, WAD, { from: ADMIN }); + await checkErrorRevert(colony.finalizeExpenditure(expenditureId, { from: ADMIN }), "colony-expenditure-not-funded"); + await checkErrorRevert( + colony.finalizeExpenditureViaArbitration(1, UINT256_MAX, expenditureId, { from: ARBITRATOR }), + "colony-expenditure-not-funded", + ); + }); + it("should allow owners to finalize expenditures from locked state", async () => { await colony.lockExpenditure(expenditureId, { from: ADMIN }); diff --git a/test/contracts-network/colony-network-recovery.js b/test/contracts-network/colony-network-recovery.js index bc0a0280bc..f88c1fc5b2 100644 --- a/test/contracts-network/colony-network-recovery.js +++ b/test/contracts-network/colony-network-recovery.js @@ -204,6 +204,7 @@ contract("Colony Network Recovery", (accounts) => { await checkErrorRevert(colonyNetwork.bridgeMessage(1, "0x00000000"), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.bridgeMessageToNetwork(1, "0x00000000"), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.createProxyColony(1, HASHZERO), "colony-in-recovery-mode"); + await checkErrorRevert(colonyNetwork.checkDomainTokenReceiverDeployed(1), "colony-domain-receiver-management-stopped"); await colonyNetwork.approveExitRecovery(); await colonyNetwork.exitRecoveryMode(); diff --git a/test/contracts-network/colony-recovery.js b/test/contracts-network/colony-recovery.js index b785f7d102..55925f4d5e 100644 --- a/test/contracts-network/colony-recovery.js +++ b/test/contracts-network/colony-recovery.js @@ -197,6 +197,7 @@ contract("Colony Recovery", (accounts) => { await checkErrorRevert(metaColony.claimExpenditurePayout(0, 0, ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.moveFundsBetweenPots(0, 0, 0, 0, 0, 0, 0, 0, ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.moveFundsBetweenPots(0, 0, 0, 0, 0, 0, ADDRESS_ZERO), "colony-in-recovery-mode"); + await checkErrorRevert(metaColony.moveFundsBetweenPots(0, 0, 0, 0, 0, 0, 0, 0, 0, ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.claimColonyFunds(ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.startNextRewardPayout(ADDRESS_ZERO, HASHZERO, HASHZERO, 0, []), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.claimRewardPayout(0, [0, 0, 0, 0, 0, 0, 0], HASHZERO, HASHZERO, 0, []), "colony-in-recovery-mode"); @@ -204,6 +205,7 @@ contract("Colony Recovery", (accounts) => { await checkErrorRevert(metaColony.setExpenditurePayouts(0, [], ADDRESS_ZERO, []), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.setExpenditurePayout(0, 0, ADDRESS_ZERO, 0), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.setExpenditurePayout(1, UINT256_MAX, 0, 0, ADDRESS_ZERO, 0), "colony-in-recovery-mode"); + await checkErrorRevert(metaColony.setExpenditureSkill(1, 1, 1), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.enterRecoveryMode(), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.burnTokens(ADDRESS_ZERO, 0), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.registerColonyLabel("", ""), "colony-in-recovery-mode"); @@ -211,6 +213,8 @@ contract("Colony Recovery", (accounts) => { await checkErrorRevert(metaColony.makeArbitraryTransaction(ADDRESS_ZERO, HASHZERO), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.makeArbitraryTransactions([], [], true), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.makeSingleArbitraryTransaction(ADDRESS_ZERO, HASHZERO), "colony-in-recovery-mode"); + await checkErrorRevert(metaColony.makeProxyArbitraryTransactions(1, [], []), "colony-in-recovery-mode"); + await checkErrorRevert(metaColony.callProxyNetwork(1, []), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.updateApprovalAmount(ADDRESS_ZERO, ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.finalizeRewardPayout(1), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.claimDomainFunds(ADDRESS_ZERO, 1), "colony-in-recovery-mode"); diff --git a/test/contracts-network/colony.js b/test/contracts-network/colony.js index ebc1939eb5..9342d42cb8 100755 --- a/test/contracts-network/colony.js +++ b/test/contracts-network/colony.js @@ -13,6 +13,7 @@ const { expectAllEvents, expectEvent, upgradeColonyOnceThenToLatest, + bn2bytes32, } = require("../../helpers/test-helper"); const { setupRandomColony, @@ -237,6 +238,14 @@ contract("Colony", (accounts) => { await expectEvent(tx, "FundingPotAdded", [fundingPotCount]); await expectEvent(tx, "DomainMetadata", [accounts[0], domainCount, IPFS_HASH]); }); + + it("should require a valid permission proof", async () => { + await colony.addDomain(1, UINT256_MAX, 1); + + // Remove permission + await colony.setUserRoles(1, UINT256_MAX, USER0, 1, bn2bytes32(0)); + await checkErrorRevert(colony.addDomain(1, UINT256_MAX, 1), "ds-auth-unauthorized"); + }); }); describe("when editing domains", () => { @@ -250,6 +259,15 @@ contract("Colony", (accounts) => { await colony.addDomain(1, UINT256_MAX, 1); await expectNoEvent(colony.editDomain(1, 0, 2, ""), "DomainMetadata"); }); + + it("should require a valid permission proof", async () => { + await colony.addDomain(1, UINT256_MAX, 1); + await colony.editDomain(1, 0, 2, IPFS_HASH); + + // Remove permission + await colony.setUserRoles(1, UINT256_MAX, USER0, 1, bn2bytes32(0)); + await checkErrorRevert(colony.editDomain(1, 0, 2, IPFS_HASH), "ds-auth-unauthorized"); + }); }); describe("when deprecating domains", () => { diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index de8a054f58..6a425ddb41 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -927,6 +927,17 @@ contract("Cross-chain", (accounts) => { expect(balanceAfter.sub(balanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); }); + it("root permissions are required for makeProxyArbitraryTransactions", async () => { + const p = guardianSpy.getPromiseForNextBridgedTransaction(); + let tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], ["0x00000000"]); + await tx.wait(); + await p; + + await colony.setUserRoles(1, UINT256_MAX_ETHERS, accounts[0], 1, ethers.utils.hexZeroPad("0x00", 32)); + tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], ["0x00000000"], { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "ds-auth-unauthorized"); + }); + it("arbitrary transactions on the foreign chain must go to contracts", async () => { const p = guardianSpy.getPromiseForNextBridgedTransaction(); @@ -1079,6 +1090,13 @@ contract("Cross-chain", (accounts) => { }); }); + describe("ColonyNetwork functions are secure", async () => { + it("a non-colony address cannot call bridgeMessage", async () => { + const tx = await homeColonyNetwork.bridgeMessage(1, HASHZERO, { gasLimit: 1000000 }); + await checkErrorRevertEthers(tx.wait(), "colony-caller-must-be-colony"); + }); + }); + describe("Invalid interactions with bridging system are handled appropriately", async () => { it("Can't bridge to a chain that's not supported", async () => { const tx = await homeColony.makeProxyArbitraryTransactions(111, [ADDRESS_ZERO], ["0x00000000"], { gasLimit: 1000000 }); From 256c6b86e3adb08b09b305e6c95da4f15bde0113 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 20 Sep 2024 16:55:46 +0100 Subject: [PATCH 37/72] Add metatransactions to ProxyColony --- contracts/bridging/ProxyColony.sol | 14 +++++++++++++- helpers/test-data-generator.js | 7 +++++-- test/cross-chain/cross-chain.js | 29 +++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index 73418a4a5e..2d29467ed3 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -26,8 +26,9 @@ import { Multicall } from "./../common/Multicall.sol"; import { IColonyNetwork } from "./../colonyNetwork/IColonyNetwork.sol"; import { ProxyColonyNetwork } from "./ProxyColonyNetwork.sol"; import { DomainTokenReceiver } from "./../common/DomainTokenReceiver.sol"; +import { BasicMetaTransaction } from "./../common/BasicMetaTransaction.sol"; -contract ProxyColony is DSAuth, Multicall, CallWithGuards { +contract ProxyColony is DSAuth, Multicall, CallWithGuards, BasicMetaTransaction { // Address of the Resolver contract used by EtherRouter for lookups and routing address resolver; // Storage slot 2 (from DSAuth there is authority and owner at storage slots 0 and 1 respectively) @@ -131,4 +132,15 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards { } } } + + // Overloading functions from BasicMetaTransaction + function getMetatransactionNonce(address _user) public view override returns (uint256 nonce) { + return metatransactionNonces[_user]; + } + + // NB if implementing this functionality in a contract with recovery mode, + // you MUST prevent the metatransaction nonces from being editable with recovery mode. + function incrementMetatransactionNonce(address _user) internal override { + metatransactionNonces[_user] += 1; + } } diff --git a/helpers/test-data-generator.js b/helpers/test-data-generator.js index 78f6d186ae..a6b810d846 100644 --- a/helpers/test-data-generator.js +++ b/helpers/test-data-generator.js @@ -313,12 +313,15 @@ exports.setupColony = async function setupColony(colonyNetwork, tokenAddress, ve return colony; }; -exports.getMetaTransactionParameters = async function getMetaTransactionParameters(txData, userAddress, targetAddress) { +exports.getMetaTransactionParameters = async function getMetaTransactionParameters(txData, userAddress, targetAddress, _chainId) { const contract = await BasicMetaTransaction.at(targetAddress); const nonce = await contract.getMetatransactionNonce(userAddress); // We should just be able to get the chain id via a web3 call, but until ganache sort their stuff out, // we dance around the houses. - const chainId = await getChainId(); + let chainId = _chainId; + if (!chainId) { + chainId = await getChainId(); + } // Sign data const msg = web3.utils.soliditySha3( diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 6a425ddb41..79b21ca94b 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -48,6 +48,7 @@ const { const { forwardTime, checkErrorRevertEthers, revert, snapshot, evmChainIdToWormholeChainId } = require("../../helpers/test-helper"); const ReputationMinerTestWrapper = require("../../packages/reputation-miner/test/ReputationMinerTestWrapper"); const { TruffleLoader } = require("../../packages/package-utils"); +const { getMetaTransactionParameters } = require("../../helpers/test-data-generator"); const UINT256_MAX_ETHERS = ethers.BigNumber.from(2).pow(256).sub(1); @@ -608,6 +609,34 @@ contract("Cross-chain", (accounts) => { expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); }); + it("Can claim tokens received on foreign chain via metatransaction", async () => { + const tokenAmount = ethers.utils.parseEther("100"); + + let tx = await foreignToken["mint(address,uint256)"](proxyColony.address, tokenAmount); + await tx.wait(); + + const metatransactionNonceBefore = await proxyColony.getMetatransactionNonce(accounts[1]); + + // Claim on the foreign chain via metatransaction + const p = guardianSpy.getPromiseForNextBridgedTransaction(); + + const payload = proxyColony.interface.encodeFunctionData("claimTokens", [foreignToken.address]); + + const { r, s, v } = await getMetaTransactionParameters(payload, accounts[1], proxyColony.address, foreignChainId); + + tx = await proxyColony.executeMetaTransaction(accounts[1], payload, r, s, v, { from: accounts[0] }); + await tx.wait(); + await p; + + // Check bookkeeping on the home chain + const balance = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); + + // Check nonce incremented + const metatransactionNonceAfter = await proxyColony.getMetatransactionNonce(accounts[1]); + expect(metatransactionNonceAfter.toHexString()).to.equal(metatransactionNonceBefore.add(1).toHexString()); + }); + it("Can track native tokens received on foreign chains", async () => { const tokenAmount = ethers.utils.parseEther("1"); From 63aa1afff593e8ee0c69acbe16bce3743632f0da Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Tue, 1 Oct 2024 11:24:02 +0100 Subject: [PATCH 38/72] Proxy-held to proxy-held token swapping --- contracts/bridging/ProxyColony.sol | 2 +- contracts/colony/ColonyFunding.sol | 65 +++++++++++++++++++ contracts/colony/ColonyStorage.sol | 5 +- contracts/colony/IColony.sol | 21 +++++++ docs/interfaces/icolony.md | 23 +++++++ test/cross-chain/cross-chain.js | 94 ++++++++++++++++++++++++++++ test/deploy-proxy-network-fixture.js | 16 +++++ 7 files changed, 224 insertions(+), 2 deletions(-) diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index 2d29467ed3..1af1977edf 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -122,7 +122,7 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards, BasicMetaTransaction // TODO: Stop, or otherwise handle, approve / transferFrom require(_targets[i] != bridgeAddress, "colony-cannot-target-bridge"); require(_targets[i] != owner, "colony-cannot-target-network"); - + // TODO: Allowing calling ourselves is okay for now, but as we add functionality might not be? (bool success, bytes memory returndata) = callWithGuards(_targets[i], _payloads[i]); // Note that this is not a require because returndata might not be a string, and if we try diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index 5321658b52..fb2ca7fe20 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -22,7 +22,9 @@ pragma experimental "ABIEncoderV2"; import { ITokenLocking } from "./../tokenLocking/ITokenLocking.sol"; import { ColonyStorage } from "./ColonyStorage.sol"; import { ERC20Extended } from "./../common/ERC20Extended.sol"; +import { ERC20 } from "./../../lib/dappsys/erc20.sol"; import { IColonyNetwork } from "./../colonyNetwork/IColonyNetwork.sol"; +import { IColony } from "./IColony.sol"; import { DomainTokenReceiver } from "./../common/DomainTokenReceiver.sol"; contract ColonyFunding is @@ -282,6 +284,69 @@ contract ColonyFunding is require(success, "colony-exchange-tokens-failed"); } + function exchangeProxyHeldTokensViaLiFi( + uint256 _permissionDomainId, + uint256 _childSkillIndex, + uint256 _domainId, + bytes memory _txdata, + uint256 _value, + uint256 _chainId, + address _token, + uint256 _amount + ) public stoppable authDomain(_permissionDomainId, _childSkillIndex, _domainId) { + // TODO: Colony Network fee + + Domain storage d = domains[_domainId]; + + // Check the domain has enough for what is + if (_token == address(0x0)) { + require( + _value + _amount <= getFundingPotBalance(d.fundingPotId, _chainId, _token), + "colony-insufficient-funds" + ); + } else { + require( + _amount <= getFundingPotBalance(d.fundingPotId, _chainId, _token), + "colony-insufficient-funds" + ); + require( + _value <= getFundingPotBalance(d.fundingPotId, _chainId, _token), + "colony-insufficient-funds" + ); + } + + // Deduct the amount from the domain + setFundingPotBalance( + d.fundingPotId, + _chainId, + _token, + getFundingPotBalance(d.fundingPotId, _chainId, _token) - _amount + ); + + // Deduct the value from the domain + setFundingPotBalance( + d.fundingPotId, + _chainId, + address(0x0), + getFundingPotBalance(d.fundingPotId, _chainId, _token) - _value + ); + + // Build and send the transaction + if (_token == address(0)) { + revert("not yet implemented"); + } else { + address[] memory targets = new address[](2); + targets[0] = _token; + targets[1] = LIFI_ADDRESS; + + bytes[] memory payloads = new bytes[](2); + payloads[0] = abi.encodeCall(ERC20.approve, (LIFI_ADDRESS, _amount)); + payloads[1] = _txdata; + + IColony(address(this)).makeProxyArbitraryTransactions(_chainId, targets, payloads); + } + } + function getNonRewardPotsTotal(address _token) public view returns (uint256) { return nonRewardPotsTotal[_token]; } diff --git a/contracts/colony/ColonyStorage.sol b/contracts/colony/ColonyStorage.sol index c20e091211..9ac320117a 100755 --- a/contracts/colony/ColonyStorage.sol +++ b/contracts/colony/ColonyStorage.sol @@ -254,7 +254,10 @@ contract ColonyStorage is ColonyDataTypes, ColonyNetworkDataTypes, DSMath, Commo function isAuthorized(address src, uint256 domainId, bytes4 sig) internal view returns (bool) { return - (src == owner) || DomainRoles(address(authority)).canCall(src, domainId, address(this), sig); + // TODO: Is there a reason we didn't have (src==address(this)?) + (src == owner) || + (src == address(this)) || + DomainRoles(address(authority)).canCall(src, domainId, address(this), sig); } function isContract(address addr) internal returns (bool) { diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 917951ef57..52e25d74fa 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -944,6 +944,27 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, uint256 _amount ) external; + /// @notice Exchange funds between two tokens, potentially between chains + /// The tokens being swapped are held by a proxy contract + /// @param _permissionDomainId The domainId in which I have the permission to take this action + /// @param _childSkillIndex The child index in `_permissionDomainId` where we can find `_domainId` + /// @param _domainId Id of the domain + /// @param _txdata Transaction data for the exchange + /// @param _value Value of the transaction + /// @param _chainId The chainId of the token + /// @param _token Address of the token. If the native token is being swapped, can be anything and _amount should be 0. + /// @param _amount Amount of tokens to exchange + function exchangeProxyHeldTokensViaLiFi( + uint256 _permissionDomainId, + uint256 _childSkillIndex, + uint256 _domainId, + bytes memory _txdata, + uint256 _value, + uint256 _chainId, + address _token, + uint256 _amount + ) external; + /// @notice Used by the bridge to indicate that funds have been claimed on another chain. /// @param _chainId Chain id of the chain where the funds were claimed /// @param _token Address of the token, `0x0` value indicates Ether diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index a303f67671..19afd1a3fa 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -412,6 +412,7 @@ Emit a positive skill reputation update. Available only to Root role holders |_amount|int256|The (positive) amount of reputation to gain +<<<<<<< HEAD ### ▸ `enterRecoveryMode()` Put colony network mining into recovery mode. Can only be called by user with recovery role. @@ -419,6 +420,28 @@ Put colony network mining into recovery mode. Can only be called by user with re +||||||| parent of 4e12a606 (Proxy-held to proxy-held token swapping) +======= +### ▸ `exchangeProxyHeldTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, uint256 _chainId, address _token, uint256 _amount)` + +Exchange funds between two tokens, potentially between chains The tokens being swapped are held by a proxy contract + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which I have the permission to take this action +|_childSkillIndex|uint256|The child index in `_permissionDomainId` where we can find `_domainId` +|_domainId|uint256|Id of the domain +|_txdata|bytes|Transaction data for the exchange +|_value|uint256|Value of the transaction +|_chainId|uint256|The chainId of the token +|_token|address|Address of the token. If the native token is being swapped, can be anything and _amount should be 0. +|_amount|uint256|Amount of tokens to exchange + + +>>>>>>> 4e12a606 (Proxy-held to proxy-held token swapping) ### ▸ `exchangeTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, address _token, uint256 _amount)` Exchange funds between two tokens, potentially between chains diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 79b21ca94b..a033ae22af 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -913,6 +913,100 @@ contract("Cross-chain", (accounts) => { const balance = await colony.getFundingPotProxyBalance(domain.fundingPotId, foreignChainId, foreignToken.address); expect(balance.toHexString()).to.equal(ethers.utils.parseEther("50").toHexString()); }); + + it("can exchange tokens in a domain held by the proxy to different tokens also on the proxy", async () => { + const foreignTokenFactory = new ethers.ContractFactory(MetaTxToken.abi, MetaTxToken.bytecode, ethersForeignSigner); + const foreignToken2 = await foreignTokenFactory.deploy("TT2", "TT2", 18); + await (await foreignToken2.unlock()).wait(); + await (await foreignToken.unlock()).wait(); + + let tx = await foreignToken["mint(address,uint256)"](proxyColony.address, ethers.utils.parseEther("100")); + await tx.wait(); + let p = guardianSpy.getPromiseForNextBridgedTransaction(); + + tx = await proxyColony.claimTokens(foreignToken.address); + await tx.wait(); + await p; + + // Check bookkeeping on the home chain + const balance = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + expect(balance.toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); + + // Move tokens from domain 1 to domain 2 + tx = await colony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); + await tx.wait(); + + const domain1 = await colony.getDomain(1); + const domain2 = await colony.getDomain(2); + console.log(domain2); + const fundingPot = await colony.getFundingPot(domain2.fundingPotId); + console.log(fundingPot); + + tx = await colony["moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)"]( + 1, + UINT256_MAX_ETHERS, + 1, + UINT256_MAX_ETHERS, + 0, + domain1.fundingPotId, + domain2.fundingPotId, + ethers.utils.parseEther("70"), + foreignChainId, + foreignToken.address, + ); + await tx.wait(); + console.log("moved"); + // Exchange tokens + const domain2ReceiverAddress = await homeColonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); + + const lifi = new ethers.Contract(LIFI_ADDRESS, LiFiFacetProxyMock.abi, ethersForeignSigner); // Signer doesn't really matter, + // we're just calling encodeFunctionData + + const txdata = lifi.interface.encodeFunctionData("swapTokensMock(uint256,address,uint256,address,address,uint256)", [ + foreignChainId, + foreignToken.address, + foreignChainId, + foreignToken2.address, + domain2ReceiverAddress, + ethers.utils.parseEther("70"), + ]); + + p = guardianSpy.getPromiseForNextBridgedTransaction(); + tx = await colony.exchangeProxyHeldTokensViaLiFi(1, 0, 2, txdata, 0, foreignChainId, foreignToken.address, ethers.utils.parseEther("70")); + await tx.wait(); + + const receipt = await p; + const swapEvent = receipt.logs + .filter((e) => e.address === LIFI_ADDRESS) + .map((e) => lifi.interface.parseLog(e)) + .filter((e) => e.name === "SwapTokens")[0]; + expect(swapEvent).to.not.be.undefined; + + // Okay, so we saw the SwapTokens event. Let's do vaguely what it said for the test, + // but in practise this would be the responsibility of whatever entity we've paid to do it + // through LiFi. + await foreignToken2["mint(address,uint256)"](swapEvent.args._toAddress, swapEvent.args._amount); // Implicit 1:1 exchange rate + + // Sweep token in to the proxy + p = guardianSpy.getPromiseForNextBridgedTransaction(); + tx = await proxyColony.claimTokensForDomain(foreignToken2.address, 2, { gasLimit: 1000000 }); + await tx.wait(); + + // Wait for the sweep to be bridged + await p; + + // Check bookkeeping on the home chain + const balance1 = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + const balance2 = await colony.getFundingPotProxyBalance(2, foreignChainId, foreignToken2.address); + expect(balance1.toHexString()).to.equal(ethers.utils.parseEther("30").toHexString()); + expect(balance2.toHexString()).to.equal(ethers.utils.parseEther("70").toHexString()); + + // And check balances of the proxy with the tokens + const balance3 = await foreignToken.balanceOf(proxyColony.address); + const balance4 = await foreignToken2.balanceOf(proxyColony.address); + expect(balance3.toHexString()).to.equal(ethers.utils.parseEther("30").toHexString()); + expect(balance4.toHexString()).to.equal(ethers.utils.parseEther("70").toHexString()); + }); }); describe("making arbitrary transactions on another chain", async () => { diff --git a/test/deploy-proxy-network-fixture.js b/test/deploy-proxy-network-fixture.js index 1e3b5b44ab..1aafbd92a4 100644 --- a/test/deploy-proxy-network-fixture.js +++ b/test/deploy-proxy-network-fixture.js @@ -6,6 +6,7 @@ const Resolver = artifacts.require("Resolver"); const DomainTokenReceiver = artifacts.require("DomainTokenReceiver"); const truffleContract = require("@truffle/contract"); +const { setCode } = require("@nomicfoundation/hardhat-network-helpers"); const createXABI = require("../lib/createx/artifacts/src/ICreateX.sol/ICreateX.json"); const { setupEtherRouter } = require("../helpers/upgradable-contracts"); @@ -15,6 +16,7 @@ const { setupProxyColonyNetwork } = require("../helpers/upgradable-contracts"); const ProxyColonyNetwork = artifacts.require("ProxyColonyNetwork"); const ProxyColony = artifacts.require("ProxyColony"); +const LiFiFacetProxyMock = artifacts.require("LiFiFacetProxyMock"); module.exports = async () => { const accounts = await web3.eth.getAccounts(); @@ -63,4 +65,18 @@ module.exports = async () => { const domainTokenReceiverImplementation = await DomainTokenReceiver.new(); await setupEtherRouter("common", "DomainTokenReceiver", { DomainTokenReceiver: domainTokenReceiverImplementation.address }, resolver); await proxyColonyNetwork.setDomainTokenReceiverResolver(resolver.address); + + // Deploy LiFiMock to LiFi address + try { + await setCode("0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE", LiFiFacetProxyMock.deployedBytecode); + } catch (error) { + if (error.message.includes("OnlyHardhatNetworkError")) { + await new Promise(function (resolve) { + web3.provider.send( + { method: "evm_setCode", params: ["0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE", LiFiFacetProxyMock.deployedBytecode] }, + resolve, + ); + }); + } + } }; From 2dfa168f5a1d55a1bc946164a5364c3081d01438 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 4 Oct 2024 11:41:19 +0100 Subject: [PATCH 39/72] Relayer actually uses destination chain to make decisions --- packages/wormhole-relayer/index.ts | 31 ++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/wormhole-relayer/index.ts b/packages/wormhole-relayer/index.ts index 2285266ce3..2aa5bb7bd0 100644 --- a/packages/wormhole-relayer/index.ts +++ b/packages/wormhole-relayer/index.ts @@ -1,5 +1,5 @@ import { Environment, StandardRelayerContext, RelayerApp, providers } from "@wormhole-foundation/relayer-engine"; -import { CHAIN_ID_ARBITRUM_SEPOLIA, CHAIN_ID_SEPOLIA } from "@certusone/wormhole-sdk"; +// import { CHAIN_ID_ARBITRUM_SEPOLIA, CHAIN_ID_SEPOLIA } from "@certusone/wormhole-sdk"; import * as path from "path"; @@ -112,17 +112,32 @@ const loader = new TruffleLoader({ console.log(`Got a VAA with sequence: ${vaa.sequence} from with txhash: ${hash}`); - let destinationBridge; + const [ + destinationEvmChainId, + // destinationAddress, + // payload + ] = new ethers.utils.AbiCoder().decode(["uint256", "address", "bytes"], `0x${vaa.payload.toString("hex")}`); - if (vaa.emitterChain === CHAIN_ID_ARBITRUM_SEPOLIA) { - destinationBridge = colonyBridges[CHAIN_ID_SEPOLIA]; - } else if (vaa.emitterChain === CHAIN_ID_SEPOLIA) { - destinationBridge = colonyBridges[CHAIN_ID_ARBITRUM_SEPOLIA]; - } else { - console.log("Unknown chain", vaa.emitterChain); + const destinationChainConfig = Object.values(config.chains).find((c) => c.evmChainId === destinationEvmChainId.toNumber()); + if (!destinationChainConfig) { + console.log("No destination chain config found for chain id", destinationEvmChainId.toNumber()); return next(); } + if (!destinationChainConfig.payForGas) { + console.log("We do not pay for gas on destination chain. Skipping"); + return next(); + } + + const destinationWormholeId = Object.keys(config.chains).find((wormholeChainId) => { + return config.chains[wormholeChainId].evmChainId === destinationEvmChainId.toNumber(); + }); + if (!destinationWormholeId) { + console.log("No wormhole chain id found for destination chain id", destinationEvmChainId.toNumber()); + return next(); + } + + const destinationBridge = colonyBridges[destinationWormholeId]; try { // TODO: Explicit gas limit is a nod to tests... const tx = await destinationBridge.receiveMessage(ctx.vaaBytes, { gasLimit: 1000000 }); From e58b7daf22ed43bcb84fef60e5c72ebe221d52ec Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 16 Oct 2024 11:40:45 +0100 Subject: [PATCH 40/72] Post-rebase fixing --- contracts/colony/ColonyFunding.sol | 137 +++++++----------- .../colonyNetwork/ColonyNetworkDeployer.sol | 109 -------------- contracts/colonyNetwork/IColonyNetwork.sol | 19 --- contracts/common/CallWithGuards.sol | 1 - hardhat.config.js | 4 +- 5 files changed, 51 insertions(+), 219 deletions(-) diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index fb2ca7fe20..451bcadca4 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -129,18 +129,8 @@ contract ColonyFunding is // able to do with recovery mode. remainder = toClaim - feeToPay; nonRewardPotsTotal[_token] += remainder; - setFundingPotBalance( - 1, - block.chainid, - _token, - getFundingPotBalance(1, block.chainid, _token) + remainder - ); - setFundingPotBalance( - 0, - block.chainid, - _token, - getFundingPotBalance(0, block.chainid, _token) + feeToPay - ); + incrementFundingPotBalance(0, block.chainid, _token, feeToPay); + incrementFundingPotBalance(1, block.chainid, _token, remainder); emit ColonyFundsClaimed(msgSender(), _token, feeToPay, remainder); } @@ -166,13 +156,7 @@ contract ColonyFunding is nonRewardPotsTotal[_token] += remainder; // fundingPots[0].balance[_token] += feeToPay; - setFundingPotBalance( - 0, - block.chainid, - _token, - getFundingPotBalance(0, block.chainid, _token) + feeToPay - ); - + incrementFundingPotBalance(0, block.chainid, _token, feeToPay); uint256 fundingPotId = domains[_domainId].fundingPotId; uint256 approvedAmount = domainReputationApproval[_domainId]; @@ -264,19 +248,8 @@ contract ColonyFunding is } // Deduct the amount from the domain - setFundingPotBalance( - domain.fundingPotId, - block.chainid, - _token, - getFundingPotBalance(domain.fundingPotId, block.chainid, _token) - _amount - ); - - setFundingPotBalance( - domain.fundingPotId, - block.chainid, - address(0x0), - getFundingPotBalance(domain.fundingPotId, block.chainid, _token) - _value - ); + decrementFundingPotBalance(domain.fundingPotId, block.chainid, _token, _amount); + decrementFundingPotBalance(domain.fundingPotId, block.chainid, address(0x0), _value); require(ERC20Extended(_token).approve(LIFI_ADDRESS, _amount), "colony-approve-failed"); (bool success, ) = LIFI_ADDRESS.call{ value: _value }(_txdata); @@ -289,21 +262,21 @@ contract ColonyFunding is uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, - uint256 _value, uint256 _chainId, address _token, uint256 _amount ) public stoppable authDomain(_permissionDomainId, _childSkillIndex, _domainId) { // TODO: Colony Network fee + decrementFundingPotBalance(d.fundingPotId, _chainId, _token, _amount); Domain storage d = domains[_domainId]; - // Check the domain has enough for what is if (_token == address(0x0)) { require( _value + _amount <= getFundingPotBalance(d.fundingPotId, _chainId, _token), "colony-insufficient-funds" ); + decrementFundingPotBalance(d.fundingPotId, _chainId, address(0x0), _value); } else { require( _amount <= getFundingPotBalance(d.fundingPotId, _chainId, _token), @@ -315,20 +288,10 @@ contract ColonyFunding is ); } - // Deduct the amount from the domain - setFundingPotBalance( - d.fundingPotId, - _chainId, - _token, - getFundingPotBalance(d.fundingPotId, _chainId, _token) - _amount + decrementFundingPotBalance(d.fundingPotId, _chainId, _token, _amount); ); - // Deduct the value from the domain - setFundingPotBalance( - d.fundingPotId, - _chainId, - address(0x0), - getFundingPotBalance(d.fundingPotId, _chainId, _token) - _value + decrementFundingPotBalance(d.fundingPotId, _chainId, address(0x0), _value); ); // Build and send the transaction @@ -540,17 +503,6 @@ contract ColonyFunding is function getFundingPot( uint256 _potId ) - public - view - returns ( - FundingPotAssociatedType associatedType, - uint256 associatedTypeId, - uint256 payoutsWeCannotMake - ) - { - FundingPot storage fundingPot = fundingPots[_potId]; - return (fundingPot.associatedType, fundingPot.associatedTypeId, fundingPot.payoutsWeCannotMake); - } function getDomainFromFundingPot(uint256 _fundingPotId) public view returns (uint256 domainId) { require(_fundingPotId <= fundingPotCount, "colony-funding-nonexistent-pot"); @@ -563,6 +515,17 @@ contract ColonyFunding is } else if (fundingPot.associatedType == FundingPotAssociatedType.DEPRECATED_Payment) { domainId = DEPRECATED_payments[fundingPot.associatedTypeId].domainId; } else if (fundingPot.associatedType == FundingPotAssociatedType.Expenditure) { + returns ( + FundingPotAssociatedType associatedType, + uint256 associatedTypeId, + uint256 payoutsWeCannotMake + ) + { + FundingPot storage fundingPot = fundingPots[_potId]; + return (fundingPot.associatedType, fundingPot.associatedTypeId, fundingPot.payoutsWeCannotMake); + } + decrementFundingPotBalance(_fromPot, _chainId, _token, _amount); + incrementFundingPotBalance(_toPot, _chainId, _token, _amount); domainId = expenditures[fundingPot.associatedTypeId].domainId; } else { // If rewards pot, return root domain. @@ -576,18 +539,8 @@ contract ColonyFunding is } // Internal - - function moveFundsBetweenPotsFunctionality( - uint256 _fromPot, - uint256 _toPot, - uint256 _amount, - uint256 _chainId, - address _token - ) internal { - FundingPot storage fromPot = fundingPots[_fromPot]; - FundingPot storage toPot = fundingPots[_toPot]; - - setFundingPotBalance( + decrementFundingPotBalance(_fromPot, _chainId, _token, _amount); + incrementFundingPotBalance(_toPot, _chainId, _token, _amount); _fromPot, _chainId, _token, @@ -695,7 +648,12 @@ contract ColonyFunding is function setExpenditurePayoutsInternal( uint256 _id, - uint256[] memory _slots, + uint256 runningTotal = previousTotal; + + for (uint256 i; i < _slots.length; i++) { + require(_amounts[i] <= MAX_PAYOUT, "colony-payout-too-large"); + uint256 currentPayout = getExpenditureSlotPayout(_id, _slots[i], _chainId, _token); + // uint256 currentPayout = expenditureSlotPayouts[_id][_slots[i]][_token]; uint256 _chainId, address _token, uint256[] memory _amounts @@ -706,12 +664,7 @@ contract ColonyFunding is assert(fundingPot.associatedType == FundingPotAssociatedType.Expenditure); uint256 previousTotal = getFundingPotPayout(expenditures[_id].fundingPotId, _chainId, _token); - uint256 runningTotal = previousTotal; - - for (uint256 i; i < _slots.length; i++) { - require(_amounts[i] <= MAX_PAYOUT, "colony-payout-too-large"); - uint256 currentPayout = getExpenditureSlotPayout(_id, _slots[i], _chainId, _token); - // uint256 currentPayout = expenditureSlotPayouts[_id][_slots[i]][_token]; + decrementFundingPotBalance(_fundingPotId, _chainId, _token, _payout); setExpenditureSlotPayout(_id, _slots[i], _chainId, _token, _amounts[i]); // expenditureSlotPayouts[_id][_slots[i]][_token] = _amounts[i]; runningTotal = (runningTotal - currentPayout) + _amounts[i]; @@ -736,12 +689,7 @@ contract ColonyFunding is address _token, uint256 _payout, address payable _user - ) private returns (uint256) { - refundDomain(_fundingPotId, _chainId, _token); - - IColonyNetwork colonyNetworkContract = IColonyNetwork(colonyNetworkAddress); - address payable metaColonyAddress = colonyNetworkContract.getMetaColony(); - + decrementFundingPotBalance(_fundingPotId, _chainId, _token, _payout); setFundingPotBalance( _fundingPotId, _chainId, @@ -789,9 +737,9 @@ contract ColonyFunding is _payout ); IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, payload); - } - + getFundingPotBalance(_fundingPotId, _chainId, _token) // slither-disable-next-line reentrancy-unlimited-gas + function incrementFundingPotBalance( emit PayoutClaimed(msgSender(), _fundingPotId, _token, payoutToUser); return payoutToUser; @@ -800,7 +748,7 @@ contract ColonyFunding is function refundDomain(uint256 _fundingPotId, uint256 _chainId, address _token) private { if ( getFundingPotPayout(_fundingPotId, _chainId, _token) < - getFundingPotBalance(_fundingPotId, _chainId, _token) + fundingPots[_potId].chainBalances[_chainId][_token] -= _decrement; ) { uint256 domainId = getDomainFromFundingPot(_fundingPotId); uint256 surplus = getFundingPotBalance(_fundingPotId, _chainId, _token) - @@ -808,17 +756,30 @@ contract ColonyFunding is moveFundsBetweenPotsFunctionality( _fundingPotId, - domains[domainId].fundingPotId, + function incrementFundingPotBalance( surplus, _chainId, _token - ); + uint256 _increment + ) internal { + } + fundingPots[_potId].balance[_token] += _increment; + function getFundingPotBalance( + fundingPots[_potId].chainBalances[_chainId][_token] += _increment; } } - function getFundingPotBalance( + function decrementFundingPotBalance( uint256 _potId, uint256 _chainId, + address _token, + uint256 _decrement + ) internal { + if (_chainId == block.chainid) { + fundingPots[_potId].balance[_token] -= _decrement; + } else { + fundingPots[_potId].chainBalances[_chainId][_token] -= _decrement; + uint256 _chainId, address _token ) internal view returns (uint256) { if (_chainId == block.chainid) { diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index 82eae174d6..24e08b77b7 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -197,7 +197,6 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage, DomainReceiverManagement return domainReceiverResolverAddress; } -<<<<<<< HEAD function idempotentDeployDomainTokenReceiver( uint256 _domainId ) public stoppable calledByColony returns (address domainTokenReceiverAddress) { @@ -232,118 +231,10 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage, DomainReceiverManagement return domainTokenReceiverAddress; } - function getDomainTokenReceiverAddress( - address _colony, - uint256 _domainId - ) public view returns (address) { - bytes32 salt = getDomainTokenReceiverDeploySalt(_colony, _domainId); - - // To get the correct address, we have to mimic the _guard functionality of CreateX - bytes32 guardedSalt = keccak256(abi.encode(bytes32(uint256(uint160(address(this)))), salt)); - return ICreateX(CREATEX_ADDRESS).computeCreate3Address(guardedSalt); - } - - function getDomainTokenReceiverDeploySalt( - address _colony, - uint256 _domainId - ) internal view returns (bytes32) { - // Calculate the address the domain should be receiving funds at - // We only want Colony Networks to be able to deploy to the same address, - // so we use the permissioned deploy protection feature of CreateX, and set - // the first 160 bits of the salt to the address of this contract. - - bytes32 salt = bytes32(uint256(uint160(address(this)))) << 96; - - bytes32 additionalSalt = keccak256(abi.encode(_colony, _domainId)); - // We use the first 88 bits of the additional salt, which is a function of the colony and domainId, - // to add entropy in the last 88 bits of the salt - salt = salt | (additionalSalt >> 168); - // We have set the first 160 bits, and the last 88 bits of the salt - // Note that this leaves byte 21 of the salt as zero (0x00), which disables cross-chain - // redeployment protection in createX. - // This is intentional, as we want to allow the same receiver to be deployed on different chains - return salt; -||||||| parent of 125a5a92 (First pass at cross-chain domain-level token swaps) - function checkDomainTokenReceiverDeployed( - uint256 _domainId - ) public calledByColony returns (address domainTokenReceiverAddress) { - // Calculate the address the domain should be receiving funds at - domainTokenReceiverAddress = getDomainTokenReceiverAddress(msgSender(), _domainId); - - if (!isContract(domainTokenReceiverAddress)) { - // Then deploy the contract - bytes32 salt = getDomainTokenReceiverDeploySalt(msgSender(), _domainId); - ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( - salt, - type(EtherRouterCreate3).creationCode, - abi.encodeWithSignature("setOwner(address)", (address(this))), - ICreateX.Values(0, 0) - ); - } - - // Check it's got the right resolver - try EtherRouter(payable(domainTokenReceiverAddress)).resolver() returns (Resolver resolver) { - if (address(resolver) != domainReceiverResolverAddress) { - EtherRouter(payable(domainTokenReceiverAddress)).setResolver(domainReceiverResolverAddress); - } - } catch { - revert("colony-network-domain-receiver-not-etherrouter"); - } - - // Check it's set up correctly - - if (DomainTokenReceiver(domainTokenReceiverAddress).getColonyAddress() != msgSender()) { - DomainTokenReceiver(domainTokenReceiverAddress).setColonyAddress(msgSender()); - } - - return domainTokenReceiverAddress; - } - - function isContract(address addr) internal view returns (bool) { - uint256 size; - assembly { - size := extcodesize(addr) - } - return size > 0; - } - - function getDomainTokenReceiverAddress( - address _colony, - uint256 _domainId - ) public view returns (address) { - bytes32 salt = getDomainTokenReceiverDeploySalt(_colony, _domainId); - - // To get the correct address, we have to mimic the _guard functionality of CreateX - bytes32 guardedSalt = keccak256(abi.encode(bytes32(uint256(uint160(address(this)))), salt)); - return ICreateX(CREATEX_ADDRESS).computeCreate3Address(guardedSalt); - } - - function getDomainTokenReceiverDeploySalt( - address _colony, - uint256 _domainId - ) internal view returns (bytes32) { - // Calculate the address the domain should be receiving funds at - // We only want Colony Networks to be able to deploy to the same address, - // so we use the permissioned deploy protection feature of CreateX, and set - // the first 160 bits of the salt to the address of this contract. - - bytes32 salt = bytes32(uint256(uint160(address(this)))) << 96; - - bytes32 additionalSalt = keccak256(abi.encode(_colony, _domainId)); - // We use the first 88 bits of the additional salt, which is a function of the colony and domainId, - // to add entropy in the last 88 bits of the salt - salt = salt | (additionalSalt >> 168); - // We have set the first 160 bits, and the last 88 bits of the salt - // Note that this leaves byte 21 of the salt as zero (0x00), which disables cross-chain - // redeployment protection in createX. - // This is intentional, as we want to allow the same receiver to be deployed on different chains - return salt; -======= function msgSenderIsColony() internal view override returns (bool) { require(_isColony[msgSender()], "colony-caller-must-be-colony"); assert(msgSender() == msg.sender); return true; ->>>>>>> 125a5a92 (First pass at cross-chain domain-level token swaps) } function isStopped() internal view override returns (bool) { diff --git a/contracts/colonyNetwork/IColonyNetwork.sol b/contracts/colonyNetwork/IColonyNetwork.sol index c978b1acd2..768bf934dd 100644 --- a/contracts/colonyNetwork/IColonyNetwork.sol +++ b/contracts/colonyNetwork/IColonyNetwork.sol @@ -550,25 +550,6 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery, IBasicMetaTransac uint256 _domainId ) external returns (address domainTokenReceiverAddress); - /// @notice Function to set the resolver that should be used by DomainTokenReceivers - /// @dev The next time a claim for a domain is called, they will first be updated to this resolver - /// @param _resolver The address of the resolver to use - function setDomainTokenReceiverResolver(address _resolver) external; - - /// @notice Get the current DomainTokenReceiver resolver - /// @dev Note that some Receivers might be using an old resolver - /// @return resolver The address of the current resolver - function getDomainTokenReceiverResolver() external view returns (address resolver); - - /// @notice Get the DomainTokenReceiver address for a particular domain - /// @param _colonyAddress The address of the colony - /// @param _domainId The domainId of the domain - /// @return domainTokenReceiverAddress The address of the DomainTokenReceiver (which may or may not be deployed currently) - function getDomainTokenReceiverAddress( - address _colonyAddress, - uint256 _domainId - ) external view returns (address domainTokenReceiverAddress); - /// @notice Handles calls to create a new colony on another chain /// @dev Should only be called by a colony, if you're trying to call this directly you're doing something wrong /// @param _destinationChainId The chainId of the chain to create the colony on diff --git a/contracts/common/CallWithGuards.sol b/contracts/common/CallWithGuards.sol index 10fb0d5543..078b15d825 100644 --- a/contracts/common/CallWithGuards.sol +++ b/contracts/common/CallWithGuards.sol @@ -17,7 +17,6 @@ pragma solidity 0.8.28; along with The Colony Network. If not, see . */ - import { IsContract } from "./IsContract.sol"; contract CallWithGuards is IsContract { diff --git a/hardhat.config.js b/hardhat.config.js index 4c9a4b5539..c1c4d32cc1 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -82,9 +82,9 @@ task("test", "Run tests").setAction(async () => { }); task("ensureCreateXDeployed", "Ensure CreateX is deployed").setAction(async () => { - const { deployCreateXIfNeeded } = require("./helpers/test-helper"); // eslint-disable-line global-require + const { idempotentDeployCreateX } = require("./helpers/test-helper"); // eslint-disable-line global-require - await deployCreateXIfNeeded(); + await idempotentDeployCreateX(); }); task("node", "Run a node, and output ganache-accounts.json for backwards-compatability").setAction(async () => { From bdeddca0ab2d99f125271cdc4af401f5f4e6bff7 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 16 Oct 2024 11:42:05 +0100 Subject: [PATCH 41/72] Introduce increment/decrement funding pot for stack size reasons --- contracts/colony/ColonyFunding.sol | 124 +++++++++--------- .../colony-network-recovery.js | 6 - 2 files changed, 62 insertions(+), 68 deletions(-) diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index 451bcadca4..39a7849f7e 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -262,21 +262,21 @@ contract ColonyFunding is uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, + uint256 _value, uint256 _chainId, address _token, uint256 _amount ) public stoppable authDomain(_permissionDomainId, _childSkillIndex, _domainId) { // TODO: Colony Network fee - decrementFundingPotBalance(d.fundingPotId, _chainId, _token, _amount); Domain storage d = domains[_domainId]; + // Check the domain has enough for what is if (_token == address(0x0)) { require( _value + _amount <= getFundingPotBalance(d.fundingPotId, _chainId, _token), "colony-insufficient-funds" ); - decrementFundingPotBalance(d.fundingPotId, _chainId, address(0x0), _value); } else { require( _amount <= getFundingPotBalance(d.fundingPotId, _chainId, _token), @@ -288,11 +288,11 @@ contract ColonyFunding is ); } + // Deduct the amount from the domain decrementFundingPotBalance(d.fundingPotId, _chainId, _token, _amount); - ); + // Deduct the value from the domain decrementFundingPotBalance(d.fundingPotId, _chainId, address(0x0), _value); - ); // Build and send the transaction if (_token == address(0)) { @@ -503,6 +503,17 @@ contract ColonyFunding is function getFundingPot( uint256 _potId ) + public + view + returns ( + FundingPotAssociatedType associatedType, + uint256 associatedTypeId, + uint256 payoutsWeCannotMake + ) + { + FundingPot storage fundingPot = fundingPots[_potId]; + return (fundingPot.associatedType, fundingPot.associatedTypeId, fundingPot.payoutsWeCannotMake); + } function getDomainFromFundingPot(uint256 _fundingPotId) public view returns (uint256 domainId) { require(_fundingPotId <= fundingPotCount, "colony-funding-nonexistent-pot"); @@ -515,17 +526,6 @@ contract ColonyFunding is } else if (fundingPot.associatedType == FundingPotAssociatedType.DEPRECATED_Payment) { domainId = DEPRECATED_payments[fundingPot.associatedTypeId].domainId; } else if (fundingPot.associatedType == FundingPotAssociatedType.Expenditure) { - returns ( - FundingPotAssociatedType associatedType, - uint256 associatedTypeId, - uint256 payoutsWeCannotMake - ) - { - FundingPot storage fundingPot = fundingPots[_potId]; - return (fundingPot.associatedType, fundingPot.associatedTypeId, fundingPot.payoutsWeCannotMake); - } - decrementFundingPotBalance(_fromPot, _chainId, _token, _amount); - incrementFundingPotBalance(_toPot, _chainId, _token, _amount); domainId = expenditures[fundingPot.associatedTypeId].domainId; } else { // If rewards pot, return root domain. @@ -539,19 +539,19 @@ contract ColonyFunding is } // Internal + + function moveFundsBetweenPotsFunctionality( + uint256 _fromPot, + uint256 _toPot, + uint256 _amount, + uint256 _chainId, + address _token + ) internal { + FundingPot storage fromPot = fundingPots[_fromPot]; + FundingPot storage toPot = fundingPots[_toPot]; + decrementFundingPotBalance(_fromPot, _chainId, _token, _amount); incrementFundingPotBalance(_toPot, _chainId, _token, _amount); - _fromPot, - _chainId, - _token, - getFundingPotBalance(_fromPot, _chainId, _token) - _amount - ); - setFundingPotBalance( - _toPot, - _chainId, - _token, - getFundingPotBalance(_toPot, _chainId, _token) + _amount - ); if (_fromPot == 1 && _chainId == block.chainid) { // If we're moving from the root pot, then check we haven't dropped below what we need @@ -648,12 +648,7 @@ contract ColonyFunding is function setExpenditurePayoutsInternal( uint256 _id, - uint256 runningTotal = previousTotal; - - for (uint256 i; i < _slots.length; i++) { - require(_amounts[i] <= MAX_PAYOUT, "colony-payout-too-large"); - uint256 currentPayout = getExpenditureSlotPayout(_id, _slots[i], _chainId, _token); - // uint256 currentPayout = expenditureSlotPayouts[_id][_slots[i]][_token]; + uint256[] memory _slots, uint256 _chainId, address _token, uint256[] memory _amounts @@ -664,7 +659,12 @@ contract ColonyFunding is assert(fundingPot.associatedType == FundingPotAssociatedType.Expenditure); uint256 previousTotal = getFundingPotPayout(expenditures[_id].fundingPotId, _chainId, _token); - decrementFundingPotBalance(_fundingPotId, _chainId, _token, _payout); + uint256 runningTotal = previousTotal; + + for (uint256 i; i < _slots.length; i++) { + require(_amounts[i] <= MAX_PAYOUT, "colony-payout-too-large"); + uint256 currentPayout = getExpenditureSlotPayout(_id, _slots[i], _chainId, _token); + // uint256 currentPayout = expenditureSlotPayouts[_id][_slots[i]][_token]; setExpenditureSlotPayout(_id, _slots[i], _chainId, _token, _amounts[i]); // expenditureSlotPayouts[_id][_slots[i]][_token] = _amounts[i]; runningTotal = (runningTotal - currentPayout) + _amounts[i]; @@ -689,13 +689,13 @@ contract ColonyFunding is address _token, uint256 _payout, address payable _user + ) private returns (uint256) { + refundDomain(_fundingPotId, _chainId, _token); + + IColonyNetwork colonyNetworkContract = IColonyNetwork(colonyNetworkAddress); + address payable metaColonyAddress = colonyNetworkContract.getMetaColony(); + decrementFundingPotBalance(_fundingPotId, _chainId, _token, _payout); - setFundingPotBalance( - _fundingPotId, - _chainId, - _token, - getFundingPotBalance(_fundingPotId, _chainId, _token) - _payout - ); setFundingPotPayout( _fundingPotId, _chainId, @@ -737,9 +737,9 @@ contract ColonyFunding is _payout ); IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, payload); - getFundingPotBalance(_fundingPotId, _chainId, _token) + } + // slither-disable-next-line reentrancy-unlimited-gas - function incrementFundingPotBalance( emit PayoutClaimed(msgSender(), _fundingPotId, _token, payoutToUser); return payoutToUser; @@ -748,7 +748,7 @@ contract ColonyFunding is function refundDomain(uint256 _fundingPotId, uint256 _chainId, address _token) private { if ( getFundingPotPayout(_fundingPotId, _chainId, _token) < - fundingPots[_potId].chainBalances[_chainId][_token] -= _decrement; + getFundingPotBalance(_fundingPotId, _chainId, _token) ) { uint256 domainId = getDomainFromFundingPot(_fundingPotId); uint256 surplus = getFundingPotBalance(_fundingPotId, _chainId, _token) - @@ -756,48 +756,48 @@ contract ColonyFunding is moveFundsBetweenPotsFunctionality( _fundingPotId, - function incrementFundingPotBalance( + domains[domainId].fundingPotId, surplus, _chainId, _token - uint256 _increment - ) internal { + ); + } } - fundingPots[_potId].balance[_token] += _increment; + function getFundingPotBalance( - fundingPots[_potId].chainBalances[_chainId][_token] += _increment; + uint256 _potId, + uint256 _chainId, + address _token + ) internal view returns (uint256) { + if (_chainId == block.chainid) { + return fundingPots[_potId].balance[_token]; } + return fundingPots[_potId].chainBalances[_chainId][_token]; } - function decrementFundingPotBalance( + function incrementFundingPotBalance( uint256 _potId, uint256 _chainId, address _token, - uint256 _decrement + uint256 _increment ) internal { if (_chainId == block.chainid) { - fundingPots[_potId].balance[_token] -= _decrement; + fundingPots[_potId].balance[_token] += _increment; } else { - fundingPots[_potId].chainBalances[_chainId][_token] -= _decrement; - uint256 _chainId, - address _token - ) internal view returns (uint256) { - if (_chainId == block.chainid) { - return fundingPots[_potId].balance[_token]; + fundingPots[_potId].chainBalances[_chainId][_token] += _increment; } - return fundingPots[_potId].chainBalances[_chainId][_token]; } - function setFundingPotBalance( + function decrementFundingPotBalance( uint256 _potId, uint256 _chainId, address _token, - uint256 _newValue - ) internal stoppable { + uint256 _decrement + ) internal { if (_chainId == block.chainid) { - fundingPots[_potId].balance[_token] = _newValue; + fundingPots[_potId].balance[_token] -= _decrement; } else { - fundingPots[_potId].chainBalances[_chainId][_token] = _newValue; + fundingPots[_potId].chainBalances[_chainId][_token] -= _decrement; } } diff --git a/test/contracts-network/colony-network-recovery.js b/test/contracts-network/colony-network-recovery.js index f88c1fc5b2..23c7753db2 100644 --- a/test/contracts-network/colony-network-recovery.js +++ b/test/contracts-network/colony-network-recovery.js @@ -193,12 +193,6 @@ contract("Colony Network Recovery", (accounts) => { await checkErrorRevert(colonyNetwork.setPayoutWhitelist(ADDRESS_ZERO, true), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.claimMiningReward(ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.startTokenAuction(ADDRESS_ZERO), "colony-in-recovery-mode"); - await checkErrorRevert(colonyNetwork.bridgeSkillIfNotMiningChain(1), "colony-in-recovery-mode"); - await checkErrorRevert(colonyNetwork.bridgePendingReputationUpdate(ADDRESS_ZERO, 0), "colony-in-recovery-mode"); - await checkErrorRevert(colonyNetwork.bridgeCurrentRootHash(ADDRESS_ZERO), "colony-in-recovery-mode"); - await checkErrorRevert(colonyNetwork.addReputationUpdateLogFromBridge(ADDRESS_ZERO, ADDRESS_ZERO, 0, 0, 0), "colony-in-recovery-mode"); - await checkErrorRevert(colonyNetwork.addPendingReputationUpdate(0, ADDRESS_ZERO), "colony-in-recovery-mode"); - await checkErrorRevert(colonyNetwork.setReputationRootHashFromBridge(HASHZERO, 0, 0), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.setDomainTokenReceiverResolver(ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.idempotentDeployDomainTokenReceiver(ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.bridgeMessage(1, "0x00000000"), "colony-in-recovery-mode"); From 5c734567c0c6cb4192b9078b93d7ac4e235da291 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 16 Oct 2024 15:56:40 +0100 Subject: [PATCH 42/72] Start using viaIR --- hardhat.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/hardhat.config.js b/hardhat.config.js index c1c4d32cc1..89b3f6d700 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -121,6 +121,7 @@ module.exports = { { version: "0.8.28", settings: { + viaIR: true, optimizer: { enabled: true, runs: 200, From 3a90ca2cf90baad0569dbd5bb79af18b91e5b8ca Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 16 Oct 2024 17:32:29 +0100 Subject: [PATCH 43/72] OneTxPayment cross-chain support --- contracts/colony/ColonyFunding.sol | 14 +++ contracts/colony/IColony.sol | 16 +++ contracts/extensions/OneTxPayment.sol | 134 +++++++++++++++++++-- docs/interfaces/extensions/onetxpayment.md | 42 +++++++ docs/interfaces/icolony.md | 17 +++ test/cross-chain/cross-chain.js | 84 ++++++++++++- test/extensions/one-tx-payment.js | 26 +++- 7 files changed, 317 insertions(+), 16 deletions(-) diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index 39a7849f7e..99dfa7638c 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -379,6 +379,20 @@ contract ColonyFunding is setExpenditurePayoutsInternal(_id, slots, block.chainid, _token, amounts); } + function setExpenditurePayout( + uint256 _id, + uint256 _slot, + uint256 _chainId, + address _token, + uint256 _amount + ) public stoppable expenditureDraft(_id) expenditureOnlyOwner(_id) { + uint256[] memory slots = new uint256[](1); + slots[0] = _slot; + uint256[] memory amounts = new uint256[](1); + amounts[0] = _amount; + setExpenditurePayoutsInternal(_id, slots, _chainId, _token, amounts); + } + int256 constant MAX_PAYOUT_MODIFIER = int256(WAD); int256 constant MIN_PAYOUT_MODIFIER = -int256(WAD); diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 52e25d74fa..952c9e0202 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -567,6 +567,22 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, uint256 _amount ) external; + /// @notice @deprecated + /// @notice Set the token payout on an expenditure slot. Can only be called by expenditure owner. + /// @dev Can only be called while expenditure is in draft state. + /// @param _id Id of the expenditure + /// @param _slot Number of the slot + /// @param _chainId The chainId of the token + /// @param _token Address of the token, `0x0` value indicates Ether + /// @param _amount Payout amount + function setExpenditurePayout( + uint256 _id, + uint256 _slot, + uint256 _chainId, + address _token, + uint256 _amount + ) external; + /// @notice Set the token payouts in given expenditure slots. Can only be called by expenditure owner. /// @dev Can only be called while expenditure is in draft state. /// @param _id Id of the expenditure diff --git a/contracts/extensions/OneTxPayment.sol b/contracts/extensions/OneTxPayment.sol index 7f6fa73384..cff4e41621 100644 --- a/contracts/extensions/OneTxPayment.sol +++ b/contracts/extensions/OneTxPayment.sol @@ -108,6 +108,7 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { /// @param _callerPermissionDomainId The domainId in which the _caller_ has the administration permission (must have funding in root) /// @param _callerChildSkillIndex Index of the _callerPermissionDomainId skill.children array to get /// @param _workers The addresses of the recipients of the payment + /// @param _chainIds The chainIds of the tokens being paid out /// @param _tokens Addresses of the tokens the payments are being made in. 0x00 for Ether. /// @param _amounts amounts of the tokens being paid out /// @param _domainId The domainId the payment should be coming from @@ -118,6 +119,7 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { uint256 _callerPermissionDomainId, uint256 _callerChildSkillIndex, address payable[] memory _workers, + uint256[] memory _chainIds, address[] memory _tokens, uint256[] memory _amounts, uint256 _domainId, @@ -139,7 +141,7 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { uint256 expenditureId = colony.makeExpenditure(1, _childSkillIndex, _domainId); uint256 fundingPotId = colony.getExpenditure(expenditureId).fundingPotId; - prepareFunding(_childSkillIndex, fundingPotId, _tokens, _amounts); + prepareFunding(_childSkillIndex, fundingPotId, _chainIds, _tokens, _amounts); uint256 slot; @@ -158,14 +160,60 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { require(_tokens[idx] > _tokens[idx - 1], "one-tx-payment-bad-token-order"); } - colony.setExpenditurePayout(expenditureId, slot, _tokens[idx], _amounts[idx]); + colony.setExpenditurePayout(expenditureId, slot, _chainIds[idx], _tokens[idx], _amounts[idx]); } - finalizeAndClaim(_permissionDomainId, _childSkillIndex, expenditureId, _workers, _tokens); + finalizeAndClaim( + _permissionDomainId, + _childSkillIndex, + expenditureId, + _workers, + _chainIds, + _tokens + ); emit OneTxPaymentMade(msgSender(), expenditureId, _workers.length); } + /// @notice deprecated makePayment without explicit chainId paramters for multichain + /// @param _permissionDomainId The domainId in which the _contract_ has permissions to add a payment and fund it + /// @param _childSkillIndex Index of the _permissionDomainId skill.children array to get + /// @param _callerPermissionDomainId The domainId in which the _caller_ has permissions to add a payment and fund it + /// @param _callerChildSkillIndex Index of the _callerPermissionDomainId skill.children array to get + /// @param _workers The addresses of the recipients of the payment + /// @param _tokens The addresses of the token the payments are being made in. 0x00 for Ether. + /// @param _amounts The amounts of the tokens being paid out + /// @param _domainId The domainId the payment should be coming from + /// @param _skillId The skillId that the payment should be marked with, possibly awarding reputation in this skill. + function makePayment( + uint256 _permissionDomainId, // Unused + uint256 _childSkillIndex, + uint256 _callerPermissionDomainId, + uint256 _callerChildSkillIndex, + address payable[] memory _workers, + address[] memory _tokens, + uint256[] memory _amounts, + uint256 _domainId, + uint256 _skillId + ) public { + uint256[] memory chainIds = new uint256[](_tokens.length); + for (uint256 i; i < _tokens.length; i++) { + chainIds[i] = block.chainid; + } + makePayment( + _permissionDomainId, + _childSkillIndex, + _callerPermissionDomainId, + _callerChildSkillIndex, + _workers, + chainIds, + _tokens, + _amounts, + _domainId, + _skillId + ); + } + /// @notice Completes a colony payment in a single transaction /// @dev Assumes that each entity holds administration and funding roles in the same domain, /// although contract and caller can have the permissions in different domains. @@ -175,6 +223,7 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { /// @param _callerPermissionDomainId The domainId in which the _caller_ has permissions to add a payment and fund it /// @param _callerChildSkillIndex Index of the _callerPermissionDomainId skill.children array to get /// @param _workers The addresses of the recipients of the payment + /// @param _chainIds The chainIds of the tokens being paid out /// @param _tokens The addresses of the token the payments are being made in. 0x00 for Ether. /// @param _amounts The amounts of the tokens being paid out /// @param _domainId The domainId the payment should be coming from @@ -185,6 +234,7 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { uint256 _callerPermissionDomainId, uint256 _callerChildSkillIndex, address payable[] memory _workers, + uint256[] memory _chainIds, address[] memory _tokens, uint256[] memory _amounts, uint256 _domainId, @@ -214,8 +264,10 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { prepareFundingWithinDomain( _permissionDomainId, _childSkillIndex, + _domainId, domainPotId, fundingPotId, + _chainIds, _tokens, _amounts ); @@ -240,16 +292,65 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { colony.setExpenditurePayout(expenditureId, slot, _tokens[idx], _amounts[idx]); } - finalizeAndClaim(_permissionDomainId, _childSkillIndex, expenditureId, _workers, _tokens); + finalizeAndClaim( + _permissionDomainId, + _childSkillIndex, + expenditureId, + _workers, + _chainIds, + _tokens + ); emit OneTxPaymentMade(msgSender(), expenditureId, _workers.length); } + /// @notice Deprecated makePaymentFundedFromDomain without explicit chainId paramters for multichain + /// @param _permissionDomainId The domainId in which the _contract_ has permissions to add a payment and fund it + /// @param _childSkillIndex Index of the _permissionDomainId skill.children array to get + /// @param _callerPermissionDomainId The domainId in which the _caller_ has permissions to add a payment and fund it + /// @param _callerChildSkillIndex Index of the _callerPermissionDomainId skill.children array to get + /// @param _workers The addresses of the recipients of the payment + /// @param _tokens The addresses of the token the payments are being made in. 0x00 for Ether. + /// @param _amounts The amounts of the tokens being paid out + /// @param _domainId The domainId the payment should be coming from + /// @param _skillId The skillId that the payment should be marked with, possibly awarding reputation in this skill. + function makePaymentFundedFromDomain( + uint256 _permissionDomainId, + uint256 _childSkillIndex, + uint256 _callerPermissionDomainId, + uint256 _callerChildSkillIndex, + address payable[] memory _workers, + address[] memory _tokens, + uint256[] memory _amounts, + uint256 _domainId, + uint256 _skillId + ) public { + uint256[] memory chainIds = new uint256[](_tokens.length); + for (uint256 i; i < _tokens.length; i++) { + chainIds[i] = block.chainid; + } + + makePaymentFundedFromDomain( + _permissionDomainId, + _childSkillIndex, + _callerPermissionDomainId, + _callerChildSkillIndex, + _workers, + chainIds, + _tokens, + _amounts, + _domainId, + _skillId + ); + } + function calculateUniqueAmounts( + uint256[] memory _chainIds, address[] memory _tokens, uint256[] memory _amounts - ) internal pure returns (uint256, address[] memory, uint256[] memory) { + ) internal pure returns (uint256, uint256[] memory, address[] memory, uint256[] memory) { uint256 uniqueTokensIdx; + uint256[] memory uniqueChainIds = new uint256[](_tokens.length); address[] memory uniqueTokens = new address[](_tokens.length); uint256[] memory uniqueAmounts = new uint256[](_tokens.length); @@ -266,35 +367,41 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { } if (!isMatch) { + uniqueChainIds[uniqueTokensIdx] = _chainIds[i]; uniqueTokens[uniqueTokensIdx] = _tokens[i]; uniqueAmounts[uniqueTokensIdx] = _amounts[i]; uniqueTokensIdx++; } } - return (uniqueTokensIdx, uniqueTokens, uniqueAmounts); + return (uniqueTokensIdx, uniqueChainIds, uniqueTokens, uniqueAmounts); } function prepareFunding( uint256 _childSkillIndex, uint256 _fundingPotId, + uint256[] memory _chainIds, address[] memory _tokens, uint256[] memory _amounts ) internal { ( uint256 uniqueTokensIdx, + uint256[] memory uniqueChainIds, address[] memory uniqueTokens, uint256[] memory uniqueAmounts - ) = calculateUniqueAmounts(_tokens, _amounts); + ) = calculateUniqueAmounts(_chainIds, _tokens, _amounts); for (uint256 i; i < uniqueTokensIdx; i++) { colony.moveFundsBetweenPots( + 1, + UINT256_MAX, 1, UINT256_MAX, _childSkillIndex, 1, _fundingPotId, uniqueAmounts[i], + uniqueChainIds[i], uniqueTokens[i] ); } @@ -303,25 +410,31 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { function prepareFundingWithinDomain( uint256 _permissionDomainId, uint256 _childSkillIndex, + uint256 _domainId, uint256 _domainPotId, uint256 _fundingPotId, + uint256[] memory _chainIds, address[] memory _tokens, uint256[] memory _amounts ) internal { ( uint256 uniqueTokensIdx, + uint256[] memory uinqueChainIds, address[] memory uniqueTokens, uint256[] memory uniqueAmounts - ) = calculateUniqueAmounts(_tokens, _amounts); + ) = calculateUniqueAmounts(_chainIds, _tokens, _amounts); for (uint256 i; i < uniqueTokensIdx; i++) { colony.moveFundsBetweenPots( _permissionDomainId, _childSkillIndex, - _childSkillIndex, + _domainId, + UINT256_MAX, + UINT256_MAX, _domainPotId, _fundingPotId, uniqueAmounts[i], + uinqueChainIds[i], uniqueTokens[i] ); } @@ -336,6 +449,7 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { uint256 _childSkillIndex, uint256 _expenditureId, address payable[] memory _workers, + uint256[] memory _chainIds, address[] memory _tokens ) internal { colony.finalizeExpenditure(_expenditureId); @@ -361,7 +475,7 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { if (idx == 0 || _workers[idx] != _workers[idx - 1]) { slot++; } - colony.claimExpenditurePayout(_expenditureId, slot, _tokens[idx]); + colony.claimExpenditurePayout(_expenditureId, slot, _chainIds[idx], _tokens[idx]); } } } diff --git a/docs/interfaces/extensions/onetxpayment.md b/docs/interfaces/extensions/onetxpayment.md index 81ed1f179c..ab86b91403 100644 --- a/docs/interfaces/extensions/onetxpayment.md +++ b/docs/interfaces/extensions/onetxpayment.md @@ -152,6 +152,26 @@ Configures the extension ### ▸ `makePayment(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _callerPermissionDomainId, uint256 _callerChildSkillIndex, address[] memory _workers, address[] memory _tokens, uint256[] memory _amounts, uint256 _domainId, uint256 _skillId)` +deprecated makePayment without explicit chainId paramters for multichain + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which the _contract_ has permissions to add a payment and fund it +|_childSkillIndex|uint256|Index of the _permissionDomainId skill.children array to get +|_callerPermissionDomainId|uint256|The domainId in which the _caller_ has permissions to add a payment and fund it +|_callerChildSkillIndex|uint256|Index of the _callerPermissionDomainId skill.children array to get +|_workers|address[]|The addresses of the recipients of the payment +|_tokens|address[]|The addresses of the token the payments are being made in. 0x00 for Ether. +|_amounts|uint256[]|The amounts of the tokens being paid out +|_domainId|uint256|The domainId the payment should be coming from +|_skillId|uint256|The skillId that the payment should be marked with, possibly awarding reputation in this skill. + + +### ▸ `makePayment(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _callerPermissionDomainId, uint256 _callerChildSkillIndex, address[] memory _workers, uint256[] memory _chainIds, address[] memory _tokens, uint256[] memory _amounts, uint256 _domainId, uint256 _skillId)` + Completes a colony payment in a single transaction *Note: Assumes that each entity holds administration and funding roles in the root domain* @@ -165,6 +185,7 @@ Completes a colony payment in a single transaction |_callerPermissionDomainId|uint256|The domainId in which the _caller_ has the administration permission (must have funding in root) |_callerChildSkillIndex|uint256|Index of the _callerPermissionDomainId skill.children array to get |_workers|address[]|The addresses of the recipients of the payment +|_chainIds|uint256[]|The chainIds of the tokens being paid out |_tokens|address[]|Addresses of the tokens the payments are being made in. 0x00 for Ether. |_amounts|uint256[]|amounts of the tokens being paid out |_domainId|uint256|The domainId the payment should be coming from @@ -173,6 +194,26 @@ Completes a colony payment in a single transaction ### ▸ `makePaymentFundedFromDomain(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _callerPermissionDomainId, uint256 _callerChildSkillIndex, address[] memory _workers, address[] memory _tokens, uint256[] memory _amounts, uint256 _domainId, uint256 _skillId)` +Deprecated makePaymentFundedFromDomain without explicit chainId paramters for multichain + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which the _contract_ has permissions to add a payment and fund it +|_childSkillIndex|uint256|Index of the _permissionDomainId skill.children array to get +|_callerPermissionDomainId|uint256|The domainId in which the _caller_ has permissions to add a payment and fund it +|_callerChildSkillIndex|uint256|Index of the _callerPermissionDomainId skill.children array to get +|_workers|address[]|The addresses of the recipients of the payment +|_tokens|address[]|The addresses of the token the payments are being made in. 0x00 for Ether. +|_amounts|uint256[]|The amounts of the tokens being paid out +|_domainId|uint256|The domainId the payment should be coming from +|_skillId|uint256|The skillId that the payment should be marked with, possibly awarding reputation in this skill. + + +### ▸ `makePaymentFundedFromDomain(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _callerPermissionDomainId, uint256 _callerChildSkillIndex, address[] memory _workers, uint256[] memory _chainIds, address[] memory _tokens, uint256[] memory _amounts, uint256 _domainId, uint256 _skillId)` + Completes a colony payment in a single transaction *Note: Assumes that each entity holds administration and funding roles in the same domain, although contract and caller can have the permissions in different domains. Payment is taken from domain funds - if the domain does not have sufficient funds, call will fail.* @@ -186,6 +227,7 @@ Completes a colony payment in a single transaction |_callerPermissionDomainId|uint256|The domainId in which the _caller_ has permissions to add a payment and fund it |_callerChildSkillIndex|uint256|Index of the _callerPermissionDomainId skill.children array to get |_workers|address[]|The addresses of the recipients of the payment +|_chainIds|uint256[]|The chainIds of the tokens being paid out |_tokens|address[]|The addresses of the token the payments are being made in. 0x00 for Ether. |_amounts|uint256[]|The amounts of the tokens being paid out |_domainId|uint256|The domainId the payment should be coming from diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index 19afd1a3fa..6d4eda8609 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -1584,6 +1584,23 @@ Set the token payout on an expenditure slot. Can only be called by expenditure o |_amount|uint256|Payout amount +### ▸ `setExpenditurePayout(uint256 _id, uint256 _slot, uint256 _chainId, address _token, uint256 _amount)` + +Set the token payout on an expenditure slot. Can only be called by expenditure owner. + +*Note: Can only be called while expenditure is in draft state.* + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_id|uint256|Id of the expenditure +|_slot|uint256|Number of the slot +|_chainId|uint256|The chainId of the token +|_token|address|Address of the token, `0x0` value indicates Ether +|_amount|uint256|Payout amount + + ### ▸ `setExpenditurePayout(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _id, uint256 _slot, address _token, uint256 _amount)` This function is deprecated and will be removed in a future version diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index a033ae22af..adc56fe3e5 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -30,6 +30,7 @@ const IColony = artifacts.require("IColony"); const ProxyColonyNetwork = artifacts.require("ProxyColonyNetwork"); const ProxyColony = artifacts.require("ProxyColony"); const MetaTxToken = artifacts.require("MetaTxToken"); +const OneTxPayment = artifacts.require("OneTxPayment"); const LiFiFacetProxyMock = artifacts.require("LiFiFacetProxyMock"); // const { assert } = require("console"); @@ -44,8 +45,11 @@ const { NETWORK_ADDRESS, HASHZERO, LIFI_ADDRESS, + ARBITRATION_ROLE, + FUNDING_ROLE, + ADMINISTRATION_ROLE, } = require("../../helpers/constants"); -const { forwardTime, checkErrorRevertEthers, revert, snapshot, evmChainIdToWormholeChainId } = require("../../helpers/test-helper"); +const { forwardTime, checkErrorRevertEthers, revert, snapshot, evmChainIdToWormholeChainId, rolesToBytes32 } = require("../../helpers/test-helper"); const ReputationMinerTestWrapper = require("../../packages/reputation-miner/test/ReputationMinerTestWrapper"); const { TruffleLoader } = require("../../packages/package-utils"); const { getMetaTransactionParameters } = require("../../helpers/test-data-generator"); @@ -59,6 +63,7 @@ const contractLoader = new TruffleLoader({ contract("Cross-chain", (accounts) => { let homeColony; let homeColonyNetwork; + let proxyColony; let remoteColonyNetwork; let homeBridge; let foreignBridge; @@ -539,7 +544,6 @@ contract("Cross-chain", (accounts) => { describe("collecting and paying out tokens on another chain", async () => { let foreignToken; let colony; - let proxyColony; beforeEach(async () => { colony = await setupColony(homeColonyNetwork); @@ -1011,7 +1015,6 @@ contract("Cross-chain", (accounts) => { describe("making arbitrary transactions on another chain", async () => { let colony; - let proxyColony; let foreignToken; beforeEach(async () => { colony = await setupColony(homeColonyNetwork); @@ -1150,7 +1153,6 @@ contract("Cross-chain", (accounts) => { describe("ProxyColony functions are secure", async () => { let colony; - let proxyColony; beforeEach(async () => { colony = await setupColony(homeColonyNetwork); @@ -1256,4 +1258,78 @@ contract("Cross-chain", (accounts) => { await checkErrorRevertEthers(tx.wait(), "colony-bridge-destination-chain-id-mismatch"); }); }); + + describe("OneTxPayment", async () => { + let version; + before(async () => { + const oneTxPaymentFactory = new ethers.ContractFactory(OneTxPayment.abi, OneTxPayment.bytecode, ethersHomeSigner); + const extension = await oneTxPaymentFactory.deploy(); + version = await extension.version(); + }); + + beforeEach(async () => { + const events = await homeColonyNetwork.queryFilter(homeColonyNetwork.filters.ColonyAdded()); + // homeColonyNetwork.fil + // Deploy a proxy colony on the foreign network + + const colonyCreationSalt = await homeColonyNetwork.getColonyCreationSalt({ blockTag: events[events.length - 1].blockNumber }); + + const p = guardianSpy.getPromiseForNextBridgedTransaction(); + + const tx = await homeColony.createProxyColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); + await tx.wait(); + + await p; + proxyColony = new ethers.Contract(homeColony.address, ProxyColony.abi, ethersForeignSigner); + // Deploy a token on the foreign network + }); + + it("Can make a OneTxPayment cross-chain", async () => { + const tokenFactory = new ethers.ContractFactory(MetaTxToken.abi, MetaTxToken.bytecode, ethersForeignSigner); + const foreignToken = await tokenFactory.deploy("Test Token", "TT", 18); + await (await foreignToken.unlock()).wait(); + + const tokenAmount = ethers.utils.parseEther("100"); + await foreignToken["mint(address,uint256)"](proxyColony.address, tokenAmount); + + let p = guardianSpy.getPromiseForNextBridgedTransaction(); + + let tx = await proxyColony.claimTokens(foreignToken.address); + await tx.wait(); + + await p; + + const paymentAmount = ethers.utils.parseEther("30"); + const ONE_TX_PAYMENT = ethers.utils.id("OneTxPayment"); + await homeColony.installExtension(ONE_TX_PAYMENT, version); + + const oneTxPaymentAddress = await homeColonyNetwork.getExtensionInstallation(ONE_TX_PAYMENT, homeColony.address); + const oneTxPayment = await new ethers.Contract(oneTxPaymentAddress, OneTxPayment.abi, ethersHomeSigner); + + const ROLES = rolesToBytes32([ARBITRATION_ROLE, FUNDING_ROLE, ADMINISTRATION_ROLE]); + await homeColony.setUserRoles(1, UINT256_MAX_ETHERS, oneTxPayment.address, 1, ROLES); + + const balanceBefore = await foreignToken.balanceOf(accounts[0]); + + p = guardianSpy.getPromiseForNextBridgedTransaction(); + console.log("amkepayment"); + tx = await oneTxPayment["makePayment(uint256,uint256,uint256,uint256,address[],uint256[],address[],uint256[],uint256,uint256)"]( + 1, + UINT256_MAX_ETHERS, + 1, + UINT256_MAX_ETHERS, + [accounts[0]], + [foreignChainId], + [foreignToken.address], + [paymentAmount], + 1, + 0, + ); + await tx.wait(); + await p; + + const balanceAfter = await foreignToken.balanceOf(accounts[0]); + expect(balanceAfter.sub(balanceBefore).toHexString()).to.equal(paymentAmount.toHexString()); + }); + }); }); diff --git a/test/extensions/one-tx-payment.js b/test/extensions/one-tx-payment.js index 45b5a1a25e..8ffcc4d545 100644 --- a/test/extensions/one-tx-payment.js +++ b/test/extensions/one-tx-payment.js @@ -216,7 +216,18 @@ contract("One transaction payments", (accounts) => { await fundColonyWithTokens(colony, token, INITIAL_FUNDING); await checkErrorRevert( - oneTxPayment.makePayment(2, UINT256_MAX, 2, UINT256_MAX, [USER1], [token.address], [10], 2, localSkillId, { from: USER1 }), + oneTxPayment.methods["makePayment(uint256,uint256,uint256,uint256,address[],address[],uint256[],uint256,uint256)"]( + 2, + UINT256_MAX, + 2, + UINT256_MAX, + [USER1], + [token.address], + [10], + 2, + localSkillId, + { from: USER1 }, + ), "one-tx-payment-not-authorized", ); }); @@ -229,7 +240,18 @@ contract("One transaction payments", (accounts) => { await colony.setArbitrationRole(1, 0, USER1, 2, true); await fundColonyWithTokens(colony, token, INITIAL_FUNDING); - await oneTxPayment.makePayment(1, 0, 2, UINT256_MAX, [USER1], [token.address], [10], 2, localSkillId, { from: USER1 }); + await oneTxPayment.methods["makePayment(uint256,uint256,uint256,uint256,address[],address[],uint256[],uint256,uint256)"]( + 1, + 0, + 2, + UINT256_MAX, + [USER1], + [token.address], + [10], + 2, + localSkillId, + { from: USER1 }, + ); }); it("should allow a single-transaction to occur when user has different permissions than contract", async () => { From a06accb83efe72fe32b2d37f60427a8a59aa6061 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 21 Oct 2024 16:23:37 +0100 Subject: [PATCH 44/72] Add events for Proxy Colony deployment --- contracts/bridging/ProxyColonyNetwork.sol | 6 +++++ .../colonyNetwork/ColonyNetworkDataTypes.sol | 6 +++++ .../colonyNetwork/ColonyNetworkDeployer.sol | 8 +++++- test/cross-chain/cross-chain.js | 27 ++++++++++++++++--- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/contracts/bridging/ProxyColonyNetwork.sol b/contracts/bridging/ProxyColonyNetwork.sol index cccbdd74aa..6c21655c7a 100644 --- a/contracts/bridging/ProxyColonyNetwork.sol +++ b/contracts/bridging/ProxyColonyNetwork.sol @@ -44,6 +44,10 @@ contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards, DomainReceiver /// @param bridgeAddress The address of the bridge contract that will be interacted with event BridgeSet(address bridgeAddress); + /// @notice Event logged when a new proxy colony is deployed. + /// @param proxyColony The address of the newly deployed proxy colony + event ProxyColonyDeployed(address proxyColony); + modifier onlyColony() { require(msgSenderIsColony(), "colony-network-caller-must-be-proxy-colony"); _; @@ -111,6 +115,8 @@ contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards, DomainReceiver shellColonies[address(etherRouter)] = true; etherRouter.setResolver(proxyColonyResolverAddress); // ignore-swc-113 + + emit ProxyColonyDeployed(address(etherRouter)); } function bridgeMessage(bytes memory _payload) public onlyColony { diff --git a/contracts/colonyNetwork/ColonyNetworkDataTypes.sol b/contracts/colonyNetwork/ColonyNetworkDataTypes.sol index 38a0f38852..65a4b95c93 100755 --- a/contracts/colonyNetwork/ColonyNetworkDataTypes.sol +++ b/contracts/colonyNetwork/ColonyNetworkDataTypes.sol @@ -161,6 +161,12 @@ interface ColonyNetworkDataTypes { /// @param count The number of the reputation update trying to be bridged in that colony event ReputationUpdateSentToBridge(address colony, uint256 count); + /// @notice Event emitted when a proxy colony deployment is requested + /// @param destinationChainId The chain ID of the destination chain + /// @param salt The salt used to generate the proxy address + event ProxyColonyRequested(uint256 destinationChainId, bytes32 salt); + + struct Skill { // total number of parent skills uint128 nParents; diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index 24e08b77b7..ee50dd4366 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -135,12 +135,18 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage, DomainReceiverManagement uint256 _destinationChainId, bytes32 _salt ) public stoppable calledByColony { - // TODO: Check if the colony is allowed to use the salt + bytes32 guardedSalt = keccak256(abi.encode(bytes32(uint256(uint160(address(this)))), _salt)); + require( + msgSender() == ICreateX(CREATEX_ADDRESS).computeCreate3Address(guardedSalt), + "colony-network-wrong-salt" + ); + bytes memory payload = abi.encodeWithSignature("createProxyColonyFromBridge(bytes32)", _salt); require( IColonyBridge(colonyBridgeAddress).sendMessage(_destinationChainId, address(this), payload), "colony-network-create-proxy-colony-failed" ); + emit ProxyColonyRequested(_destinationChainId, _salt); } /** diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index adc56fe3e5..e43372ab4d 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -112,7 +112,9 @@ contract("Cross-chain", (accounts) => { before(async () => { homeChainId = await ethersHomeSigner.provider.send("eth_chainId", []); + homeChainId = ethers.BigNumber.from(homeChainId).toHexString(); foreignChainId = await ethersForeignSigner.provider.send("eth_chainId", []); + foreignChainId = ethers.BigNumber.from(foreignChainId).toHexString(); // We need to deploy the network to the other chain try { @@ -142,7 +144,7 @@ contract("Cross-chain", (accounts) => { wormholeHomeChainId = evmChainIdToWormholeChainId(homeChainId); // const foreignNetworkId = await ethersForeignSigner.provider.send("net_version", []); - foreignChainId = await ethersForeignSigner.provider.send("eth_chainId", []); + // foreignChainId = await ethersForeignSigner.provider.send("eth_chainId", []); // wormholeForeignChainId = evmChainIdToWormholeChainId(foreignChainId); // Deploy shell colonyNetwork to whichever chain truffle hasn't already deployed to. @@ -339,8 +341,18 @@ contract("Cross-chain", (accounts) => { tx = await deployedColony.createProxyColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); - await tx.wait(); - await p; + let receipt = await tx.wait(); + const proxyRequestEvent = receipt.events.filter((e) => e.address === homeColonyNetwork.address)[0]; + let parsed = homeColonyNetwork.interface.parseLog(proxyRequestEvent); + expect(parsed.name).to.equal("ProxyColonyRequested"); + expect(parsed.args.destinationChainId.toHexString()).to.equal(foreignChainId); + expect(parsed.args.salt).to.equal(colonyCreationSalt); + receipt = await p; + + const proxyDeployedEvent = receipt.logs.filter((e) => e.address === remoteColonyNetwork.address)[0]; + parsed = remoteColonyNetwork.interface.parseLog(proxyDeployedEvent); + expect(parsed.name).to.equal("ProxyColonyDeployed"); + expect(parsed.args.proxyColony).to.equal(deployedColony.address); // Did we deploy a shell colony on the foreign chain at the right address? Should have EtherRouter code... const code = await ethersForeignProvider.getCode(deployedColony.address); @@ -355,6 +367,15 @@ contract("Cross-chain", (accounts) => { expect(resolverAddress).to.equal(expectedResolver); }); + it("colonies can't deploy proxies that aren't for themselves", async () => { + const colony = await setupColony(homeColonyNetwork); + + const deployedColony = new ethers.Contract(colony.address, IColony.abi, ethersHomeSigner); + + const tx = await deployedColony.createProxyColony(foreignChainId, ethers.constants.HashZero, { gasLimit: 1000000 }); + checkErrorRevertEthers(tx.wait(), "colony-network-wrong-salt"); + }); + it("bridge data can be queried", async () => { const bridgeAddress = await homeColonyNetwork.getColonyBridgeAddress(); expect(bridgeAddress).to.equal(homeColonyBridge.address); From 6f79e08b55ff3db450c15f9d025e9ceea09042d8 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 8 Nov 2024 11:38:09 +0000 Subject: [PATCH 45/72] Update docs post-rebase --- docs/interfaces/icolony.md | 4 ---- docs/interfaces/imetacolony.md | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index 6d4eda8609..e93397fdc1 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -412,7 +412,6 @@ Emit a positive skill reputation update. Available only to Root role holders |_amount|int256|The (positive) amount of reputation to gain -<<<<<<< HEAD ### ▸ `enterRecoveryMode()` Put colony network mining into recovery mode. Can only be called by user with recovery role. @@ -420,8 +419,6 @@ Put colony network mining into recovery mode. Can only be called by user with re -||||||| parent of 4e12a606 (Proxy-held to proxy-held token swapping) -======= ### ▸ `exchangeProxyHeldTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, uint256 _chainId, address _token, uint256 _amount)` Exchange funds between two tokens, potentially between chains The tokens being swapped are held by a proxy contract @@ -441,7 +438,6 @@ Exchange funds between two tokens, potentially between chains The tokens being s |_amount|uint256|Amount of tokens to exchange ->>>>>>> 4e12a606 (Proxy-held to proxy-held token swapping) ### ▸ `exchangeTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, address _token, uint256 _amount)` Exchange funds between two tokens, potentially between chains diff --git a/docs/interfaces/imetacolony.md b/docs/interfaces/imetacolony.md index 00c59df475..6daebf1a35 100644 --- a/docs/interfaces/imetacolony.md +++ b/docs/interfaces/imetacolony.md @@ -455,6 +455,25 @@ Put colony network mining into recovery mode. Can only be called by user with re +### ▸ `exchangeProxyHeldTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, uint256 _chainId, address _token, uint256 _amount)` + +Exchange funds between two tokens, potentially between chains The tokens being swapped are held by a proxy contract + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_permissionDomainId|uint256|The domainId in which I have the permission to take this action +|_childSkillIndex|uint256|The child index in `_permissionDomainId` where we can find `_domainId` +|_domainId|uint256|Id of the domain +|_txdata|bytes|Transaction data for the exchange +|_value|uint256|Value of the transaction +|_chainId|uint256|The chainId of the token +|_token|address|Address of the token. If the native token is being swapped, can be anything and _amount should be 0. +|_amount|uint256|Amount of tokens to exchange + + ### ▸ `exchangeTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, address _token, uint256 _amount)` Exchange funds between two tokens, potentially between chains @@ -1624,6 +1643,23 @@ Set the token payout on an expenditure slot. Can only be called by expenditure o |_amount|uint256|Payout amount +### ▸ `setExpenditurePayout(uint256 _id, uint256 _slot, uint256 _chainId, address _token, uint256 _amount)` + +Set the token payout on an expenditure slot. Can only be called by expenditure owner. + +*Note: Can only be called while expenditure is in draft state.* + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_id|uint256|Id of the expenditure +|_slot|uint256|Number of the slot +|_chainId|uint256|The chainId of the token +|_token|address|Address of the token, `0x0` value indicates Ether +|_amount|uint256|Payout amount + + ### ▸ `setExpenditurePayout(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _id, uint256 _slot, address _token, uint256 _amount)` This function is deprecated and will be removed in a future version From 11b4968a2af1fe19746e6905b275d0f16676cc13 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 8 Nov 2024 15:00:27 +0000 Subject: [PATCH 46/72] Fixes post-rebase on fund-domains-directly --- ...nagement.sol:DomainReceiverManagement.json | 3 ++ .../common/IsContract.sol:IsContract.json | 3 ++ ...FacetProxyMock.sol:LiFiFacetProxyMock.json | 3 ++ contracts/colony/Colony.sol | 28 +++++++++++++++++++ contracts/colony/ColonyAuthority.sol | 8 ++++++ docs/interfaces/icolonynetwork.md | 28 +++++++++++++++++++ test/contracts-network/colony-funding.js | 14 ---------- 7 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 .storage-layouts-normalized/contracts/common/DomainReceiverManagement.sol:DomainReceiverManagement.json create mode 100644 .storage-layouts-normalized/contracts/common/IsContract.sol:IsContract.json create mode 100644 .storage-layouts-normalized/contracts/testHelpers/LiFiFacetProxyMock.sol:LiFiFacetProxyMock.json diff --git a/.storage-layouts-normalized/contracts/common/DomainReceiverManagement.sol:DomainReceiverManagement.json b/.storage-layouts-normalized/contracts/common/DomainReceiverManagement.sol:DomainReceiverManagement.json new file mode 100644 index 0000000000..82b695cebb --- /dev/null +++ b/.storage-layouts-normalized/contracts/common/DomainReceiverManagement.sol:DomainReceiverManagement.json @@ -0,0 +1,3 @@ +{ + "storage": [] +} \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/common/IsContract.sol:IsContract.json b/.storage-layouts-normalized/contracts/common/IsContract.sol:IsContract.json new file mode 100644 index 0000000000..82b695cebb --- /dev/null +++ b/.storage-layouts-normalized/contracts/common/IsContract.sol:IsContract.json @@ -0,0 +1,3 @@ +{ + "storage": [] +} \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/testHelpers/LiFiFacetProxyMock.sol:LiFiFacetProxyMock.json b/.storage-layouts-normalized/contracts/testHelpers/LiFiFacetProxyMock.sol:LiFiFacetProxyMock.json new file mode 100644 index 0000000000..82b695cebb --- /dev/null +++ b/.storage-layouts-normalized/contracts/testHelpers/LiFiFacetProxyMock.sol:LiFiFacetProxyMock.json @@ -0,0 +1,3 @@ +{ + "storage": [] +} \ No newline at end of file diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index 19f9874836..a7daf828fd 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -322,6 +322,34 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP sig = bytes4(keccak256("editAllowedDomainReputationReceipt(uint256,uint256,bool)")); colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); + + sig = bytes4( + keccak256("setExpenditurePayout(uint256,uint256,uint256,uint256,uint256,address,uint256)") + ); + colonyAuthority.setRoleCapability(uint8(ColonyRole.Arbitration), address(this), sig, true); + + sig = bytes4( + keccak256("moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,address)") + ); + colonyAuthority.setRoleCapability(uint8(ColonyRole.Funding), address(this), sig, true); + + sig = bytes4(keccak256("makeProxyArbitraryTransactions(uint256,address[],bytes[])")); + colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); + + sig = bytes4(keccak256("callProxyNetwork(uint256,bytes[])")); + colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); + + sig = bytes4( + keccak256("exchangeTokensViaLiFi(uint256,uint256,uint256,bytes,uint256,address,uint256)") + ); + colonyAuthority.setRoleCapability(uint8(ColonyRole.Funding), address(this), sig, true); + + sig = bytes4( + keccak256( + "exchangeProxyHeldTokensViaLiFi(uint256,uint256,uint256,bytes,uint256,uint256,address,uint256)" + ) + ); + colonyAuthority.setRoleCapability(uint8(ColonyRole.Funding), address(this), sig, true); } function createProxyColony(uint256 _destinationChainId, bytes32 _salt) public stoppable { diff --git a/contracts/colony/ColonyAuthority.sol b/contracts/colony/ColonyAuthority.sol index b0c217adea..a8c6a4f686 100644 --- a/contracts/colony/ColonyAuthority.sol +++ b/contracts/colony/ColonyAuthority.sol @@ -137,6 +137,14 @@ contract ColonyAuthority is CommonAuthority { // Added in colony v?? addRoleCapability(ROOT_ROLE, "editAllowedDomainReputationReceipt(uint256,uint256,bool)"); + + addRoleCapability(ARBITRATION_ROLE, "setExpenditurePayout(uint256,uint256,uint256,uint256,uint256,address,uint256)"); + addRoleCapability(FUNDING_ROLE, "moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)"); + addRoleCapability(ROOT_ROLE, "makeProxyArbitraryTransactions(uint256,address[],bytes[])"); + addRoleCapability(ROOT_ROLE, "callProxyNetwork(uint256,bytes[])"); + + addRoleCapability(FUNDING_ROLE, "exchangeTokensViaLiFi(uint256,uint256,uint256,bytes,uint256,address,uint256)"); + addRoleCapability(FUNDING_ROLE, "exchangeProxyHeldTokensViaLiFi(uint256,uint256,uint256,bytes,uint256,uint256,address,uint256)"); } function addRoleCapability(uint8 role, bytes memory sig) private { diff --git a/docs/interfaces/icolonynetwork.md b/docs/interfaces/icolonynetwork.md index fd35d79271..37731bb04c 100644 --- a/docs/interfaces/icolonynetwork.md +++ b/docs/interfaces/icolonynetwork.md @@ -93,6 +93,34 @@ Indicate approval to exit recovery mode. Can only be called by user with recover +### ▸ `bridgeMessage(uint256 _chainId, bytes memory _payload)` + +Bridge a message to another chain + +*Note: This will bridge the message to the same address that requested the bridge on the other chain* + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_chainId|uint256|The chainId of the chain to bridge to +|_payload|bytes|The message to bridge + + +### ▸ `bridgeMessageToNetwork(uint256 _chainId, bytes memory _payload)` + +Bridge a message to the ProxyNetwork on another chain + +*Note: This should only be able to be called by the metacolony* + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_chainId|uint256|The chainId of the chain to bridge to +|_payload|bytes|The message to bridge + + ### ▸ `burnUnneededRewards(uint256 _amount)` Used to burn tokens that are not needed to pay out rewards (because not every possible defence was made for all submissions) diff --git a/test/contracts-network/colony-funding.js b/test/contracts-network/colony-funding.js index a74aa7cb66..b9aad3875f 100755 --- a/test/contracts-network/colony-funding.js +++ b/test/contracts-network/colony-funding.js @@ -872,20 +872,6 @@ contract("Colony Funding", (accounts) => { await checkErrorRevert(colonyNetwork.checkDomainTokenReceiverDeployed(2), "colony-caller-must-be-colony"); }); - it("only the owner (which should be colonyNetwork) can call setColonyAddress on DomainTokenReceiver", async () => { - await colony.addDomain(1, UINT256_MAX, 1); - await colony.claimDomainFunds(ethers.constants.AddressZero, 2); - - const receiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); - const receiverAsEtherRouter = await EtherRouter.at(receiverAddress); - const receiver = await DomainTokenReceiver.at(receiverAddress); - const owner = await receiverAsEtherRouter.owner(); - expect(owner).to.equal(colonyNetwork.address); - - await checkErrorRevert(receiver.setColonyAddress(colony.address), "ds-auth-unauthorized"); - await receiver.setColonyAddress.estimateGas(colony.address, { from: colonyNetwork.address }); - }); - it("If transfer fails from receiver, then the funds are not claimed", async () => { await colony.addDomain(1, UINT256_MAX, 1); const receiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); From fd43011c12a238e02aac58c9f6f23b839df6a558 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Thu, 21 Nov 2024 14:10:02 +0000 Subject: [PATCH 47/72] makeArbitraryTransactions to singular on ProxyColony --- contracts/bridging/ProxyColony.sol | 29 ++++++------- contracts/colony/Colony.sol | 2 +- .../colony/ColonyArbitraryTransaction.sol | 22 ++++++---- contracts/colony/ColonyAuthority.sol | 2 +- contracts/colony/ColonyFunding.sol | 17 ++++---- contracts/colony/IColony.sol | 10 ++--- docs/interfaces/icolony.md | 6 +-- docs/interfaces/imetacolony.md | 6 +-- test/contracts-network/colony-recovery.js | 2 +- test/cross-chain/cross-chain.js | 42 +++++++++---------- 10 files changed, 69 insertions(+), 69 deletions(-) diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index 1af1977edf..bc8d43ada5 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -112,24 +112,21 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards, BasicMetaTransaction emit TransferMade(_token, _recipient, _amount); } - function makeArbitraryTransactions( - address[] memory _targets, - bytes[] memory _payloads + function makeArbitraryTransaction( + address _target, + bytes memory _payload ) public onlyColonyBridge { - require(_targets.length == _payloads.length, "colony-targets-and-payloads-length-mismatch"); address bridgeAddress = ProxyColonyNetwork(owner).colonyBridgeAddress(); - for (uint256 i; i < _targets.length; i += 1) { - // TODO: Stop, or otherwise handle, approve / transferFrom - require(_targets[i] != bridgeAddress, "colony-cannot-target-bridge"); - require(_targets[i] != owner, "colony-cannot-target-network"); - // TODO: Allowing calling ourselves is okay for now, but as we add functionality might not be? - (bool success, bytes memory returndata) = callWithGuards(_targets[i], _payloads[i]); - - // Note that this is not a require because returndata might not be a string, and if we try - // to decode it we'll get a revert. - if (!success) { - revert(abi.decode(returndata, (string))); - } + // TODO: Stop, or otherwise handle, approve / transferFrom + require(_target != bridgeAddress, "colony-cannot-target-bridge"); + require(_target != owner, "colony-cannot-target-network"); + // TODO: Allowing calling ourselves is okay for now, but as we add functionality might not be? + (bool success, bytes memory returndata) = callWithGuards(_target, _payload); + + // Note that this is not a require because returndata might not be a string, and if we try + // to decode it we'll get a revert. + if (!success) { + revert(abi.decode(returndata, (string))); } } diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index a7daf828fd..b63685fe33 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -333,7 +333,7 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP ); colonyAuthority.setRoleCapability(uint8(ColonyRole.Funding), address(this), sig, true); - sig = bytes4(keccak256("makeProxyArbitraryTransactions(uint256,address[],bytes[])")); + sig = bytes4(keccak256("makeProxyArbitraryTransaction(uint256,address,bytes)")); colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); sig = bytes4(keccak256("callProxyNetwork(uint256,bytes[])")); diff --git a/contracts/colony/ColonyArbitraryTransaction.sol b/contracts/colony/ColonyArbitraryTransaction.sol index 279b68aa3c..29fe0ef10b 100644 --- a/contracts/colony/ColonyArbitraryTransaction.sol +++ b/contracts/colony/ColonyArbitraryTransaction.sol @@ -44,17 +44,21 @@ contract ColonyArbitraryTransaction is ColonyStorage { return res; } - function makeProxyArbitraryTransactions( + function makeProxyArbitraryTransaction( uint256 _chainId, - address[] memory _destinations, - bytes[] memory _actions + address _destination, + bytes memory _action ) public stoppable auth returns (bool) { - bytes memory payload = abi.encodeWithSignature( - "makeArbitraryTransactions(address[],bytes[])", - _destinations, - _actions - ); - IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, payload); + if (_destination == address(this)) { + IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, _action); + } else { + bytes memory payload = abi.encodeWithSignature( + "makeArbitraryTransaction(address,bytes)", + _destination, + _action + ); + IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, payload); + } } function callProxyNetwork( diff --git a/contracts/colony/ColonyAuthority.sol b/contracts/colony/ColonyAuthority.sol index a8c6a4f686..ca55f44d1a 100644 --- a/contracts/colony/ColonyAuthority.sol +++ b/contracts/colony/ColonyAuthority.sol @@ -140,7 +140,7 @@ contract ColonyAuthority is CommonAuthority { addRoleCapability(ARBITRATION_ROLE, "setExpenditurePayout(uint256,uint256,uint256,uint256,uint256,address,uint256)"); addRoleCapability(FUNDING_ROLE, "moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)"); - addRoleCapability(ROOT_ROLE, "makeProxyArbitraryTransactions(uint256,address[],bytes[])"); + addRoleCapability(ROOT_ROLE, "makeProxyArbitraryTransaction(uint256,address,bytes)"); addRoleCapability(ROOT_ROLE, "callProxyNetwork(uint256,bytes[])"); addRoleCapability(FUNDING_ROLE, "exchangeTokensViaLiFi(uint256,uint256,uint256,bytes,uint256,address,uint256)"); diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index 99dfa7638c..989334fc47 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -26,6 +26,7 @@ import { ERC20 } from "./../../lib/dappsys/erc20.sol"; import { IColonyNetwork } from "./../colonyNetwork/IColonyNetwork.sol"; import { IColony } from "./IColony.sol"; import { DomainTokenReceiver } from "./../common/DomainTokenReceiver.sol"; +import { ProxyColony } from "./../bridging/ProxyColony.sol"; contract ColonyFunding is ColonyStorage // ignore-swc-123 @@ -298,15 +299,17 @@ contract ColonyFunding is if (_token == address(0)) { revert("not yet implemented"); } else { - address[] memory targets = new address[](2); - targets[0] = _token; - targets[1] = LIFI_ADDRESS; + bytes[] memory actions = new bytes[](2); - bytes[] memory payloads = new bytes[](2); - payloads[0] = abi.encodeCall(ERC20.approve, (LIFI_ADDRESS, _amount)); - payloads[1] = _txdata; + actions[0] = abi.encodeCall( + ProxyColony.makeArbitraryTransaction, + (_token, abi.encodeCall(ERC20.approve, (LIFI_ADDRESS, _amount))) + ); + actions[1] = abi.encodeCall(ProxyColony.makeArbitraryTransaction, (LIFI_ADDRESS, _txdata)); + + bytes memory multicallData = abi.encodeWithSignature("multicall(bytes[])", actions); - IColony(address(this)).makeProxyArbitraryTransactions(_chainId, targets, payloads); + IColony(address(this)).makeProxyArbitraryTransaction(_chainId, address(this), multicallData); } } diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 952c9e0202..05e6b2431e 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -64,12 +64,12 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, /// @notice Execute arbitrary transactions on behalf of the Colony via a proxy colony on another chain /// @dev If proxy colony not already deployed, will do nothing /// @param chainId The chainId of the proxy colony - /// @param _destinations Array of addresses to be targeted - /// @param _actions Array of Bytes arrays encoding the function calls and arguments - function makeProxyArbitraryTransactions( + /// @param _destination Address to be targeted + /// @param _action Array of Bytes arrays encoding the function call + function makeProxyArbitraryTransaction( uint256 chainId, - address[] memory _destinations, - bytes[] memory _actions + address _destination, + bytes memory _action ) external; /// @notice Execute arbitrary transactions on behalf of the Colony in series diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index e93397fdc1..623809053b 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -1233,7 +1233,7 @@ Add a new expenditure in the colony. Secured function to authorised members. |---|---|---| |expenditureId|uint256|Identifier of the newly created expenditure -### ▸ `makeProxyArbitraryTransactions(uint256 chainId, address[] memory _destinations, bytes[] memory _actions)` +### ▸ `makeProxyArbitraryTransaction(uint256 chainId, address _destination, bytes memory _action)` Execute arbitrary transactions on behalf of the Colony via a proxy colony on another chain @@ -1244,8 +1244,8 @@ Execute arbitrary transactions on behalf of the Colony via a proxy colony on ano |Name|Type|Description| |---|---|---| |chainId|uint256|The chainId of the proxy colony -|_destinations|address[]|Array of addresses to be targeted -|_actions|bytes[]|Array of Bytes arrays encoding the function calls and arguments +|_destination|address|Address to be targeted +|_action|bytes|Array of Bytes arrays encoding the function call ### ▸ `makeSingleArbitraryTransaction(address _target, bytes memory _action):bool success` diff --git a/docs/interfaces/imetacolony.md b/docs/interfaces/imetacolony.md index 6daebf1a35..aeeefa263a 100644 --- a/docs/interfaces/imetacolony.md +++ b/docs/interfaces/imetacolony.md @@ -1284,7 +1284,7 @@ Add a new expenditure in the colony. Secured function to authorised members. |---|---|---| |expenditureId|uint256|Identifier of the newly created expenditure -### ▸ `makeProxyArbitraryTransactions(uint256 chainId, address[] memory _destinations, bytes[] memory _actions)` +### ▸ `makeProxyArbitraryTransaction(uint256 chainId, address _destination, bytes memory _action)` Execute arbitrary transactions on behalf of the Colony via a proxy colony on another chain @@ -1295,8 +1295,8 @@ Execute arbitrary transactions on behalf of the Colony via a proxy colony on ano |Name|Type|Description| |---|---|---| |chainId|uint256|The chainId of the proxy colony -|_destinations|address[]|Array of addresses to be targeted -|_actions|bytes[]|Array of Bytes arrays encoding the function calls and arguments +|_destination|address|Address to be targeted +|_action|bytes|Array of Bytes arrays encoding the function call ### ▸ `makeSingleArbitraryTransaction(address _target, bytes memory _action):bool success` diff --git a/test/contracts-network/colony-recovery.js b/test/contracts-network/colony-recovery.js index 55925f4d5e..245e3a9127 100644 --- a/test/contracts-network/colony-recovery.js +++ b/test/contracts-network/colony-recovery.js @@ -213,7 +213,7 @@ contract("Colony Recovery", (accounts) => { await checkErrorRevert(metaColony.makeArbitraryTransaction(ADDRESS_ZERO, HASHZERO), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.makeArbitraryTransactions([], [], true), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.makeSingleArbitraryTransaction(ADDRESS_ZERO, HASHZERO), "colony-in-recovery-mode"); - await checkErrorRevert(metaColony.makeProxyArbitraryTransactions(1, [], []), "colony-in-recovery-mode"); + await checkErrorRevert(metaColony.makeProxyArbitraryTransaction(1, ADDRESS_ZERO, HASHZERO), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.callProxyNetwork(1, []), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.updateApprovalAmount(ADDRESS_ZERO, ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.finalizeRewardPayout(1), "colony-in-recovery-mode"); diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index e43372ab4d..d2ba85fa6e 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -624,7 +624,7 @@ contract("Cross-chain", (accounts) => { // One bridged transaction will be the request across, one will be reporting what was claimed back const payload = proxyColony.interface.encodeFunctionData("claimTokens", [foreignToken.address]); - tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [proxyColony.address], [payload]); + tx = await colony.makeProxyArbitraryTransaction(foreignChainId, proxyColony.address, payload); await tx.wait(); await p; @@ -1066,7 +1066,7 @@ contract("Cross-chain", (accounts) => { const payload = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [proxyColony.address, ethers.utils.parseEther("100")]); - const tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], [payload]); + const tx = await colony.makeProxyArbitraryTransaction(foreignChainId, foreignToken.address, payload); await tx.wait(); await p; @@ -1074,14 +1074,14 @@ contract("Cross-chain", (accounts) => { expect(balanceAfter.sub(balanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); }); - it("root permissions are required for makeProxyArbitraryTransactions", async () => { + it("root permissions are required for makeProxyArbitraryTransaction", async () => { const p = guardianSpy.getPromiseForNextBridgedTransaction(); - let tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], ["0x00000000"]); + let tx = await colony.makeProxyArbitraryTransaction(foreignChainId, foreignToken.address, "0x00000000"); await tx.wait(); await p; await colony.setUserRoles(1, UINT256_MAX_ETHERS, accounts[0], 1, ethers.utils.hexZeroPad("0x00", 32)); - tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], ["0x00000000"], { gasLimit: 1000000 }); + tx = await colony.makeProxyArbitraryTransaction(foreignChainId, foreignToken.address, "0x00000000", { gasLimit: 1000000 }); await checkErrorRevertEthers(tx.wait(), "ds-auth-unauthorized"); }); @@ -1090,7 +1090,7 @@ contract("Cross-chain", (accounts) => { const payload = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [proxyColony.address, ethers.utils.parseEther("100")]); - const tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [accounts[0]], [payload]); + const tx = await colony.makeProxyArbitraryTransaction(foreignChainId, accounts[0], payload); await tx.wait(); await p; @@ -1104,9 +1104,12 @@ contract("Cross-chain", (accounts) => { const p = guardianSpy.getPromiseForNextBridgedTransaction(2); const payload1 = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [proxyColony.address, ethers.utils.parseEther("100")]); + const arbitraryCallPayload1 = proxyColony.interface.encodeFunctionData("makeArbitraryTransaction", [foreignToken.address, payload1]); const payload2 = proxyColony.interface.encodeFunctionData("claimTokens(address)", [foreignToken.address]); - const tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address, proxyColony.address], [payload1, payload2]); + const multicallPayload = proxyColony.interface.encodeFunctionData("multicall", [[arbitraryCallPayload1, payload2]]); + + const tx = await colony.makeProxyArbitraryTransaction(foreignChainId, proxyColony.address, multicallPayload); await tx.wait(); await p; @@ -1120,31 +1123,24 @@ contract("Cross-chain", (accounts) => { }); it("invalid cross-chain arbitrary transactions are rejected", async () => { + // Check can't target Network let p = guardianSpy.getPromiseForNextBridgedTransaction(); - - const tx = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], ["0x00000000", "0x00000000"]); + const tx = await colony.makeProxyArbitraryTransaction(foreignChainId, remoteColonyNetwork.address, "0x00000000"); await tx.wait(); - await checkErrorRevertEthers(p, "colony-targets-and-payloads-length-mismatch"); - - // Check can't target Network - p = guardianSpy.getPromiseForNextBridgedTransaction(); - const tx2 = await colony.makeProxyArbitraryTransactions(foreignChainId, [remoteColonyNetwork.address], ["0x00000000"]); - await tx2.wait(); - await checkErrorRevertEthers(p, "colony-cannot-target-network"); // Check can't target the bridge p = guardianSpy.getPromiseForNextBridgedTransaction(); - const tx3 = await colony.makeProxyArbitraryTransactions(foreignChainId, [remoteColonyBridge.address], ["0x00000000"]); - await tx3.wait(); + const tx2 = await colony.makeProxyArbitraryTransaction(foreignChainId, remoteColonyBridge.address, "0x00000000"); + await tx2.wait(); await checkErrorRevertEthers(p, "colony-cannot-target-bridge"); // Otherwise valid transaction, it just fails p = guardianSpy.getPromiseForNextBridgedTransaction(); - const tx4 = await colony.makeProxyArbitraryTransactions(foreignChainId, [foreignToken.address], ["0x00000000"]); - await tx4.wait(); + const tx3 = await colony.makeProxyArbitraryTransaction(foreignChainId, foreignToken.address, "0x00000000"); + await tx3.wait(); await checkErrorRevertEthers(p, "require-execute-call-reverted-with-no-error"); }); @@ -1195,8 +1191,8 @@ contract("Cross-chain", (accounts) => { await checkErrorRevertEthers(tx.wait(), "colony-only-bridge"); }); - it("a non-bridge address cannot call makeArbitraryTransactions", async () => { - const tx = await proxyColony.makeArbitraryTransactions([], [], { gasLimit: 1000000 }); + it("a non-bridge address cannot call makeArbitraryTransaction", async () => { + const tx = await proxyColony.makeArbitraryTransaction(ADDRESS_ZERO, "0x00", { gasLimit: 1000000 }); await checkErrorRevertEthers(tx.wait(), "colony-only-bridge"); }); }); @@ -1245,7 +1241,7 @@ contract("Cross-chain", (accounts) => { describe("Invalid interactions with bridging system are handled appropriately", async () => { it("Can't bridge to a chain that's not supported", async () => { - const tx = await homeColony.makeProxyArbitraryTransactions(111, [ADDRESS_ZERO], ["0x00000000"], { gasLimit: 1000000 }); + const tx = await homeColony.makeProxyArbitraryTransaction(111, ADDRESS_ZERO, "0x00000000", { gasLimit: 1000000 }); await checkErrorRevertEthers(tx.wait(), "colony-bridge-not-known-chain"); }); From b159419a750238af466fd2491416c3724f28e303 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 22 Nov 2024 12:13:52 +0000 Subject: [PATCH 48/72] Same-chain token swapping test --- test/contracts-network/colony-funding.js | 64 +++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/test/contracts-network/colony-funding.js b/test/contracts-network/colony-funding.js index b9aad3875f..2b2fcb227b 100755 --- a/test/contracts-network/colony-funding.js +++ b/test/contracts-network/colony-funding.js @@ -16,6 +16,7 @@ const { SLOT2, ROOT_ROLE, ADDRESS_ZERO, + LIFI_ADDRESS, } = require("../../helpers/constants"); const { @@ -25,7 +26,15 @@ const { setupFundedExpenditure, setupRandomToken, } = require("../../helpers/test-data-generator"); -const { getTokenArgs, checkErrorRevert, web3GetBalance, removeSubdomainLimit, expectEvent, rolesToBytes32 } = require("../../helpers/test-helper"); +const { + getTokenArgs, + checkErrorRevert, + web3GetBalance, + removeSubdomainLimit, + expectEvent, + rolesToBytes32, + getChainId, +} = require("../../helpers/test-helper"); const { setupDomainTokenReceiverResolver } = require("../../helpers/upgradable-contracts"); const { expect } = chai; @@ -39,6 +48,7 @@ const Resolver = artifacts.require("Resolver"); const DomainTokenReceiver = artifacts.require("DomainTokenReceiver"); const TokenAuthority = artifacts.require("contracts/common/TokenAuthority.sol:TokenAuthority"); const ToggleableToken = artifacts.require("ToggleableToken"); +const LiFiFacetProxyMock = artifacts.require("LiFiFacetProxyMock"); contract("Colony Funding", (accounts) => { const MANAGER = accounts[0]; @@ -49,6 +59,7 @@ contract("Colony Funding", (accounts) => { let otherToken; let colonyNetwork; let metaColony; + let chainId; before(async () => { const cnAddress = (await EtherRouter.deployed()).address; @@ -57,6 +68,8 @@ contract("Colony Funding", (accounts) => { const metaColonyAddress = await colonyNetwork.getMetaColony(); metaColony = await IMetaColony.at(metaColonyAddress); + + chainId = await getChainId(); }); beforeEach(async () => { @@ -909,4 +922,53 @@ contract("Colony Funding", (accounts) => { expect(resolverAfter).to.equal(newResolver.address); }); }); + + describe("when exchanging tokens", () => { + it("can exchange tokens in a domain via LiFi", async () => { + await token.mint(colony.address, 100); + await colony.claimColonyFunds(token.address); + await colony.addDomain(1, UINT256_MAX, 1); + + const domain1 = await colony.getDomain(1); + const domain2 = await colony.getDomain(2); + + // Move 50 tokens from the colony to domain 2 + await colony.moveFundsBetweenPots(1, UINT256_MAX, 1, UINT256_MAX, 0, domain1.fundingPotId, domain2.fundingPotId, 50, chainId, token.address); + + const domain2ReceiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); + + const lifi = await LiFiFacetProxyMock.at(LIFI_ADDRESS); + const ethersProvider = new ethers.providers.Web3Provider(web3.currentProvider); + const lifiEthers = new ethers.Contract(LIFI_ADDRESS, LiFiFacetProxyMock.abi, ethersProvider); + + const txdata = lifi.contract.methods["swapTokensMock(uint256,address,uint256,address,address,uint256)"]( + chainId, + token.address, + chainId, + otherToken.address, + domain2ReceiverAddress, + 50, + ).encodeABI(); + + const tx = await colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, token.address, 50); + const swapEvent = tx.receipt.rawLogs + .filter((e) => e.address === LIFI_ADDRESS) + .map((e) => lifiEthers.interface.parseLog(e)) + .filter((e) => e.name === "SwapTokens")[0]; + expect(swapEvent).to.not.be.undefined; + + // Okay, so we saw the SwapTokens event. Let's do vaguely what it said for the test, + // but in practise this would be the responsibility of whatever entity we've paid to do it + // through LiFi. + await otherToken.mint(swapEvent.args._toAddress, swapEvent.args._amount); // Implicit 1:1 exchange rate + + // Now claim the tokens + await colony.claimDomainFunds(otherToken.address, 2); + + // See if bookkeeping was tracked correctly + const domain = await colony.getDomain(2); + const balance = await colony.getFundingPotBalance(domain.fundingPotId, otherToken.address); + expect(balance).to.eq.BN(50); + }); + }); }); From fa17afc86a7273c2369c30a4784ab4888dc886f3 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 25 Nov 2024 17:29:56 +0000 Subject: [PATCH 49/72] More LiFi guards/tests --- contracts/colony/ColonyFunding.sol | 18 ++++- test/contracts-network/colony-funding.js | 97 ++++++++++++++++++++++-- 2 files changed, 106 insertions(+), 9 deletions(-) diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index 989334fc47..c92e53edd2 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -231,6 +231,8 @@ contract ColonyFunding is // TODO: Colony Network fee Domain storage domain = domains[_domainId]; + uint256 priorApproval = ERC20Extended(_token).allowance(address(this), LIFI_ADDRESS); + // Check the domain has enough for what is if (_token == address(0x0)) { require( @@ -252,10 +254,24 @@ contract ColonyFunding is decrementFundingPotBalance(domain.fundingPotId, block.chainid, _token, _amount); decrementFundingPotBalance(domain.fundingPotId, block.chainid, address(0x0), _value); - require(ERC20Extended(_token).approve(LIFI_ADDRESS, _amount), "colony-approve-failed"); + require( + ERC20Extended(_token).approve(LIFI_ADDRESS, _amount + priorApproval), + "colony-approve-failed" + ); (bool success, ) = LIFI_ADDRESS.call{ value: _value }(_txdata); require(success, "colony-exchange-tokens-failed"); + + // Check the allowances afterwards, ensuring that at least some of the tokens we approved were spent by + // the LiFi transaction - if not, a different token might have been spent, so revert. + uint256 postApproval = ERC20Extended(_token).allowance(address(this), LIFI_ADDRESS); + require(_amount + priorApproval > postApproval, "colony-unexpected-exchange"); + require(postApproval >= priorApproval, "colony-more-than-intended-allowance-used"); + + // If the LiFi transaction didn't use all the tokens, reduce the allowance back to what it was before + if (postApproval > priorApproval) { + require(ERC20Extended(_token).approve(LIFI_ADDRESS, priorApproval), "colony-post-exchange-approve-failed"); + } } function exchangeProxyHeldTokensViaLiFi( diff --git a/test/contracts-network/colony-funding.js b/test/contracts-network/colony-funding.js index 2b2fcb227b..5c675c60cc 100755 --- a/test/contracts-network/colony-funding.js +++ b/test/contracts-network/colony-funding.js @@ -34,6 +34,7 @@ const { expectEvent, rolesToBytes32, getChainId, + encodeTxData, } = require("../../helpers/test-helper"); const { setupDomainTokenReceiverResolver } = require("../../helpers/upgradable-contracts"); @@ -923,24 +924,31 @@ contract("Colony Funding", (accounts) => { }); }); - describe("when exchanging tokens", () => { - it("can exchange tokens in a domain via LiFi", async () => { - await token.mint(colony.address, 100); + describe("when exchanging tokens via LiFi", () => { + let domain1; + let domain2; + let domain2ReceiverAddress; + let lifi; + let lifiEthers; + beforeEach(async () => { + await token.mint(colony.address, 200); await colony.claimColonyFunds(token.address); await colony.addDomain(1, UINT256_MAX, 1); - const domain1 = await colony.getDomain(1); - const domain2 = await colony.getDomain(2); + domain1 = await colony.getDomain(1); + domain2 = await colony.getDomain(2); // Move 50 tokens from the colony to domain 2 await colony.moveFundsBetweenPots(1, UINT256_MAX, 1, UINT256_MAX, 0, domain1.fundingPotId, domain2.fundingPotId, 50, chainId, token.address); - const domain2ReceiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); + domain2ReceiverAddress = await colonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); - const lifi = await LiFiFacetProxyMock.at(LIFI_ADDRESS); + lifi = await LiFiFacetProxyMock.at(LIFI_ADDRESS); const ethersProvider = new ethers.providers.Web3Provider(web3.currentProvider); - const lifiEthers = new ethers.Contract(LIFI_ADDRESS, LiFiFacetProxyMock.abi, ethersProvider); + lifiEthers = new ethers.Contract(LIFI_ADDRESS, LiFiFacetProxyMock.abi, ethersProvider); + }); + it("can exchange tokens in a domain via LiFi", async () => { const txdata = lifi.contract.methods["swapTokensMock(uint256,address,uint256,address,address,uint256)"]( chainId, token.address, @@ -970,5 +978,78 @@ contract("Colony Funding", (accounts) => { const balance = await colony.getFundingPotBalance(domain.fundingPotId, otherToken.address); expect(balance).to.eq.BN(50); }); + + it("should not mess up approval bookkeeping when exchanging tokens via LiFi", async () => { + const action1 = await encodeTxData(token, "approve", [LIFI_ADDRESS, 80]); + await colony.makeArbitraryTransaction(token.address, action1); + + const txdata = lifi.contract.methods["swapTokensMock(uint256,address,uint256,address,address,uint256)"]( + chainId, + token.address, + chainId, + otherToken.address, + domain2ReceiverAddress, + 40, + ).encodeABI(); + + await colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, token.address, 50); + await otherToken.mint(domain2ReceiverAddress, 50); // Better than 1:1 exchange rate + + const approval = await colony.getTokenApproval(token.address, LIFI_ADDRESS); + expect(approval).to.be.eq.BN(80); + const allApprovals = await colony.getTotalTokenApproval(token.address); + expect(allApprovals).to.be.eq.BN(80); + const tokenApproval = await token.allowance(colony.address, LIFI_ADDRESS); + expect(tokenApproval).to.be.eq.BN(80); + }); + + it("'lying' calls of exchangeTokensViaLiFi trying to spend other tokens are caught", async () => { + await fundColonyWithTokens(colony, otherToken, 100); + await colony.claimColonyFunds(otherToken.address); + + const action1 = await encodeTxData(otherToken, "approve", [LIFI_ADDRESS, 80]); + await colony.makeArbitraryTransaction(otherToken.address, action1); + + const txdata = lifi.contract.methods["swapTokensMock(uint256,address,uint256,address,address,uint256)"]( + chainId, + otherToken.address, + chainId, + otherToken.address, + domain2ReceiverAddress, + 40, + ).encodeABI(); + await checkErrorRevert(colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, token.address, 50), "colony-unexpected-exchange"); + }); + + it("'lying' calls of exchangeTokensViaLiFi trying to spend already-approved tokens are caught", async () => { + const action1 = await encodeTxData(token, "approve", [LIFI_ADDRESS, 80]); + await colony.makeArbitraryTransaction(token.address, action1); + + const txdata = lifi.contract.methods["swapTokensMock(uint256,address,uint256,address,address,uint256)"]( + chainId, + token.address, + chainId, + otherToken.address, + domain2ReceiverAddress, + 60, + ).encodeABI(); + await checkErrorRevert(colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, token.address, 50), "colony-more-than-intended-allowance-used"); + }); + + it("If LiFi transaction was cheaper than expected, shouldn't leave extra allowance behind", async () => { + const txdata = lifi.contract.methods["swapTokensMock(uint256,address,uint256,address,address,uint256)"]( + chainId, + token.address, + chainId, + otherToken.address, + domain2ReceiverAddress, + 40, + ).encodeABI(); + + await colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, token.address, 50); + + const approval = await token.allowance(colony.address, LIFI_ADDRESS); + expect(approval).to.be.eq.BN(0); + }); }); }); From 42ba4023d56768861f073b3682d6dbe9f606cf1b Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Tue, 26 Nov 2024 11:04:24 +0000 Subject: [PATCH 50/72] Guard against LiFi spending otherwise approved tokens --- contracts/colony/ColonyFunding.sol | 13 ++++++++++++- test/contracts-network/colony-funding.js | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index c92e53edd2..b4847941fe 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -250,6 +250,14 @@ contract ColonyFunding is ); } + if (_domainId == 1) { + // Check that we have enough not-already-approved tokens + require( + getFundingPotBalance(domain.fundingPotId, _token) >= tokenApprovalTotals[_token] + _amount, + "colony-insufficient-funds" + ); + } + // Deduct the amount from the domain decrementFundingPotBalance(domain.fundingPotId, block.chainid, _token, _amount); decrementFundingPotBalance(domain.fundingPotId, block.chainid, address(0x0), _value); @@ -270,7 +278,10 @@ contract ColonyFunding is // If the LiFi transaction didn't use all the tokens, reduce the allowance back to what it was before if (postApproval > priorApproval) { - require(ERC20Extended(_token).approve(LIFI_ADDRESS, priorApproval), "colony-post-exchange-approve-failed"); + require( + ERC20Extended(_token).approve(LIFI_ADDRESS, priorApproval), + "colony-post-exchange-approve-failed" + ); } } diff --git a/test/contracts-network/colony-funding.js b/test/contracts-network/colony-funding.js index 5c675c60cc..354b7ae6fd 100755 --- a/test/contracts-network/colony-funding.js +++ b/test/contracts-network/colony-funding.js @@ -1051,5 +1051,23 @@ contract("Colony Funding", (accounts) => { const approval = await token.allowance(colony.address, LIFI_ADDRESS); expect(approval).to.be.eq.BN(0); }); + + it("shouldn't use tokens that are already approved if swapping in root", async () => { + const action1 = await encodeTxData(token, "approve", [ADDRESS_ZERO, 140]); + await colony.makeArbitraryTransaction(token.address, action1); + + // 10 remain unapproved in the root domain + // Try to spend 50 with LiFi + + const txdata = lifi.contract.methods["swapTokensMock(uint256,address,uint256,address,address,uint256)"]( + chainId, + token.address, + chainId, + otherToken.address, + domain2ReceiverAddress, + 50, + ).encodeABI(); + await checkErrorRevert(colony.exchangeTokensViaLiFi(1, UINT256_MAX, 1, txdata, 0, token.address, 50), "colony-insufficient-funds"); + }); }); }); From f19ab86175c325b9bee0d6018bb9f543264d61f2 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 27 Nov 2024 11:17:56 +0000 Subject: [PATCH 51/72] For now, prevent proxy colony calling self --- contracts/bridging/ProxyColony.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index bc8d43ada5..95d5c1fc97 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -120,7 +120,7 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards, BasicMetaTransaction // TODO: Stop, or otherwise handle, approve / transferFrom require(_target != bridgeAddress, "colony-cannot-target-bridge"); require(_target != owner, "colony-cannot-target-network"); - // TODO: Allowing calling ourselves is okay for now, but as we add functionality might not be? + require(_target != address(this), "colony-cannot-target-self"); (bool success, bytes memory returndata) = callWithGuards(_target, _payload); // Note that this is not a require because returndata might not be a string, and if we try From b8d6c9f4f0bc92e9dfb301a3707b8eb2ef23270f Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 29 Nov 2024 12:27:45 +0000 Subject: [PATCH 52/72] Add Wormholescan API mock --- .circleci/config.yml | 5 + .eslintignore | 1 + .eslintrc | 1 + .../bridging/WormholeBridgeForColony.sol | 8 + contracts/testHelpers/WormholeMock.sol | 2 +- helpers/wormholescanMock/.gitignore | 5 + helpers/wormholescanMock/nodemon.json | 5 + helpers/wormholescanMock/openapitools.json | 14 + helpers/wormholescanMock/package-lock.json | 1945 +++++++ helpers/wormholescanMock/package.json | 31 + helpers/wormholescanMock/src/index.ts | 202 + helpers/wormholescanMock/swagger.json | 4484 +++++++++++++++++ package.json | 3 +- packages/wormhole-relayer/index.ts | 2 +- pnpm-lock.yaml | 1476 +++++- pnpm-workspace.yaml | 3 +- scripts/setup-bridging-contracts.js | 11 + test/cross-chain/cross-chain.js | 38 + 18 files changed, 8114 insertions(+), 122 deletions(-) create mode 100644 helpers/wormholescanMock/.gitignore create mode 100644 helpers/wormholescanMock/nodemon.json create mode 100644 helpers/wormholescanMock/openapitools.json create mode 100644 helpers/wormholescanMock/package-lock.json create mode 100644 helpers/wormholescanMock/package.json create mode 100644 helpers/wormholescanMock/src/index.ts create mode 100644 helpers/wormholescanMock/swagger.json diff --git a/.circleci/config.yml b/.circleci/config.yml index fc01b1b1d1..d5408cec7b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -61,6 +61,10 @@ step_compile_mock_spy: &step_compile_mock_spy cd ./../.. && npx hardhat compile cp ./packages/wormhole-relayer/config.example.js ./packages/wormhole-relayer/config.js npx tsc +step_generate_wormhole_types: &step_generate_wormhole_types + run: + name: "Generate wormhole API types for mock endpoint" + command: sudo apt install default-jre && cd ./helpers/wormholescanMock && pnpm i && npm run generate-types jobs: build-checks: @@ -348,6 +352,7 @@ jobs: - <<: *step_setup_global_packages - <<: *step_install_lsof - <<: *step_compile_mock_spy + - <<: *step_generate_wormhole_types - run: name: "Installing the safe-contracts dependencies" command: cd ./lib/safe-contracts && pnpm i --ignore-scripts diff --git a/.eslintignore b/.eslintignore index 959bc274cf..f24dd7f70b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,3 +7,4 @@ keys/* lib/* packages/**/node_modules scripts/mockGuardianSpy.js +helpers/wormholescanMock/dist/* \ No newline at end of file diff --git a/.eslintrc b/.eslintrc index b687bdebe9..5d3bdb717d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -43,6 +43,7 @@ "error", "ignorePackages", { + "": "never", "js": "never", "jsx": "never", "ts": "never", diff --git a/contracts/bridging/WormholeBridgeForColony.sol b/contracts/bridging/WormholeBridgeForColony.sol index fb937c22fa..1abba39587 100644 --- a/contracts/bridging/WormholeBridgeForColony.sol +++ b/contracts/bridging/WormholeBridgeForColony.sol @@ -25,6 +25,8 @@ import { CallWithGuards } from "../common/CallWithGuards.sol"; import { DSAuth } from "../../lib/dappsys/auth.sol"; contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { + event WormholeMessageReceived(uint16 emitterChainId, bytes32 emitterAddress, uint64 sequence); + address public colonyNetwork; IWormhole public wormhole; @@ -125,6 +127,12 @@ contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { if (!success) { revert(abi.decode(returndata, (string))); } + + emit WormholeMessageReceived( + wormholeMessage.emitterChainId, + wormholeMessage.emitterAddress, + wormholeMessage.sequence + ); } function sendMessage( diff --git a/contracts/testHelpers/WormholeMock.sol b/contracts/testHelpers/WormholeMock.sol index 3f303b2595..dc313018b2 100644 --- a/contracts/testHelpers/WormholeMock.sol +++ b/contracts/testHelpers/WormholeMock.sol @@ -73,7 +73,7 @@ contract WormholeMock is IWormhole { require(bridgeEnabled, "bridge-disabled"); cumulativeSequence += 1; - emit LogMessagePublished(msg.sender, sequence, nonce, payload, consistencyLevel); + emit LogMessagePublished(msg.sender, cumulativeSequence, nonce, payload, consistencyLevel); return cumulativeSequence; } diff --git a/helpers/wormholescanMock/.gitignore b/helpers/wormholescanMock/.gitignore new file mode 100644 index 0000000000..4267c370d8 --- /dev/null +++ b/helpers/wormholescanMock/.gitignore @@ -0,0 +1,5 @@ +wwwroot/*.js +node_modules +typings +dist +openapi-generated-types \ No newline at end of file diff --git a/helpers/wormholescanMock/nodemon.json b/helpers/wormholescanMock/nodemon.json new file mode 100644 index 0000000000..783a783697 --- /dev/null +++ b/helpers/wormholescanMock/nodemon.json @@ -0,0 +1,5 @@ +{ + "watch": ["src"], + "ext": "ts", + "exec": "concurrently 'npx tsc --watch' 'ts-node src/index.ts'" + } \ No newline at end of file diff --git a/helpers/wormholescanMock/openapitools.json b/helpers/wormholescanMock/openapitools.json new file mode 100644 index 0000000000..7dd53c5b7f --- /dev/null +++ b/helpers/wormholescanMock/openapitools.json @@ -0,0 +1,14 @@ +{ + "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "7.10.0", + "generators": { + "typescript-fetch": { + "generatorName": "typescript-fetch", + "inputSpec": "https://api.wormholescan.io/swagger.json", + "output": "#{cwd}/openapi-generated-types" + } + } + } +} diff --git a/helpers/wormholescanMock/package-lock.json b/helpers/wormholescanMock/package-lock.json new file mode 100644 index 0000000000..bdbdb3dbfb --- /dev/null +++ b/helpers/wormholescanMock/package-lock.json @@ -0,0 +1,1945 @@ +{ + "name": "wormholescanmock", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "wormholescanmock", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@apidevtools/swagger-express-middleware": "^4.0.2", + "@openapitools/openapi-generator-cli": "^2.15.3", + "@smartrecruiters/openapi-first": "^1.2.0", + "@types/swagger-express-middleware": "^1.0.16", + "express": "^4.21.1", + "swagger-typescript-codegen": "^3.2.4" + }, + "devDependencies": { + "@types/express": "^5.0.0", + "@types/node": "^22.10.0", + "typescript": "^5.7.2" + } + }, + "../../node_modules/.pnpm/@apidevtools+swagger-express-middleware@4.0.2_express@4.21.1_openapi-types@12.1.3/node_modules/@apidevtools/swagger-express-middleware": { + "version": "4.0.2", + "license": "MIT", + "dependencies": { + "@apidevtools/swagger-methods": "^3.0.2", + "@apidevtools/swagger-parser": "^10.0.1", + "@jsdevtools/ono": "^7.1.3", + "body-parser": "^1.19.0", + "cookie-parser": "^1.4.4", + "debug": "^4.1.1", + "lodash": "^4.17.19", + "multer": "^1.4.2", + "tmp": "^0.2.1", + "tv4": "^1.2.5", + "type-is": "^1.6.18" + }, + "devDependencies": { + "@jsdevtools/eslint-config": "^1.1.0", + "@jsdevtools/version-bump-prompt": "^6.0.6", + "basic-auth": "^2.0.1", + "chai": "^4.2.0", + "chai-datetime": "^1.7.0", + "eslint": "^7.6.0", + "express": "^4.17.1", + "mocha": "^8.1.0", + "npm-check": "^5.9.0", + "nyc": "^15.1.0", + "shx": "^0.3.2", + "sinon": "^9.0.2", + "supertest": "^4.0.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "express": "4.x" + } + }, + "../../node_modules/.pnpm/@smartrecruiters+openapi-first@1.2.0/node_modules/@smartrecruiters/openapi-first": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "lodash": "4.17.21", + "type-is": "1.6.18" + }, + "devDependencies": { + "@smartrecruiters/eslint-config": "5.0.2", + "chai": "4.3.4", + "eslint": "7.30.0", + "eslint-plugin-security": "1.4.0", + "express": "4.17.1", + "mocha": "9.0.2", + "nyc": "15.1.0", + "sinon": "11.1.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "../../node_modules/.pnpm/@types+express@5.0.0/node_modules/@types/express": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "../../node_modules/.pnpm/@types+node@22.10.0/node_modules/@types/node": { + "version": "22.10.0", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "../../node_modules/.pnpm/@types+swagger-express-middleware@1.0.16/node_modules/@types/swagger-express-middleware": { + "version": "1.0.16", + "license": "MIT", + "dependencies": { + "@types/express": "*", + "swagger-parser": "^7.0.1" + } + }, + "../../node_modules/.pnpm/express@4.21.1/node_modules/express": { + "version": "4.21.1", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "devDependencies": { + "after": "0.8.2", + "connect-redis": "3.4.2", + "cookie-parser": "1.4.6", + "cookie-session": "2.0.0", + "ejs": "3.1.9", + "eslint": "8.47.0", + "express-session": "1.17.2", + "hbs": "4.2.0", + "marked": "0.7.0", + "method-override": "3.0.0", + "mocha": "10.2.0", + "morgan": "1.10.0", + "nyc": "15.1.0", + "pbkdf2-password": "1.2.1", + "supertest": "6.3.0", + "vhost": "~3.0.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "../../node_modules/.pnpm/swagger-typescript-codegen@3.2.4/node_modules/swagger-typescript-codegen": { + "version": "3.2.4", + "license": "Apache-2.0", + "dependencies": { + "commander": "^2.19.0", + "js-beautify": "^1.8.9", + "jshint": "^2.9.7", + "lodash": "^4.17.19", + "mustache": "^3.0.1", + "update-notifier": "^4.1.0" + }, + "bin": { + "swagger2ts": "bin/swagger2ts.js" + }, + "devDependencies": { + "@types/commander": "^2.12.2", + "@types/jest": "^23.3.10", + "@types/js-beautify": "^1.8.0", + "@types/lodash": "^4.14.119", + "@types/mustache": "^0.8.32", + "@types/node": "^10.12.18", + "final-fs": "^1.6.0", + "grunt": "^1.0.3", + "grunt-contrib-jshint": "^2.0.0", + "grunt-jsonlint": "^1.0.4", + "grunt-vows": "^0.4.1", + "husky": "^1.3.1", + "jest": "^23.6.0", + "lint-staged": "^8.1.0", + "matchdep": "^2.0.0", + "prettier": "1.15.3", + "request": "^2.88.0", + "superagent": "^4.0.0", + "tmp": "0.0.33", + "typescript": "^3.2.2", + "vows": "^0.8.2" + } + }, + "../../node_modules/.pnpm/typescript@5.7.2/node_modules/typescript": { + "version": "5.7.2", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "devDependencies": { + "@dprint/formatter": "^0.4.1", + "@dprint/typescript": "0.93.0", + "@esfx/canceltoken": "^1.0.0", + "@eslint/js": "^9.11.1", + "@octokit/rest": "^21.0.2", + "@types/chai": "^4.3.20", + "@types/diff": "^5.2.2", + "@types/minimist": "^1.2.5", + "@types/mocha": "^10.0.8", + "@types/ms": "^0.7.34", + "@types/node": "latest", + "@types/source-map-support": "^0.5.10", + "@types/which": "^3.0.4", + "@typescript-eslint/rule-tester": "^8.8.0", + "@typescript-eslint/type-utils": "^8.8.0", + "@typescript-eslint/utils": "^8.8.0", + "azure-devops-node-api": "^14.1.0", + "c8": "^10.1.2", + "chai": "^4.5.0", + "chalk": "^4.1.2", + "chokidar": "^3.6.0", + "diff": "^5.2.0", + "dprint": "^0.47.2", + "esbuild": "^0.24.0", + "eslint": "^9.11.1", + "eslint-formatter-autolinkable-stylish": "^1.4.0", + "eslint-plugin-regexp": "^2.6.0", + "fast-xml-parser": "^4.5.0", + "glob": "^10.4.5", + "globals": "^15.9.0", + "hereby": "^1.10.0", + "jsonc-parser": "^3.3.1", + "knip": "^5.30.6", + "minimist": "^1.2.8", + "mocha": "^10.7.3", + "mocha-fivemat-progress-reporter": "^0.1.0", + "monocart-coverage-reports": "^2.11.0", + "ms": "^2.1.3", + "playwright": "^1.47.2", + "source-map-support": "^0.5.21", + "tslib": "^2.7.0", + "typescript": "^5.6.2", + "typescript-eslint": "^8.8.0", + "which": "^3.0.1" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@apidevtools/swagger-express-middleware": { + "resolved": "../../node_modules/.pnpm/@apidevtools+swagger-express-middleware@4.0.2_express@4.21.1_openapi-types@12.1.3/node_modules/@apidevtools/swagger-express-middleware", + "link": true + }, + "node_modules/@babel/runtime": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@nestjs/axios": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.1.1.tgz", + "integrity": "sha512-ySoxrzqX80P1q6LKLKGcgyBd2utg4gbC+4FsJNpXYvILorMlxss/ECNogD9EXLCE4JS5exVFD5ez0nK5hXcNTQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "axios": "^1.3.1", + "rxjs": "^6.0.0 || ^7.0.0" + } + }, + "node_modules/@nestjs/common": { + "version": "10.4.6", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.6.tgz", + "integrity": "sha512-KkezkZvU9poWaNq4L+lNvx+386hpOxPJkfXBBeSMrcqBOx8kVr36TGN2uYkF4Ta4zNu1KbCjmZbc0rhHSg296g==", + "license": "MIT", + "dependencies": { + "iterare": "1.2.1", + "tslib": "2.7.0", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/common/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/@nestjs/core": { + "version": "10.4.6", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.6.tgz", + "integrity": "sha512-zXVPxCNRfO6gAy0yvEDjUxE/8gfZICJFpsl2lZAUH31bPb6m+tXuhUq2mVCTEltyMYQ+DYtRe+fEYM2v152N1g==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@nuxtjs/opencollective": "0.3.2", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "3.3.0", + "tslib": "2.7.0", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/microservices": "^10.0.0", + "@nestjs/platform-express": "^10.0.0", + "@nestjs/websockets": "^10.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } + } + }, + "node_modules/@nestjs/core/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/@nuxtjs/opencollective": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", + "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "consola": "^2.15.0", + "node-fetch": "^2.6.1" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/@openapitools/openapi-generator-cli": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.15.3.tgz", + "integrity": "sha512-2UBnsDlMt36thhdXxisbA1qReVtbCaw+NCvXoslRXlaJBL4qkAmZUhNeDLNu3LCbwA2PASMWhJSqeLwgwMCitw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@nestjs/axios": "3.1.1", + "@nestjs/common": "10.4.6", + "@nestjs/core": "10.4.6", + "@nuxtjs/opencollective": "0.3.2", + "axios": "1.7.7", + "chalk": "4.1.2", + "commander": "8.3.0", + "compare-versions": "4.1.4", + "concurrently": "6.5.1", + "console.table": "0.10.0", + "fs-extra": "10.1.0", + "glob": "9.3.5", + "inquirer": "8.2.6", + "lodash": "4.17.21", + "proxy-agent": "6.4.0", + "reflect-metadata": "0.1.13", + "rxjs": "7.8.1", + "tslib": "2.8.1" + }, + "bin": { + "openapi-generator-cli": "main.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/openapi_generator" + } + }, + "node_modules/@smartrecruiters/openapi-first": { + "resolved": "../../node_modules/.pnpm/@smartrecruiters+openapi-first@1.2.0/node_modules/@smartrecruiters/openapi-first", + "link": true + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT" + }, + "node_modules/@types/express": { + "resolved": "../../node_modules/.pnpm/@types+express@5.0.0/node_modules/@types/express", + "link": true + }, + "node_modules/@types/node": { + "resolved": "../../node_modules/.pnpm/@types+node@22.10.0/node_modules/@types/node", + "link": true + }, + "node_modules/@types/swagger-express-middleware": { + "resolved": "../../node_modules/.pnpm/@types+swagger-express-middleware@1.0.16/node_modules/@types/swagger-express-middleware", + "link": true + }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "license": "MIT" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/compare-versions": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.4.tgz", + "integrity": "sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==", + "license": "MIT" + }, + "node_modules/concurrently": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz", + "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "bin": { + "concurrently": "bin/concurrently.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/concurrently/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", + "license": "MIT" + }, + "node_modules/console.table": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz", + "integrity": "sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==", + "license": "MIT", + "dependencies": { + "easy-table": "1.1.0" + }, + "engines": { + "node": "> 0.10" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/easy-table": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz", + "integrity": "sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==", + "license": "MIT", + "optionalDependencies": { + "wcwidth": ">=1.0.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/express": { + "resolved": "../../node_modules/.pnpm/express@4.21.1/node_modules/express", + "link": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-uri": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4", + "fs-extra": "^11.2.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "license": "ISC", + "engines": { + "node": ">=6" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "license": "ISC" + }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", + "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.5", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "license": "MIT" + }, + "node_modules/proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "license": "Apache-2.0" + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==" + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/swagger-typescript-codegen": { + "resolved": "../../node_modules/.pnpm/swagger-typescript-codegen@3.2.4/node_modules/swagger-typescript-codegen", + "link": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "resolved": "../../node_modules/.pnpm/typescript@5.7.2/node_modules/typescript", + "link": true + }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "license": "MIT", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + } + } +} diff --git a/helpers/wormholescanMock/package.json b/helpers/wormholescanMock/package.json new file mode 100644 index 0000000000..15242d0314 --- /dev/null +++ b/helpers/wormholescanMock/package.json @@ -0,0 +1,31 @@ +{ + "name": "wormholescanmock", + "version": "1.0.0", + "main": "dist/index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "generate-types": "openapi-generator-cli generate --generator-key typescript-fetch", + "start": "node dist/index.js", + "dev": "nodemon src/index.ts" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@openapitools/openapi-generator-cli": "^2.15.3", + "dotenv": "^8.0.0", + "ethers": "^5.7.2", + "ethers-types": "^3.17.3", + "express": "^4.21.1", + "openapi-generator": "^0.1.39", + "swagger-typescript-codegen": "^3.2.4" + }, + "devDependencies": { + "@types/express": "^5.0.0", + "@types/node": "^22.10.0", + "concurrently": "^9.1.0", + "nodemon": "^3.1.7", + "ts-node": "^9.1.1", + "typescript": "^5.7.2" + }, + "description": "" +} diff --git a/helpers/wormholescanMock/src/index.ts b/helpers/wormholescanMock/src/index.ts new file mode 100644 index 0000000000..0951a23249 --- /dev/null +++ b/helpers/wormholescanMock/src/index.ts @@ -0,0 +1,202 @@ +import express, { Express, Request, Response } from "express"; +import { LogDescription } from "ethers/lib/utils"; +import { ethers } from "ethers"; +import { OperationsOperationResponse, VaaChainID } from "../openapi-generated-types/models"; + +const app: Express = express(); +const port = process.env.PORT || 3000; +const providerURLs: string[] = process.env.PROVIDER_URLS ? process.env.PROVIDER_URLS.split(",") : []; + +const providers: ethers.providers.JsonRpcProvider[] = []; + +function wormholeAddressToEvmAddress(address: string): string { + return ethers.utils.getAddress(`0x${address.slice(26, 66)}`); +} + +function evmChainIdToWormholeChainId(chainId: number) { + // So I've decreed that for chainId 265669100, we use 10003 (which is really arbitrum sepolia) + // and for chainId 265669101, we use 10002 (which is really sepolia). + + switch (chainId) { + case 265669100: + return 10003; + case 265669101: + return 10002; + case 265669102: + return 10004; + default: + throw new Error("Invalid chainId"); + } +} + +type LogAndEvent = { + log: ethers.providers.Log; + event: LogDescription; + chainId?: VaaChainID; +}; + +async function getLogMessagePublishedForVAA( + provider: ethers.providers.Provider, + emitterWormholeChainId: number, + wormholeSender: string, + sequence: string, +): Promise { + const providerChainId = (await provider.getNetwork()).chainId; + const providerWormholeChainId = await evmChainIdToWormholeChainId(providerChainId); + if (providerWormholeChainId !== emitterWormholeChainId) { + return; + } + const logs = await provider.getLogs({ + topics: [ethers.utils.id("LogMessagePublished(address,uint64,uint32,bytes,uint8)"), wormholeSender], + fromBlock: 1, + }); + + if (logs.length === 0) { + return; + } + + const wormhole = new ethers.Contract( + logs[0].address, + ["event LogMessagePublished(address indexed sender,uint64 sequence,uint32 nonce,bytes payload,uint8 consistencyLevel)"], + provider, + ); + + const logsAndEvents = logs.map((log) => { + return { log, event: wormhole.interface.parseLog(log) } as LogAndEvent; + }); + + const filteredLogsAndEvents = logsAndEvents.filter( + (logAndEvent) => + logAndEvent.event.args.sequence.toString() === sequence && + ethers.utils.getAddress(logAndEvent.event.args.sender) === wormholeAddressToEvmAddress(wormholeSender), + ); + + if (filteredLogsAndEvents.length === 0) { + return; + } + const requestingEventAndLog = filteredLogsAndEvents[0]; + + const { chainId } = await provider.getNetwork(); + requestingEventAndLog.chainId = evmChainIdToWormholeChainId(chainId); + return requestingEventAndLog; // eslint-disable-line consistent-return +} + +async function getColonyReceivingTransaction( + provider: ethers.providers.Provider, + emitterWormholeChainId: number, + emitterWormholeAddress: string, + sequence: string, +): Promise { + const logs = await provider.getLogs({ + topics: [ethers.utils.id("WormholeMessageReceived(uint16,bytes32,uint64)")], + }); + + if (logs.length === 0) { + return; + } + const wormhole = new ethers.Contract( + logs[0].address, + ["event WormholeMessageReceived(uint16 emitterChainId, bytes32 emitterAddress, uint64 sequence)"], + provider, + ); + + const logsAndEvents = logs.map((log) => { + return { log, event: wormhole.interface.parseLog(log) } as LogAndEvent; + }); + + const filteredLogsAndEvents = logsAndEvents.filter( + (logAndEvent: LogAndEvent) => + logAndEvent.event.args?.sequence.toString() === sequence && + logsAndEvents[0].event.args.emitterAddress === emitterWormholeAddress && + logAndEvent.event.args?.emitterChainId === emitterWormholeChainId, + ); + + if (filteredLogsAndEvents.length === 0) { + return; + } + const receivingEventAndLog = filteredLogsAndEvents[0]; + + const { chainId } = await provider.getNetwork(); + const wormholeChainId = await evmChainIdToWormholeChainId(chainId); + receivingEventAndLog.chainId = wormholeChainId; + return receivingEventAndLog; // eslint-disable-line consistent-return +} + +app.get("/api/v1/operations/:chain/:emitter/:sequence", async (req: Request, res: Response) => { + // TODO: get provider based on :chain + // Emitter must be a valid _Wormhole_ address + // Which is length 64 and valid hex + let emitterAddress = req.params.emitter; + if (req.params.emitter.substring(0, 2) !== "0x") { + emitterAddress = `0x${req.params.emitter}`; + } + emitterAddress = emitterAddress.toLowerCase(); + if (!ethers.utils.isHexString(emitterAddress) || emitterAddress.length !== 66) { + res.status(400).send({ + code: 3, + message: "MALFORMED EMITTER_ADDR", + }); + return; + } + + let requestingEventAndLog; + for (const provider of providers) { + requestingEventAndLog = await getLogMessagePublishedForVAA(provider, parseInt(req.params.chain, 10), emitterAddress, req.params.sequence); + if (requestingEventAndLog) { + break; + } + } + + if (!requestingEventAndLog) { + res.status(404).send({ + code: 1, + message: "VAA NOT FOUND", + }); + return; + } + + let receivingEventAndLog; + for (const provider of providers) { + receivingEventAndLog = await getColonyReceivingTransaction(provider, parseInt(req.params.chain, 10), emitterAddress, req.params.sequence); + if (receivingEventAndLog) { + break; + } + } + + const body: OperationsOperationResponse = { + id: `${req.params.chain}/${req.params.emitter}/${req.params.sequence}`, + sourceChain: { + chainId: requestingEventAndLog.chainId, + transaction: { + txHash: requestingEventAndLog.log.transactionHash, + }, + status: "confirmed", + }, + }; + + if (receivingEventAndLog) { + body.targetChain = { + chainId: receivingEventAndLog.chainId, + transaction: { + txHash: receivingEventAndLog.log.transactionHash, + }, + status: "confirmed", + }; + } + + res.send(body); +}); + +app.listen(port, async () => { + for (const providerURL of providerURLs) { + const p = new ethers.providers.JsonRpcProvider(providerURL); + try { + await p.getNetwork(); + providers.push(p); + } catch (e) { + console.error(`Failed to connect to provider ${providerURL} with error ${e}, skipping`); + } + } + + console.log(`[server]: Server is running at http://localhost:${port}`); +}); diff --git a/helpers/wormholescanMock/swagger.json b/helpers/wormholescanMock/swagger.json new file mode 100644 index 0000000000..1985772fb3 --- /dev/null +++ b/helpers/wormholescanMock/swagger.json @@ -0,0 +1,4484 @@ +{ + "swagger": "2.0", + "info": { + "description": "Wormhole Guardian API\nThis is the API for the Wormhole Guardian and Explorer.\nThe API has two namespaces: wormholescan and guardian.\nwormholescan is the namespace for the explorer and the new endpoints. The prefix is /api/v1.\nguardian is the legacy namespace backguard compatible with guardian node API. The prefix is /v1.\nThis API is public and does not require authentication although some endpoints are rate limited.\nCheck each endpoint documentation for more information.", + "title": "Wormholescan API", + "termsOfService": "https://wormhole.com/", + "contact": { + "name": "API Support", + "url": "https://discord.com/invite/wormholecrypto", + "email": "info@wormhole.com" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0" + }, + "basePath": "/", + "paths": { + "/api/v1/address/:address": { + "get": { + "description": "Lookup an address", + "tags": [ + "wormholescan" + ], + "operationId": "find-address-by-id", + "parameters": [ + { + "type": "string", + "description": "address", + "name": "address", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Page number. Starts at 0.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-address_AddressOverview" + } + }, + "400": { + "description": "Bad Request" + }, + "404": { + "description": "Not Found" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/application-activity": { + "get": { + "description": "Search for a specific period of time the number of transactions and the volume per application.", + "tags": [ + "wormholescan" + ], + "operationId": "application-activity", + "parameters": [ + { + "type": "string", + "description": "Time span, supported values: 1d, 1mo and 1y", + "name": "timespan", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "From date, supported format 2006-01-02T15:04:05Z07:00", + "name": "from", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "To date, supported format 2006-01-02T15:04:05Z07:00", + "name": "to", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Search by appId", + "name": "appIds", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/transactions.ChainActivityTopResult" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/global-tx/:chain_id/:emitter/:seq": { + "get": { + "description": "Find a global transaction by VAA ID\nGlobal transactions is a logical association of two transactions that are related to each other by a unique VAA ID.\nThe first transaction is created on the origin chain when the VAA is emitted.\nThe second transaction is created on the destination chain when the VAA is redeemed.\nIf the response only contains an origin tx the VAA was not redeemed.", + "tags": [ + "wormholescan" + ], + "operationId": "find-global-transaction-by-id", + "parameters": [ + { + "type": "integer", + "description": "id of the blockchain", + "name": "chain_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "address of the emitter", + "name": "emitter", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "sequence of the VAA", + "name": "seq", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/transactions.Tx" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/config": { + "get": { + "description": "Returns governor configuration for all guardians.", + "tags": [ + "wormholescan" + ], + "operationId": "governor-config", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-governor_GovConfig" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/config/:guardian_address": { + "get": { + "description": "Returns governor configuration for a given guardian.", + "tags": [ + "wormholescan" + ], + "operationId": "governor-config-by-guardian-address", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-governor_GovConfig" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/enqueued_vaas/": { + "get": { + "description": "Returns enqueued VAAs for each blockchain.", + "tags": [ + "wormholescan" + ], + "operationId": "governor-enqueued-vaas", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + }, + { + "enum": [ + "ASC", + "DESC" + ], + "type": "string", + "description": "Sort results in ascending or descending order.", + "name": "sortOrder", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_governor_EnqueuedVaas" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/enqueued_vaas/:chain": { + "get": { + "description": "Returns all enqueued VAAs for a given blockchain.", + "tags": [ + "wormholescan" + ], + "operationId": "guardians-enqueued-vaas-by-chain", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + }, + { + "enum": [ + "ASC", + "DESC" + ], + "type": "string", + "description": "Sort results in ascending or descending order.", + "name": "sortOrder", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_governor_EnqueuedVaaDetail" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/limit": { + "get": { + "description": "Returns the governor limit for all blockchains.", + "tags": [ + "wormholescan" + ], + "operationId": "governor-notional-limit", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_governor_GovernorLimit" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/notional/available": { + "get": { + "description": "Returns the amount of notional value available for each blockchain.", + "tags": [ + "wormholescan" + ], + "operationId": "governor-notional-available", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + }, + { + "enum": [ + "ASC", + "DESC" + ], + "type": "string", + "description": "Sort results in ascending or descending order.", + "name": "sortOrder", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_governor_NotionalAvailable" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/notional/available/:chain": { + "get": { + "description": "Returns the amount of notional value available for a given blockchain.", + "tags": [ + "wormholescan" + ], + "operationId": "governor-notional-available-by-chain", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_governor_NotionalAvailableDetail" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/notional/limit": { + "get": { + "description": "Returns the detailed notional limit for all blockchains.", + "tags": [ + "wormholescan" + ], + "operationId": "governor-notional-limit-detail", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_governor_NotionalLimitDetail" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/notional/limit/:chain": { + "get": { + "description": "Returns the detailed notional limit available for a given blockchain.", + "tags": [ + "wormholescan" + ], + "operationId": "governor-notional-limit-detail-by-chain", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_governor_NotionalLimitDetail" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/notional/max_available/:chain": { + "get": { + "description": "Returns the maximum amount of notional value available for a given blockchain.", + "tags": [ + "wormholescan" + ], + "operationId": "governor-max-notional-available-by-chain", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-governor_MaxNotionalAvailableRecord" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/status": { + "get": { + "description": "Returns the governor status for all guardians.", + "tags": [ + "wormholescan" + ], + "operationId": "governor-status", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_governor_GovStatus" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/status/:guardian_address": { + "get": { + "description": "Returns the governor status for a given guardian.", + "tags": [ + "wormholescan" + ], + "operationId": "governor-status-by-guardian-address", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-governor_GovStatus" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/governor/vaas": { + "get": { + "description": "Returns all vaas in Governor.", + "tags": [ + "wormholescan" + ], + "operationId": "governor-vaas", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_governor_GovernorVaasResponse" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/health": { + "get": { + "description": "Health check", + "tags": [ + "wormholescan" + ], + "operationId": "health-check", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string" + } + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/last-txs": { + "get": { + "description": "Returns the number of transactions by a defined time span and sample rate.", + "tags": [ + "wormholescan" + ], + "operationId": "get-last-transactions", + "parameters": [ + { + "type": "string", + "description": "Time Span, default: 1d, supported values: [1d, 1w, 1mo]. 1mo ​​is 30 days.", + "name": "timeSpan", + "in": "query" + }, + { + "type": "string", + "description": "Sample Rate, default: 1h, supported values: [1h, 1d]. Valid configurations with timeSpan: 1d/1h, 1w/1d, 1mo/1d", + "name": "sampleRate", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/transactions.TransactionCountResult" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/native-token-transfer/activity": { + "get": { + "description": "Returns a list of values (tx count or notional) of the Native Token Transfer for a emitter and destination chains.", + "tags": [ + "wormholescan" + ], + "operationId": "/api/v1/native-token-transfer/activity", + "parameters": [ + { + "type": "string", + "description": "Symbol of the native-token-transfer token.", + "name": "symbol", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Renders the results using notional or tx count (default is notional).", + "name": "by", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/stats.NativeTokenTransferActivity" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/native-token-transfer/summary": { + "get": { + "description": "Returns a summary of the Native Token Transfer.", + "tags": [ + "wormholescan" + ], + "operationId": "/api/v1/native-token-transfer/summary", + "parameters": [ + { + "type": "string", + "description": "coingecko_id of the desired token.", + "name": "coingecko_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/stats.NativeTokenTransferSummary" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/native-token-transfer/token-list": { + "get": { + "description": "Returns the list of supported Native Token Transfer tokens.", + "tags": [ + "wormholescan" + ], + "operationId": "/api/v1/native-token-transfer/token-list", + "parameters": [ + { + "type": "string", + "description": "Specify true/false if the response includes links.", + "name": "withLinks", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/stats.Token" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/native-token-transfer/top-address": { + "get": { + "description": "Returns a list of values (tx count or notional) of the Native Token Transfer for address.", + "tags": [ + "wormholescan" + ], + "operationId": "/api/v1/native-token-transfer/top-address", + "parameters": [ + { + "type": "string", + "description": "Symbol of the native-token-transfer token.", + "name": "symbol", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Renders the results using notional or tx count (default is notional).", + "name": "by", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/stats.NativeTokenTransferTopAddress" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/native-token-transfer/top-holder": { + "get": { + "description": "Returns a list of volume and chain of the Native Token Transfer for top holders.", + "tags": [ + "wormholescan" + ], + "operationId": "/api/v1/native-token-transfer/top-holder", + "parameters": [ + { + "type": "string", + "description": "Coingecko_id of the native-token-transfer token.", + "name": "coingecko_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/stats.NativeTokenTransferTopHolder" + } + } + }, + "400": { + "description": "Bad Request" + }, + "404": { + "description": "Not Found" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/native-token-transfer/transfer-by-time": { + "get": { + "description": "Returns a list of values (tx count or notional) of the Native Token Transfer for a emitter and destination chains.", + "tags": [ + "wormholescan" + ], + "operationId": "/api/v1/native-token-transfer/transfer-by-time", + "parameters": [ + { + "type": "string", + "description": "From date, supported format 2006-01-02T15:04:05Z07:00", + "name": "from", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "To date, supported format 2006-01-02T15:04:05Z07:00", + "name": "to", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Symbol of the native-token-transfer token.", + "name": "symbol", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Renders the results using notional or tx count (default is notional).", + "name": "by", + "in": "query" + }, + { + "type": "string", + "description": "Time Span, supported values: [1h, 1d, 1mo, 1y].", + "name": "timeSpan", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/stats.NativeTokenTransferByTime" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/observations": { + "get": { + "description": "Returns all observations, sorted in descending timestamp order.", + "tags": [ + "wormholescan" + ], + "operationId": "find-observations", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "Transaction hash of the Observations", + "name": "txHash", + "in": "query" + }, + { + "enum": [ + "ASC", + "DESC" + ], + "type": "string", + "description": "Sort results in ascending or descending order.", + "name": "sortOrder", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/observations.ObservationDoc" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/observations/:chain": { + "get": { + "description": "Returns all observations for a given blockchain, sorted in descending timestamp order.", + "tags": [ + "wormholescan" + ], + "operationId": "find-observations-by-chain", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + }, + { + "enum": [ + "ASC", + "DESC" + ], + "type": "string", + "description": "Sort results in ascending or descending order.", + "name": "sortOrder", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/observations.ObservationDoc" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/observations/:chain/:emitter": { + "get": { + "description": "Returns all observations for a specific emitter address, sorted in descending timestamp order.", + "tags": [ + "wormholescan" + ], + "operationId": "find-observations-by-emitter", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + }, + { + "enum": [ + "ASC", + "DESC" + ], + "type": "string", + "description": "Sort results in ascending or descending order.", + "name": "sortOrder", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/observations.ObservationDoc" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/observations/:chain/:emitter/:sequence": { + "get": { + "description": "Find observations identified by emitter chain, emitter address and sequence.", + "tags": [ + "wormholescan" + ], + "operationId": "find-observations-by-sequence", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + }, + { + "enum": [ + "ASC", + "DESC" + ], + "type": "string", + "description": "Sort results in ascending or descending order.", + "name": "sortOrder", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/observations.ObservationDoc" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/observations/:chain/:emitter/:sequence/:signer/:hash": { + "get": { + "description": "Find a specific observation.", + "tags": [ + "wormholescan" + ], + "operationId": "find-observations-by-id", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + }, + { + "enum": [ + "ASC", + "DESC" + ], + "type": "string", + "description": "Sort results in ascending or descending order.", + "name": "sortOrder", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/observations.ObservationDoc" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/operations": { + "get": { + "description": "Find all operations.", + "tags": [ + "wormholescan" + ], + "operationId": "get-operations", + "parameters": [ + { + "type": "string", + "description": "address of the emitter", + "name": "address", + "in": "query" + }, + { + "type": "string", + "description": "hash of the transaction", + "name": "txHash", + "in": "query" + }, + { + "type": "integer", + "description": "page number", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "pageSize", + "name": "pageSize", + "in": "query" + }, + { + "type": "string", + "description": "source chains of the operation, separated by comma", + "name": "sourceChain", + "in": "query" + }, + { + "type": "string", + "description": "target chains of the operation, separated by comma", + "name": "targetChain", + "in": "query" + }, + { + "type": "string", + "description": "appID of the operation", + "name": "appId", + "in": "query" + }, + { + "type": "boolean", + "description": "single appId of the operation", + "name": "exclusiveAppId", + "in": "query" + }, + { + "type": "string", + "description": "beginning of period", + "name": "from", + "in": "query" + }, + { + "type": "string", + "description": "end of period", + "name": "to", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/operations.OperationResponse" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/operations/{chain_id}/{emitter}/{seq}": { + "get": { + "description": "Find operations by ID (chainID/emitter/sequence).", + "tags": [ + "wormholescan" + ], + "operationId": "get-operation-by-id", + "parameters": [ + { + "type": "integer", + "description": "id of the blockchain", + "name": "chain_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "address of the emitter", + "name": "emitter", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "sequence of the VAA", + "name": "seq", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/operations.OperationResponse" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/protocols/stats": { + "get": { + "description": "Returns the representative stats for the top protocols", + "tags": [ + "wormholescan" + ], + "operationId": "get-top-protocols-stats", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/protocols.ProtocolTotalValuesDTO" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/protocols.ProtocolTotalValuesDTO" + } + } + } + } + } + }, + "/api/v1/ready": { + "get": { + "description": "Ready check", + "tags": [ + "wormholescan" + ], + "operationId": "ready-check", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { + "ready": { + "type": "string" + } + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/relays/:chain/:emitter/:sequence": { + "get": { + "description": "Get a specific relay information by chainID, emitter address and sequence.", + "tags": [ + "wormholescan" + ], + "operationId": "find-relay-by-vaa-id", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/relays.RelayResponse" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/scorecards": { + "get": { + "description": "Returns a list of KPIs for Wormhole.\nTVL is total value locked by token bridge contracts in USD.\nVolume is the all-time total volume transferred through the token bridge in USD.\n24h volume is the volume transferred through the token bridge in the last 24 hours, in USD.\nTotal Tx count is the number of transaction bridging assets since the creation of the network (does not include Pyth or other messages).\n24h tx count is the number of transaction bridging assets in the last 24 hours (does not include Pyth or other messages).\nTotal messages is the number of VAAs emitted since the creation of the network (includes Pyth messages).", + "tags": [ + "wormholescan" + ], + "operationId": "get-scorecards", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/transactions.ScorecardsResponse" + } + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/supply": { + "get": { + "description": "Get W token supply data (circulation and total supply).", + "tags": [ + "wormholescan" + ], + "operationId": "supply-info", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/supply.SupplyInfoResponse" + } + } + } + } + }, + "/api/v1/supply/circulating": { + "get": { + "description": "Get W token circulation supply.", + "tags": [ + "wormholescan" + ], + "operationId": "circulating-supply", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/api/v1/supply/total": { + "get": { + "description": "Get W token total supply.", + "tags": [ + "wormholescan" + ], + "operationId": "total-supply", + "responses": { + "200": { + "description": "OK" + } + } + } + }, + "/api/v1/token/:chain_id/:token_address": { + "get": { + "description": "Returns a token symbol, coingecko id and address by chain and token address.", + "tags": [ + "wormholescan" + ], + "operationId": "get-token-by-chain-and-address", + "parameters": [ + { + "type": "integer", + "description": "id of the blockchain", + "name": "chain_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "token address", + "name": "token_address", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/transactions.Token" + } + }, + "400": { + "description": "Bad Request" + }, + "404": { + "description": "Not Found" + } + } + } + }, + "/api/v1/top-100-corridors": { + "get": { + "description": "Returns a list of the top 100 tokens, sorted in descending order by the number of transactions.", + "tags": [ + "wormholescan" + ], + "operationId": "/api/v1/top-100-corridors", + "parameters": [ + { + "type": "string", + "description": "Time span, supported values: 2d and 7d (default is 2d).", + "name": "timeSpan", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/stats.TopCorridorsResult" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/top-assets-by-volume": { + "get": { + "description": "Returns a list of emitter_chain and asset pairs with ordered by volume.\nThe volume is calculated using the notional price of the symbol at the day the VAA was emitted.", + "tags": [ + "wormholescan" + ], + "operationId": "get-top-assets-by-volume", + "parameters": [ + { + "type": "string", + "description": "Time span, supported values: 7d, 15d, 30d.", + "name": "timeSpan", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/transactions.TopAssetsResponse" + } + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/top-chain-pairs-by-num-transfers": { + "get": { + "description": "Returns a list of the emitter_chain and destination_chain pair ordered by transfer count.", + "tags": [ + "wormholescan" + ], + "operationId": "get-top-chain-pairs-by-num-transfers", + "parameters": [ + { + "type": "string", + "description": "Time span, supported values: 7d, 15d, 30d.", + "name": "timeSpan", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/transactions.TopChainPairsResponse" + } + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/top-symbols-by-volume": { + "get": { + "description": "Returns a list of symbols by origin chain and tokens.\nThe volume is calculated using the notional price of the symbol at the day the VAA was emitted.", + "tags": [ + "wormholescan" + ], + "operationId": "top-symbols-by-volume", + "parameters": [ + { + "type": "string", + "description": "Time span, supported values: 7d, 15d and 30d (default is 7d).", + "name": "timeSpan", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/stats.TopSymbolByVolumeResult" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/transactions/": { + "get": { + "description": "Returns transactions. Output is paginated.", + "tags": [ + "wormholescan" + ], + "operationId": "list-transactions", + "parameters": [ + { + "type": "integer", + "description": "Page number. Starts at 0.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + }, + { + "enum": [ + "ASC", + "DESC" + ], + "type": "string", + "description": "Sort results in ascending or descending order.", + "name": "sortOrder", + "in": "query" + }, + { + "type": "string", + "description": "Filter transactions by Address.", + "name": "address", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/transactions.ListTransactionsResponse" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/transactions/:chain_id/:emitter/:seq": { + "get": { + "description": "Find VAA metadata by ID.", + "tags": [ + "wormholescan" + ], + "operationId": "get-transaction-by-id", + "parameters": [ + { + "type": "integer", + "description": "id of the blockchain", + "name": "chain_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "address of the emitter", + "name": "emitter", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "sequence of the VAA", + "name": "seq", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/transactions.TransactionDetail" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/vaas/": { + "get": { + "description": "Returns all VAAs. Output is paginated and can also be be sorted.", + "tags": [ + "wormholescan" + ], + "operationId": "find-all-vaas", + "parameters": [ + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + }, + { + "enum": [ + "ASC", + "DESC" + ], + "type": "string", + "description": "Sort results in ascending or descending order.", + "name": "sortOrder", + "in": "query" + }, + { + "type": "string", + "description": "Transaction hash of the VAA", + "name": "txHash", + "in": "query" + }, + { + "type": "boolean", + "description": "include the parsed contents of the VAA, if available", + "name": "parsedPayload", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_vaa_VaaDoc" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/vaas/:chain_id": { + "get": { + "description": "Returns all the VAAs generated in specific blockchain.", + "tags": [ + "wormholescan" + ], + "operationId": "find-vaas-by-chain", + "parameters": [ + { + "type": "integer", + "description": "id of the blockchain", + "name": "chain_id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + }, + { + "enum": [ + "ASC", + "DESC" + ], + "type": "string", + "description": "Sort results in ascending or descending order.", + "name": "sortOrder", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_vaa_VaaDoc" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/vaas/:chain_id/:emitter": { + "get": { + "description": "Returns all all the VAAs generated by a specific emitter address.", + "tags": [ + "wormholescan" + ], + "operationId": "find-vaas-by-emitter", + "parameters": [ + { + "type": "integer", + "description": "id of the blockchain", + "name": "chain_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "address of the emitter", + "name": "emitter", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "destination chain (deprecated param)", + "name": "toChain", + "in": "query" + }, + { + "type": "integer", + "description": "Page number.", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Number of elements per page.", + "name": "pageSize", + "in": "query" + }, + { + "enum": [ + "ASC", + "DESC" + ], + "type": "string", + "description": "Sort results in ascending or descending order.", + "name": "sortOrder", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_vaa_VaaDoc" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/vaas/:chain_id/:emitter/:seq": { + "get": { + "description": "Find a VAA by ID.", + "tags": [ + "wormholescan" + ], + "operationId": "find-vaa-by-id", + "parameters": [ + { + "type": "integer", + "description": "id of the blockchain", + "name": "chain_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "address of the emitter", + "name": "emitter", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "sequence of the VAA", + "name": "seq", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "include the parsed contents of the VAA, if available", + "name": "parsedPayload", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_vaa_VaaDoc" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/vaas/:chain_id/:emitter/:seq/duplicated": { + "get": { + "description": "Find duplicated VAA by ID.", + "tags": [ + "wormholescan" + ], + "operationId": "find-duplicated-vaa-by-id", + "parameters": [ + { + "type": "integer", + "description": "id of the blockchain", + "name": "chain_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "address of the emitter", + "name": "emitter", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "sequence of the VAA", + "name": "seq", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_vaa_VaaDoc" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/vaas/parse": { + "post": { + "description": "Parse a VAA.", + "tags": [ + "wormholescan" + ], + "operationId": "parse-vaa", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/parser.ParseVaaWithStandarizedPropertiesdResponse" + } + }, + "400": { + "description": "Bad Request" + }, + "404": { + "description": "Not Found" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/vaas/vaa-counts": { + "get": { + "description": "Returns the total number of VAAs emitted for each blockchain.", + "tags": [ + "wormholescan" + ], + "operationId": "get-vaa-counts", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.Response-array_vaa_VaaStats" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/version": { + "get": { + "description": "Get version/release information.", + "tags": [ + "wormholescan" + ], + "operationId": "get-version", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/infrastructure.VersionResponse" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/x-chain-activity": { + "get": { + "description": "Returns a list of chain pairs by origin chain and destination chain.\nThe list could be rendered by notional or transaction count.\nThe volume is calculated using the notional price of the symbol at the day the VAA was emitted.", + "tags": [ + "wormholescan" + ], + "operationId": "x-chain-activity", + "parameters": [ + { + "type": "string", + "description": "Time span, supported values: 7d, 30d, 90d, 1y and all-time (default is 7d).", + "name": "timeSpan", + "in": "query" + }, + { + "type": "string", + "description": "Renders the results using notional or tx count (default is notional).", + "name": "by", + "in": "query" + }, + { + "type": "string", + "description": "List of apps separated by comma (default is all apps).", + "name": "apps", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/transactions.ChainActivity" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/api/v1/x-chain-activity/tops": { + "get": { + "description": "Search for a specific period of time the number of transactions and the volume.", + "tags": [ + "wormholescan" + ], + "operationId": "x-chain-activity-tops", + "parameters": [ + { + "type": "string", + "description": "Time span, supported values: 1d, 1mo and 1y", + "name": "timespan", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "From date, supported format 2006-01-02T15:04:05Z07:00", + "name": "from", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "To date, supported format 2006-01-02T15:04:05Z07:00", + "name": "to", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "Search by appId", + "name": "appId", + "in": "query" + }, + { + "type": "string", + "description": "Search by sourceChain", + "name": "sourceChain", + "in": "query" + }, + { + "type": "string", + "description": "Search by targetChain", + "name": "targetChain", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/transactions.ChainActivityTopResult" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/swagger.json": { + "get": { + "description": "Returns the swagger specification for this API.", + "tags": [ + "wormholescan" + ], + "operationId": "swagger", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/v1/governor/available_notional_by_chain": { + "get": { + "description": "Get available notional by chainID\nSince from the wormhole-explorer point of view it is not a node, but has the information of all nodes,\nin order to build the endpoints it was assumed:\nThere are N number of remainingAvailableNotional values in the GovernorConfig collection. N = number of guardians\nfor a chainID. The smallest remainingAvailableNotional value for a chainID is used for the endpoint response.", + "tags": [ + "Guardian" + ], + "operationId": "governor-available-notional-by-chain", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/governor.AvailableNotionalResponse" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/v1/governor/enqueued_vaas": { + "get": { + "description": "Get enqueued VAAs", + "tags": [ + "Guardian" + ], + "operationId": "guardians-enqueued-vaas", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/governor.EnqueuedVaaResponse" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/v1/governor/is_vaa_enqueued/:chain_id/:emitter/:seq": { + "get": { + "description": "Check if vaa is enqueued", + "tags": [ + "Guardian" + ], + "operationId": "guardians-is-vaa-enqueued", + "parameters": [ + { + "type": "integer", + "description": "id of the blockchain", + "name": "chain_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "address of the emitter", + "name": "emitter", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "sequence of the vaa", + "name": "seq", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/governor.EnqueuedVaaResponse" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/v1/governor/token_list": { + "get": { + "description": "Get token list\nSince from the wormhole-explorer point of view it is not a node, but has the information of all nodes,\nin order to build the endpoints it was assumed:\nFor tokens with the same originChainId and originAddress and different price values for each node,\nthe price that has most occurrences in all the nodes for an originChainId and originAddress is returned.", + "tags": [ + "Guardian" + ], + "operationId": "guardians-token-list", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.TokenList" + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/v1/guardianset/current": { + "get": { + "description": "Get current guardian set.", + "tags": [ + "Guardian" + ], + "operationId": "guardian-set", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/guardian.GuardianSetResponse" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/v1/heartbeats": { + "get": { + "description": "Get heartbeats for guardians", + "tags": [ + "Guardian" + ], + "operationId": "guardians-hearbeats", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/heartbeats.HeartbeatsResponse" + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/v1/signed_batch_vaa/:chain_id/:emitter/sequence/:seq": { + "get": { + "description": "get a batch of VAA []byte from a chainID, emitter address and sequence.", + "tags": [ + "Guardian" + ], + "operationId": "guardians-find-signed-batch-vaa", + "parameters": [ + { + "type": "integer", + "description": "id of the blockchain", + "name": "chain_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "address of the emitter", + "name": "emitter", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "sequence of the VAA", + "name": "seq", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { + "vaaBytes": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/v1/signed_vaa/:chain_id/:emitter/:seq": { + "get": { + "description": "get a VAA []byte from a chainID, emitter address and sequence.", + "tags": [ + "Guardian" + ], + "operationId": "guardians-find-signed-vaa", + "parameters": [ + { + "type": "integer", + "description": "id of the blockchain", + "name": "chain_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "address of the emitter", + "name": "emitter", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "sequence of the VAA", + "name": "seq", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "properties": { + "vaaBytes": { + "type": "array", + "items": { + "type": "integer" + } + } + } + } + }, + "400": { + "description": "Bad Request" + }, + "500": { + "description": "Internal Server Error" + } + } + } + } + }, + "definitions": { + "address.AddressOverview": { + "type": "object", + "properties": { + "vaas": { + "type": "array", + "items": { + "$ref": "#/definitions/vaa.VaaDoc" + } + } + } + }, + "github_com_wormhole-foundation_wormhole-explorer_api_routes_guardian_guardian.GuardianSet": { + "type": "object", + "properties": { + "addresses": { + "type": "array", + "items": { + "type": "string" + } + }, + "index": { + "type": "integer" + } + } + }, + "governor.AvailableNotionalItemResponse": { + "type": "object", + "properties": { + "bigTransactionSize": { + "type": "string" + }, + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "notionalLimit": { + "type": "string" + }, + "remainingAvailableNotional": { + "type": "string" + } + } + }, + "governor.AvailableNotionalResponse": { + "type": "object", + "properties": { + "entries": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.AvailableNotionalItemResponse" + } + } + } + }, + "governor.Emitter": { + "type": "object", + "properties": { + "emitterAddress": { + "type": "string" + }, + "enqueuedVaas": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.EnqueuedVAA" + } + }, + "totalEnqueuedVaas": { + "type": "integer" + } + } + }, + "governor.EnqueuedVAA": { + "type": "object", + "properties": { + "notionalValue": { + "type": "integer" + }, + "releaseTime": { + "type": "string" + }, + "sequence": { + "type": "string" + }, + "txHash": { + "type": "string" + } + } + }, + "governor.EnqueuedVaa": { + "type": "object", + "properties": { + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "emitterAddress": { + "type": "string" + }, + "notionalValue": { + "type": "integer" + }, + "sequence": { + "type": "string" + }, + "txHash": { + "type": "string" + } + } + }, + "governor.EnqueuedVaaDetail": { + "type": "object", + "properties": { + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "emitterAddress": { + "type": "string" + }, + "notionalValue": { + "type": "integer" + }, + "releaseTime": { + "type": "integer" + }, + "sequence": { + "type": "string" + }, + "txHash": { + "type": "string" + } + } + }, + "governor.EnqueuedVaaItemResponse": { + "type": "object", + "properties": { + "emitterAddress": { + "type": "string" + }, + "emitterChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "notionalValue": { + "type": "string" + }, + "releaseTime": { + "type": "integer" + }, + "sequence": { + "type": "integer" + }, + "txHash": { + "type": "string" + } + } + }, + "governor.EnqueuedVaaResponse": { + "type": "object", + "properties": { + "entries": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.EnqueuedVaaItemResponse" + } + } + } + }, + "governor.EnqueuedVaas": { + "type": "object", + "properties": { + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "enqueuedVaas": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.EnqueuedVaa" + } + } + } + }, + "governor.GovConfig": { + "type": "object", + "properties": { + "chains": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.GovConfigChains" + } + }, + "counter": { + "type": "integer" + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nodeName": { + "type": "string" + }, + "tokens": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.GovConfigfTokens" + } + }, + "updatedAt": { + "type": "string" + } + } + }, + "governor.GovConfigChains": { + "type": "object", + "properties": { + "bigTransactionSize": { + "type": "integer" + }, + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "notionalLimit": { + "type": "integer" + } + } + }, + "governor.GovConfigfTokens": { + "type": "object", + "properties": { + "originAddress": { + "type": "string" + }, + "originChainId": { + "type": "integer" + }, + "price": { + "type": "number" + } + } + }, + "governor.GovStatus": { + "type": "object", + "properties": { + "chains": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.GovStatusChains" + } + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nodeName": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "governor.GovStatusChainEmitter": { + "type": "object", + "properties": { + "emitterAddress": { + "type": "string" + }, + "enqueuedVaas": {}, + "totalEnqueuedVaas": { + "type": "integer" + } + } + }, + "governor.GovStatusChains": { + "type": "object", + "properties": { + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "emitters": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.GovStatusChainEmitter" + } + }, + "remainingAvailableNotional": { + "type": "integer" + } + } + }, + "governor.GovernorLimit": { + "type": "object", + "properties": { + "availableNotional": { + "type": "integer" + }, + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "maxTransactionSize": { + "type": "integer" + }, + "notionalLimit": { + "type": "integer" + } + } + }, + "governor.GovernorVaasResponse": { + "type": "object", + "properties": { + "amount": { + "type": "integer" + }, + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "emitterAddress": { + "type": "string" + }, + "releaseTime": { + "type": "string" + }, + "sequence": { + "type": "string" + }, + "status": { + "type": "string" + }, + "txHash": { + "type": "string" + }, + "vaaId": { + "type": "string" + } + } + }, + "governor.MaxNotionalAvailableRecord": { + "type": "object", + "properties": { + "availableNotional": { + "type": "integer" + }, + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "createdAt": { + "type": "string" + }, + "emitters": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.Emitter" + } + }, + "id": { + "type": "string" + }, + "nodeName": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "governor.NotionalAvailable": { + "type": "object", + "properties": { + "availableNotional": { + "type": "integer" + }, + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + } + } + }, + "governor.NotionalAvailableDetail": { + "type": "object", + "properties": { + "availableNotional": { + "type": "integer" + }, + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nodeName": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "governor.NotionalLimitDetail": { + "type": "object", + "properties": { + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "string" + }, + "maxTransactionSize": { + "type": "integer" + }, + "nodeName": { + "type": "string" + }, + "notionalLimit": { + "type": "integer" + }, + "updatedAt": { + "type": "string" + } + } + }, + "governor.TokenList": { + "type": "object", + "properties": { + "originAddress": { + "type": "string" + }, + "originChainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "price": { + "type": "number" + } + } + }, + "guardian.GuardianSetResponse": { + "type": "object", + "properties": { + "guardianSet": { + "$ref": "#/definitions/github_com_wormhole-foundation_wormhole-explorer_api_routes_guardian_guardian.GuardianSet" + } + } + }, + "heartbeats.HeartbeatNetworkResponse": { + "type": "object", + "properties": { + "contractAddress": { + "type": "string" + }, + "errorCount": { + "type": "string" + }, + "height": { + "type": "string" + }, + "id": { + "type": "integer" + } + } + }, + "heartbeats.HeartbeatResponse": { + "type": "object", + "properties": { + "p2pNodeAddr": { + "type": "string" + }, + "rawHeartbeat": { + "$ref": "#/definitions/heartbeats.RawHeartbeat" + }, + "verifiedGuardianAddr": { + "type": "string" + } + } + }, + "heartbeats.HeartbeatsResponse": { + "type": "object", + "properties": { + "entries": { + "type": "array", + "items": { + "$ref": "#/definitions/heartbeats.HeartbeatResponse" + } + } + } + }, + "heartbeats.RawHeartbeat": { + "type": "object", + "properties": { + "bootTimestamp": { + "type": "string" + }, + "counter": { + "type": "string" + }, + "features": { + "type": "array", + "items": { + "type": "string" + } + }, + "guardianAddr": { + "type": "string" + }, + "networks": { + "type": "array", + "items": { + "$ref": "#/definitions/heartbeats.HeartbeatNetworkResponse" + } + }, + "nodeName": { + "type": "string" + }, + "timestamp": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "infrastructure.VersionResponse": { + "type": "object", + "properties": { + "branch": { + "type": "string" + }, + "build": { + "type": "string" + }, + "build_date": { + "type": "string" + }, + "machine": { + "type": "string" + }, + "user": { + "type": "string" + } + } + }, + "observations.ObservationDoc": { + "type": "object", + "properties": { + "emitterAddr": { + "type": "string" + }, + "emitterChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "guardianAddr": { + "type": "string" + }, + "hash": { + "type": "array", + "items": { + "type": "integer" + } + }, + "id": { + "type": "string" + }, + "indexedAt": { + "type": "string" + }, + "sequence": { + "type": "string" + }, + "signature": { + "type": "array", + "items": { + "type": "integer" + } + }, + "txHash": { + "type": "array", + "items": { + "type": "integer" + } + }, + "updatedAt": { + "type": "string" + } + } + }, + "operations.Content": { + "type": "object", + "properties": { + "payload": { + "type": "object", + "additionalProperties": {} + }, + "standarizedProperties": { + "$ref": "#/definitions/operations.StandardizedProperties" + } + } + }, + "operations.Data": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "value": { + "type": "object", + "additionalProperties": {} + } + } + }, + "operations.EmitterAddress": { + "type": "object", + "properties": { + "hex": { + "type": "string" + }, + "native": { + "type": "string" + } + } + }, + "operations.OperationResponse": { + "type": "object", + "properties": { + "content": { + "$ref": "#/definitions/operations.Content" + }, + "data": { + "type": "object", + "additionalProperties": {} + }, + "emitterAddress": { + "$ref": "#/definitions/operations.EmitterAddress" + }, + "emitterChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "id": { + "type": "string" + }, + "sequence": { + "type": "string" + }, + "sourceChain": { + "$ref": "#/definitions/operations.SourceChain" + }, + "targetChain": { + "$ref": "#/definitions/operations.TargetChain" + }, + "vaa": { + "$ref": "#/definitions/operations.Vaa" + } + } + }, + "operations.SourceChain": { + "type": "object", + "properties": { + "attribute": { + "$ref": "#/definitions/operations.Data" + }, + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "fee": { + "type": "string" + }, + "feeUSD": { + "type": "string" + }, + "from": { + "type": "string" + }, + "gasTokenNotional": { + "type": "string" + }, + "status": { + "type": "string" + }, + "timestamp": { + "type": "string" + }, + "transaction": { + "$ref": "#/definitions/operations.Transaction" + } + } + }, + "operations.StandardizedProperties": { + "type": "object", + "properties": { + "amount": { + "type": "string" + }, + "appIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "fee": { + "type": "string" + }, + "feeAddress": { + "type": "string" + }, + "feeChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "fromAddress": { + "type": "string" + }, + "fromChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "toAddress": { + "type": "string" + }, + "toChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "tokenAddress": { + "type": "string" + }, + "tokenChain": { + "$ref": "#/definitions/vaa.ChainID" + } + } + }, + "operations.TargetChain": { + "type": "object", + "properties": { + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "fee": { + "type": "string" + }, + "feeUSD": { + "type": "string" + }, + "from": { + "type": "string" + }, + "gasTokenNotional": { + "type": "string" + }, + "status": { + "type": "string" + }, + "timestamp": { + "type": "string" + }, + "to": { + "type": "string" + }, + "transaction": { + "$ref": "#/definitions/operations.Transaction" + } + } + }, + "operations.Transaction": { + "type": "object", + "properties": { + "secondTxHash": { + "type": "string" + }, + "txHash": { + "type": "string" + } + } + }, + "operations.Vaa": { + "type": "object", + "properties": { + "guardianSetIndex": { + "type": "integer" + }, + "isDuplicated": { + "type": "boolean" + }, + "raw": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, + "parser.ParseVaaWithStandarizedPropertiesdResponse": { + "type": "object", + "properties": { + "parsedPayload": {}, + "standardizedProperties": { + "$ref": "#/definitions/parser.StandardizedProperties" + } + } + }, + "parser.StandardizedProperties": { + "type": "object", + "properties": { + "amount": { + "type": "string" + }, + "appIds": { + "type": "array", + "items": { + "type": "string" + } + }, + "fee": { + "type": "string" + }, + "feeAddress": { + "type": "string" + }, + "feeChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "fromAddress": { + "type": "string" + }, + "fromChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "normalizedDecimals": { + "type": "integer" + }, + "toAddress": { + "type": "string" + }, + "toChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "tokenAddress": { + "type": "string" + }, + "tokenChain": { + "$ref": "#/definitions/vaa.ChainID" + } + } + }, + "protocols.ProtocolTotalValuesDTO": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "last_24_hour_volume": { + "type": "number" + }, + "last_day_diff_percentage": { + "type": "string" + }, + "last_day_diff_volume_percentage": { + "type": "string" + }, + "last_day_messages": { + "type": "integer" + }, + "protocol": { + "type": "string" + }, + "total_messages": { + "type": "integer" + }, + "total_value_locked": { + "type": "number" + }, + "total_value_secured": { + "type": "number" + }, + "total_value_transferred": { + "type": "number" + } + } + }, + "relays.DeliveryReponse": { + "type": "object", + "properties": { + "budget": { + "type": "string" + }, + "execution": { + "$ref": "#/definitions/relays.ResultExecutionResponse" + }, + "maxRefund": { + "type": "string" + }, + "relayGasUsed": { + "type": "integer" + }, + "targetChainDecimals": { + "type": "integer" + } + } + }, + "relays.InstructionsResponse": { + "type": "object", + "properties": { + "encodedExecutionInfo": { + "type": "string" + }, + "extraReceiverValue": { + "type": "object", + "properties": { + "_hex": { + "type": "string" + }, + "_isBigNumber": { + "type": "boolean" + } + } + }, + "refundAddress": { + "type": "string" + }, + "refundChainId": { + "type": "integer" + }, + "refundDeliveryProvider": { + "type": "string" + }, + "requestedReceiverValue": { + "type": "object", + "properties": { + "_hex": { + "type": "string" + }, + "_isBigNumber": { + "type": "boolean" + } + } + }, + "senderAddress": { + "type": "string" + }, + "sourceDeliveryProvider": { + "type": "string" + }, + "targetAddress": { + "type": "string" + }, + "targetChainId": { + "type": "integer" + }, + "vaaKeys": { + "type": "array", + "items": {} + } + } + }, + "relays.RelayDataResponse": { + "type": "object", + "properties": { + "delivery": { + "$ref": "#/definitions/relays.DeliveryReponse" + }, + "fromTxHash": { + "type": "string" + }, + "instructions": { + "$ref": "#/definitions/relays.InstructionsResponse" + }, + "maxAttempts": { + "type": "integer" + }, + "toTxHash": { + "type": "string" + } + } + }, + "relays.RelayResponse": { + "type": "object", + "properties": { + "completedAt": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/relays.RelayDataResponse" + }, + "failedAt": { + "type": "string" + }, + "id": { + "type": "string" + }, + "receivedAt": { + "type": "string" + }, + "relayer": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "relays.ResultExecutionResponse": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "gasUsed": { + "type": "string" + }, + "refundStatus": { + "type": "string" + }, + "revertString": { + "type": "string" + }, + "status": { + "type": "string" + }, + "transactionHash": { + "type": "string" + } + } + }, + "response.Response-address_AddressOverview": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/address.AddressOverview" + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-array_governor_EnqueuedVaaDetail": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.EnqueuedVaaDetail" + } + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-array_governor_EnqueuedVaas": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.EnqueuedVaas" + } + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-array_governor_GovStatus": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.GovStatus" + } + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-array_governor_GovernorLimit": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.GovernorLimit" + } + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-array_governor_GovernorVaasResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.GovernorVaasResponse" + } + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-array_governor_NotionalAvailable": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.NotionalAvailable" + } + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-array_governor_NotionalAvailableDetail": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.NotionalAvailableDetail" + } + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-array_governor_NotionalLimitDetail": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/governor.NotionalLimitDetail" + } + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-array_vaa_VaaDoc": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/vaa.VaaDoc" + } + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-array_vaa_VaaStats": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/vaa.VaaStats" + } + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-governor_GovConfig": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/governor.GovConfig" + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-governor_GovStatus": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/governor.GovStatus" + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.Response-governor_MaxNotionalAvailableRecord": { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/governor.MaxNotionalAvailableRecord" + }, + "pagination": { + "$ref": "#/definitions/response.ResponsePagination" + } + } + }, + "response.ResponsePagination": { + "type": "object", + "properties": { + "next": { + "type": "string" + } + } + }, + "stats.NativeTokenTransferActivity": { + "type": "object", + "properties": { + "destinationChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "emitterChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "symbol": { + "type": "string" + }, + "value": { + "type": "number" + } + } + }, + "stats.NativeTokenTransferByTime": { + "type": "object", + "properties": { + "symbol": { + "type": "string" + }, + "time": { + "type": "string" + }, + "value": { + "type": "number" + } + } + }, + "stats.NativeTokenTransferSummary": { + "type": "object", + "properties": { + "circulatingSupply": { + "type": "number" + }, + "fullyDilutedValuation": { + "type": "number" + }, + "image": { + "$ref": "#/definitions/stats.image" + }, + "links": { + "type": "object", + "additionalProperties": {} + }, + "marketCap": { + "type": "number" + }, + "platforms": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "price": { + "type": "number" + }, + "symbol": { + "type": "string" + }, + "totalSupply": { + "type": "number" + }, + "totalTokenTransferred": { + "type": "number" + }, + "totalValueTokenTransferred": { + "type": "number" + } + } + }, + "stats.NativeTokenTransferTopAddress": { + "type": "object", + "properties": { + "fromAddress": { + "type": "string" + }, + "value": { + "type": "number" + } + } + }, + "stats.NativeTokenTransferTopHolder": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "chain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "volume": { + "type": "number" + } + } + }, + "stats.Token": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "chain_id": { + "$ref": "#/definitions/vaa.ChainID" + }, + "circulating_supply": { + "type": "number" + }, + "coingecko_id": { + "type": "string" + }, + "fully_diluted_valuation": { + "type": "number" + }, + "image": { + "type": "object", + "properties": { + "large": { + "type": "string" + }, + "small": { + "type": "string" + }, + "thumb": { + "type": "string" + } + } + }, + "links": { + "type": "object", + "additionalProperties": {} + }, + "market_cap": { + "type": "number" + }, + "platforms": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "price": { + "type": "number" + }, + "price_change_percentage_24h": { + "type": "number" + }, + "symbol": { + "type": "string" + }, + "total_value_locked": { + "type": "number" + }, + "volume_24h": { + "type": "number" + } + } + }, + "stats.TokenResult": { + "type": "object", + "properties": { + "emitter_chain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "token_address": { + "type": "string" + }, + "token_chain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "txs": { + "type": "number" + }, + "volume": { + "type": "number" + } + } + }, + "stats.TopCorridor": { + "type": "object", + "properties": { + "emitter_chain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "target_chain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "token_address": { + "type": "string" + }, + "token_chain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "txs": { + "type": "integer" + } + } + }, + "stats.TopCorridorsResult": { + "type": "object", + "properties": { + "corridors": { + "type": "array", + "items": { + "$ref": "#/definitions/stats.TopCorridor" + } + } + } + }, + "stats.TopSymbolByVolumeResult": { + "type": "object", + "properties": { + "symbols": { + "type": "array", + "items": { + "$ref": "#/definitions/stats.TopSymbolResult" + } + } + } + }, + "stats.TopSymbolResult": { + "type": "object", + "properties": { + "symbol": { + "type": "string" + }, + "tokens": { + "type": "array", + "items": { + "$ref": "#/definitions/stats.TokenResult" + } + }, + "txs": { + "type": "number" + }, + "volume": { + "type": "number" + } + } + }, + "stats.image": { + "type": "object", + "properties": { + "large": { + "type": "string" + }, + "small": { + "type": "string" + }, + "thumb": { + "type": "string" + } + } + }, + "supply.SupplyInfoResponse": { + "type": "object", + "properties": { + "circulating_supply": { + "type": "string" + }, + "total_supply": { + "type": "string" + } + } + }, + "transactions.AssetWithVolume": { + "type": "object", + "properties": { + "emitterChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "symbol": { + "type": "string" + }, + "tokenAddress": { + "type": "string" + }, + "tokenChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "volume": { + "type": "string" + } + } + }, + "transactions.AttributeDoc": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "value": { + "type": "object", + "additionalProperties": {} + } + } + }, + "transactions.ChainActivity": { + "type": "object", + "properties": { + "txs": { + "type": "array", + "items": { + "$ref": "#/definitions/transactions.Tx" + } + } + } + }, + "transactions.ChainActivityTopResult": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "destination_chain": { + "type": "string" + }, + "emitter_chain": { + "type": "string" + }, + "from": { + "type": "string" + }, + "to": { + "type": "string" + }, + "volume": { + "type": "integer" + } + } + }, + "transactions.ChainPair": { + "type": "object", + "properties": { + "destinationChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "emitterChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "numberOfTransfers": { + "type": "string" + } + } + }, + "transactions.Destination": { + "type": "object", + "properties": { + "chain": { + "type": "integer" + }, + "percentage": { + "type": "number" + }, + "volume": { + "type": "number" + } + } + }, + "transactions.DestinationTx": { + "type": "object", + "properties": { + "blockNumber": { + "type": "string" + }, + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "from": { + "type": "string" + }, + "method": { + "type": "string" + }, + "status": { + "type": "string" + }, + "timestamp": { + "type": "string" + }, + "to": { + "type": "string" + }, + "txHash": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, + "transactions.GlobalTransactionDoc": { + "type": "object", + "properties": { + "destinationTx": { + "$ref": "#/definitions/transactions.DestinationTx" + }, + "id": { + "type": "string" + }, + "originTx": { + "$ref": "#/definitions/transactions.OriginTx" + } + } + }, + "transactions.ListTransactionsResponse": { + "type": "object", + "properties": { + "transactions": { + "type": "array", + "items": { + "$ref": "#/definitions/transactions.TransactionDetail" + } + } + } + }, + "transactions.OriginTx": { + "type": "object", + "properties": { + "attribute": { + "$ref": "#/definitions/transactions.AttributeDoc" + }, + "from": { + "type": "string" + }, + "status": { + "type": "string" + }, + "txHash": { + "type": "string" + } + } + }, + "transactions.ScorecardsResponse": { + "type": "object", + "properties": { + "24h_messages": { + "description": "Number of VAAs emitted in the last 24 hours (includes Pyth messages).", + "type": "string" + }, + "24h_volume": { + "description": "Volume transferred through the token bridge in the last 24 hours, in USD.", + "type": "string" + }, + "30d_volume": { + "description": "Volume transferred through the token bridge in the last 24 hours, in USD.", + "type": "string" + }, + "7d_volume": { + "description": "Volume transferred through the token bridge in the last 24 hours, in USD.", + "type": "string" + }, + "total_messages": { + "description": "Number of VAAs emitted since the creation of the network (includes Pyth messages).", + "type": "string" + }, + "total_tx_count": { + "description": "Number of VAAs emitted since the creation of the network (does not include Pyth messages)", + "type": "string" + }, + "total_volume": { + "type": "string" + }, + "tvl": { + "description": "Total value locked in USD.", + "type": "string" + } + } + }, + "transactions.Token": { + "type": "object", + "properties": { + "coingeckoId": { + "type": "string" + }, + "decimals": { + "type": "integer" + }, + "symbol": { + "type": "string" + } + } + }, + "transactions.TopAssetsResponse": { + "type": "object", + "properties": { + "assets": { + "type": "array", + "items": { + "$ref": "#/definitions/transactions.AssetWithVolume" + } + } + } + }, + "transactions.TopChainPairsResponse": { + "type": "object", + "properties": { + "chainPairs": { + "type": "array", + "items": { + "$ref": "#/definitions/transactions.ChainPair" + } + } + } + }, + "transactions.TransactionCountResult": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "time": { + "type": "string" + } + } + }, + "transactions.TransactionDetail": { + "type": "object", + "properties": { + "emitterAddress": { + "description": "EmitterAddress contains the VAA's emitter address, encoded in hex.", + "type": "string" + }, + "emitterChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "emitterNativeAddress": { + "description": "EmitterNativeAddress contains the VAA's emitter address, encoded in the emitter chain's native format.", + "type": "string" + }, + "globalTx": { + "$ref": "#/definitions/transactions.GlobalTransactionDoc" + }, + "id": { + "type": "string" + }, + "payload": { + "type": "object", + "additionalProperties": true + }, + "standardizedProperties": { + "type": "object", + "additionalProperties": true + }, + "symbol": { + "type": "string" + }, + "timestamp": { + "type": "string" + }, + "tokenAmount": { + "type": "string" + }, + "txHash": { + "type": "string" + }, + "usdAmount": { + "type": "string" + } + } + }, + "transactions.Tx": { + "type": "object", + "properties": { + "chain": { + "type": "integer" + }, + "destinations": { + "type": "array", + "items": { + "$ref": "#/definitions/transactions.Destination" + } + }, + "percentage": { + "type": "number" + }, + "volume": { + "type": "number" + } + } + }, + "vaa.ChainID": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 28, + 29, + 30, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 43, + 44, + 3104, + 4000, + 4001, + 4002, + 4003, + 4004, + 4005, + 4006, + 4007, + 4008, + 10002, + 10003, + 10004, + 10005, + 10006, + 10007 + ], + "x-enum-varnames": [ + "ChainIDUnset", + "ChainIDSolana", + "ChainIDEthereum", + "ChainIDTerra", + "ChainIDBSC", + "ChainIDPolygon", + "ChainIDAvalanche", + "ChainIDOasis", + "ChainIDAlgorand", + "ChainIDAurora", + "ChainIDFantom", + "ChainIDKarura", + "ChainIDAcala", + "ChainIDKlaytn", + "ChainIDCelo", + "ChainIDNear", + "ChainIDMoonbeam", + "ChainIDTerra2", + "ChainIDInjective", + "ChainIDOsmosis", + "ChainIDSui", + "ChainIDAptos", + "ChainIDArbitrum", + "ChainIDOptimism", + "ChainIDGnosis", + "ChainIDPythNet", + "ChainIDXpla", + "ChainIDBtc", + "ChainIDBase", + "ChainIDSei", + "ChainIDRootstock", + "ChainIDScroll", + "ChainIDMantle", + "ChainIDBlast", + "ChainIDXLayer", + "ChainIDLinea", + "ChainIDBerachain", + "ChainIDSeiEVM", + "ChainIDSnaxchain", + "ChainIDUnichain", + "ChainIDWormchain", + "ChainIDCosmoshub", + "ChainIDEvmos", + "ChainIDKujira", + "ChainIDNeutron", + "ChainIDCelestia", + "ChainIDStargaze", + "ChainIDSeda", + "ChainIDDymension", + "ChainIDProvenance", + "ChainIDSepolia", + "ChainIDArbitrumSepolia", + "ChainIDBaseSepolia", + "ChainIDOptimismSepolia", + "ChainIDHolesky", + "ChainIDPolygonSepolia" + ] + }, + "vaa.VaaDoc": { + "type": "object", + "properties": { + "digest": { + "type": "string" + }, + "emitterAddr": { + "type": "string" + }, + "emitterChain": { + "$ref": "#/definitions/vaa.ChainID" + }, + "emitterNativeAddr": { + "type": "string" + }, + "guardianSetIndex": { + "type": "integer" + }, + "id": { + "type": "string" + }, + "indexedAt": { + "type": "string" + }, + "isDuplicated": { + "type": "boolean" + }, + "payload": { + "description": "Payload is an extension field - it is not present in the guardian API.", + "type": "object", + "additionalProperties": true + }, + "timestamp": { + "type": "string" + }, + "txHash": { + "description": "TxHash is an extension field - it is not present in the guardian API.", + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "vaa": { + "type": "array", + "items": { + "type": "integer" + } + }, + "version": { + "type": "integer" + } + } + }, + "vaa.VaaStats": { + "type": "object", + "properties": { + "chainId": { + "$ref": "#/definitions/vaa.ChainID" + }, + "count": { + "type": "integer" + } + } + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index 5833a5c976..7bd8281c12 100644 --- a/package.json +++ b/package.json @@ -165,7 +165,8 @@ "packages/kyc-oracle", "packages/metatransaction-broadcaster", "packages/package-utils", - "packages/reputation-miner" + "packages/reputation-miner", + "helpers/wormholescanMock" ], "dependencies": { "@certusone/wormhole-sdk": "^0.10.15", diff --git a/packages/wormhole-relayer/index.ts b/packages/wormhole-relayer/index.ts index 2aa5bb7bd0..1a840231da 100644 --- a/packages/wormhole-relayer/index.ts +++ b/packages/wormhole-relayer/index.ts @@ -110,7 +110,7 @@ const loader = new TruffleLoader({ } const hash = ctx.sourceTxHash; - console.log(`Got a VAA with sequence: ${vaa.sequence} from with txhash: ${hash}`); + console.log(`Got a VAA with id: ${vaa.id.emitterChain}/${vaa.id.emitterAddress}/${vaa.id.sequence} from txhash: ${hash}`); const [ destinationEvmChainId, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e9b06092cd..0f2adf01dc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -232,6 +232,49 @@ importers: specifier: ^1.10.4 version: 1.10.4 + helpers/wormholescanMock: + dependencies: + '@openapitools/openapi-generator-cli': + specifier: ^2.15.3 + version: 2.16.2 + dotenv: + specifier: ^8.0.0 + version: 8.6.0 + ethers: + specifier: ^5.7.2 + version: 5.7.2 + ethers-types: + specifier: ^3.17.3 + version: 3.18.1(ethers@5.7.2) + express: + specifier: ^4.21.1 + version: 4.21.2 + openapi-generator: + specifier: ^0.1.39 + version: 0.1.39(ajv@6.12.6) + swagger-typescript-codegen: + specifier: ^3.2.4 + version: 3.2.4 + devDependencies: + '@types/express': + specifier: ^5.0.0 + version: 5.0.0 + '@types/node': + specifier: ^22.10.0 + version: 22.10.10 + concurrently: + specifier: ^9.1.0 + version: 9.1.2 + nodemon: + specifier: ^3.1.7 + version: 3.1.9 + ts-node: + specifier: ^9.1.1 + version: 9.1.1(typescript@5.7.3) + typescript: + specifier: ^5.7.2 + version: 5.7.3 + lib/safe-contracts: devDependencies: '@gnosis.pm/mock-contract': @@ -516,12 +559,10 @@ packages: dependencies: '@babel/highlight': 7.24.7 picocolors: 1.1.1 - dev: true /@babel/helper-validator-identifier@7.24.7: resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} - dev: true /@babel/highlight@7.24.7: resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} @@ -531,7 +572,6 @@ packages: chalk: 2.4.2 js-tokens: 4.0.0 picocolors: 1.0.1 - dev: true /@babel/polyfill@7.12.1: resolution: {integrity: sha512-X0pi0V6gxLi6lFZpGmeNa4zxtwEmCs42isWLNjZZDE0Y8yVfgu0T2OAHlzBbdYlqbW/YXVvoBHpATEM+goCj8g==} @@ -2063,6 +2103,10 @@ packages: '@ethersproject/properties': 5.7.0 '@ethersproject/strings': 5.7.0 + /@exodus/schemasafe@1.3.0: + resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==} + dev: false + /@fastify/busboy@2.1.1: resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} @@ -2618,6 +2662,11 @@ packages: call-bind: 1.0.8 dev: true + /@lukeed/csprng@1.1.0: + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + dev: false + /@metamask/eth-sig-util@4.0.1: resolution: {integrity: sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==} engines: {node: '>=12.0.0'} @@ -2694,6 +2743,69 @@ packages: - bufferutil - utf-8-validate + /@nestjs/axios@3.1.3(@nestjs/common@10.4.15)(axios@1.7.9)(rxjs@7.8.1): + resolution: {integrity: sha512-RZ/63c1tMxGLqyG3iOCVt7A72oy4x1eM6QEhd4KzCYpaVWW0igq0WSREeRoEZhIxRcZfDfIIkvsOMiM7yfVGZQ==} + peerDependencies: + '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + axios: ^1.3.1 + rxjs: ^6.0.0 || ^7.0.0 + dependencies: + '@nestjs/common': 10.4.15(reflect-metadata@0.1.13)(rxjs@7.8.1) + axios: 1.7.9 + rxjs: 7.8.1 + dev: false + + /@nestjs/common@10.4.15(reflect-metadata@0.1.13)(rxjs@7.8.1): + resolution: {integrity: sha512-vaLg1ZgwhG29BuLDxPA9OAcIlgqzp9/N8iG0wGapyUNTf4IY4O6zAHgN6QalwLhFxq7nOI021vdRojR1oF3bqg==} + peerDependencies: + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + dependencies: + iterare: 1.2.1 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.8.1 + uid: 2.0.2 + dev: false + + /@nestjs/core@10.4.15(@nestjs/common@10.4.15)(reflect-metadata@0.1.13)(rxjs@7.8.1): + resolution: {integrity: sha512-UBejmdiYwaH6fTsz2QFBlC1cJHM+3UDeLZN+CiP9I1fRv2KlBZsmozGLbV5eS1JAVWJB4T5N5yQ0gjN8ZvcS2w==} + requiresBuild: true + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + '@nestjs/websockets': ^10.0.0 + reflect-metadata: ^0.1.12 || ^0.2.0 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true + dependencies: + '@nestjs/common': 10.4.15(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nuxtjs/opencollective': 0.3.2 + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 3.3.0 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.8.1 + uid: 2.0.2 + transitivePeerDependencies: + - encoding + dev: false + /@noble/curves@1.4.2: resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} dependencies: @@ -3098,9 +3210,56 @@ packages: dev: false optional: true + /@nuxtjs/opencollective@0.3.2: + resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + dependencies: + chalk: 4.1.2 + consola: 2.15.3 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + dev: false + /@one-ini/wasm@0.1.1: resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + /@openapitools/openapi-generator-cli@2.16.2: + resolution: {integrity: sha512-AjPxFPpCr8atXYWAFRlWttWcLz0+2VEEw2dTk8wLBwzwz3+dN/8t1Zi4s89QNPJyrOm1lc+g42nNIy8MJlWOIA==} + engines: {node: '>=16'} + hasBin: true + requiresBuild: true + dependencies: + '@nestjs/axios': 3.1.3(@nestjs/common@10.4.15)(axios@1.7.9)(rxjs@7.8.1) + '@nestjs/common': 10.4.15(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.4.15(@nestjs/common@10.4.15)(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nuxtjs/opencollective': 0.3.2 + axios: 1.7.9 + chalk: 4.1.2 + commander: 8.3.0 + compare-versions: 4.1.4 + concurrently: 6.5.1 + console.table: 0.10.0 + fs-extra: 10.1.0 + glob: 9.3.5 + inquirer: 8.2.6 + lodash: 4.17.21 + proxy-agent: 6.4.0 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.8.1 + transitivePeerDependencies: + - '@nestjs/microservices' + - '@nestjs/platform-express' + - '@nestjs/websockets' + - class-transformer + - class-validator + - debug + - encoding + - supports-color + dev: false + /@openzeppelin/contracts@3.4.2: resolution: {integrity: sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA==} dev: true @@ -3411,7 +3570,6 @@ packages: /@sindresorhus/is@0.14.0: resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==} engines: {node: '>=6'} - dev: true /@sindresorhus/is@4.6.0: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} @@ -3648,7 +3806,6 @@ packages: engines: {node: '>=6'} dependencies: defer-to-connect: 1.1.3 - dev: true /@szmarczak/http-timer@4.0.6: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} @@ -3707,6 +3864,10 @@ packages: dev: false optional: true + /@tootallnate/quickjs-emscripten@0.23.0: + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + dev: false + /@truffle/abi-utils@1.0.3: resolution: {integrity: sha512-AWhs01HCShaVKjml7Z4AbVREr/u4oiWxCcoR7Cktm0mEvtT04pvnxW5xB/cI4znRkrbPdFQlFt67kgrAjesYkw==} engines: {node: ^16.20 || ^18.16 || >=20} @@ -3903,6 +4064,13 @@ packages: dependencies: '@types/node': 20.17.16 + /@types/body-parser@1.19.5: + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + dependencies: + '@types/connect': 3.4.38 + '@types/node': 22.10.10 + dev: true + /@types/cacheable-request@6.0.3: resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} dependencies: @@ -3912,6 +4080,10 @@ packages: '@types/responselike': 1.0.3 dev: true + /@types/caseless@0.12.5: + resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} + dev: false + /@types/chai@4.3.16: resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} dev: true @@ -3931,6 +4103,24 @@ packages: dependencies: '@types/node': 20.17.16 + /@types/express-serve-static-core@5.0.5: + resolution: {integrity: sha512-GLZPrd9ckqEBFMcVM/qRFAP0Hg3qiVEojgEFsx/N/zKXsBzbGF6z5FBDpZ0+Xhp1xr+qRZYjfGr1cWHB9oFHSA==} + dependencies: + '@types/node': 22.10.10 + '@types/qs': 6.9.18 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + dev: true + + /@types/express@5.0.0: + resolution: {integrity: sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==} + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 5.0.5 + '@types/qs': 6.9.18 + '@types/serve-static': 1.15.7 + dev: true + /@types/form-data@0.0.33: resolution: {integrity: sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==} dependencies: @@ -3948,6 +4138,10 @@ packages: resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} dev: true + /@types/http-errors@2.0.4: + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + dev: true + /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true @@ -3960,7 +4154,6 @@ packages: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: '@types/node': 20.17.16 - dev: true /@types/lodash.values@4.3.9: resolution: {integrity: sha512-IJ20OEfqNwm3k8ENwoM3q0yOs4UMpgtD4GqxB4lwBHToGthHWqhyh5DdSgQjioocz0QK2SSBkJfCq95ZTV8BTw==} @@ -3980,6 +4173,10 @@ packages: /@types/lru-cache@5.1.1: resolution: {integrity: sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==} + /@types/mime@1.3.5: + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + dev: true + /@types/minimatch@5.1.2: resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==} dev: true @@ -4025,10 +4222,19 @@ packages: dependencies: undici-types: 6.19.8 + /@types/node@22.10.10: + resolution: {integrity: sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==} + dependencies: + undici-types: 6.20.0 + /@types/node@8.10.66: resolution: {integrity: sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==} dev: true + /@types/nunjucks@3.2.6: + resolution: {integrity: sha512-pHiGtf83na1nCzliuAdq8GowYiXvH5l931xZ0YEHaLMNFgynpEqx+IPStlu7UaDkehfvl01e4x/9Tpwhy7Ue3w==} + dev: false + /@types/pbkdf2@3.1.2: resolution: {integrity: sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==} dependencies: @@ -4046,6 +4252,19 @@ packages: resolution: {integrity: sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==} dev: true + /@types/range-parser@1.2.7: + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + dev: true + + /@types/request@2.48.12: + resolution: {integrity: sha512-G3sY+NpsA9jnwm0ixhAFQSJ3Q9JkpLZpJbI3GMv0mIAT0y3mRabYeINzal5WOChIiaTEGQYlHOKgkaM9EisWHw==} + dependencies: + '@types/caseless': 0.12.5 + '@types/node': 22.10.10 + '@types/tough-cookie': 4.0.5 + form-data: 2.5.2 + dev: false + /@types/resolve@0.0.8: resolution: {integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==} dependencies: @@ -4056,7 +4275,6 @@ packages: resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} dependencies: '@types/node': 20.17.16 - dev: true /@types/secp256k1@4.0.6: resolution: {integrity: sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==} @@ -4066,6 +4284,21 @@ packages: /@types/seedrandom@3.0.1: resolution: {integrity: sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw==} + /@types/send@0.17.4: + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + dependencies: + '@types/mime': 1.3.5 + '@types/node': 22.10.10 + dev: true + + /@types/serve-static@1.15.7: + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + dependencies: + '@types/http-errors': 2.0.4 + '@types/node': 22.10.10 + '@types/send': 0.17.4 + dev: true + /@types/sinon-chai@3.2.12: resolution: {integrity: sha512-9y0Gflk3b0+NhQZ/oxGtaAJDvRywCa5sIyaVnounqLvmf93yBF4EgIRspePtkMs3Tr844nCclYMlcCNmLCvjuQ==} dependencies: @@ -4083,6 +4316,10 @@ packages: resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} dev: true + /@types/tough-cookie@4.0.5: + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + dev: false + /@types/triple-beam@1.3.5: resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} @@ -4694,6 +4931,10 @@ packages: jsonparse: 1.3.1 through: 2.3.8 + /a-sync-waterfall@1.0.1: + resolution: {integrity: sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==} + dev: false + /abbrev@1.0.9: resolution: {integrity: sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==} dev: true @@ -4857,6 +5098,11 @@ packages: transitivePeerDependencies: - supports-color + /agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + dev: false + /agentkeepalive@4.5.0: resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} engines: {node: '>= 8.0.0'} @@ -4886,7 +5132,6 @@ packages: fast-deep-equal: 1.1.0 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.3.1 - dev: true /ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -4895,7 +5140,6 @@ packages: fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - dev: true /ajv@8.16.0: resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} @@ -4969,12 +5213,10 @@ packages: /ansi-regex@2.1.1: resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} engines: {node: '>=0.10.0'} - dev: true /ansi-regex@3.0.1: resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==} engines: {node: '>=4'} - dev: true /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} @@ -5280,7 +5522,6 @@ packages: /asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - dev: true /asn1.js@4.10.1: resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} @@ -5294,12 +5535,10 @@ packages: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} dependencies: safer-buffer: 2.1.2 - dev: true /assert-plus@1.0.0: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} - dev: true /assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} @@ -5314,6 +5553,13 @@ packages: resolution: {integrity: sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==} dev: true + /ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + dependencies: + tslib: 2.8.1 + dev: false + /astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} @@ -5390,11 +5636,9 @@ packages: /aws-sign2@0.7.0: resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} - dev: true /aws4@1.13.0: resolution: {integrity: sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==} - dev: true /axios@0.21.4: resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==} @@ -6059,11 +6303,15 @@ packages: pascalcase: 0.1.1 dev: true + /basic-ftp@5.0.5: + resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} + engines: {node: '>=10.0.0'} + dev: false + /bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} dependencies: tweetnacl: 0.14.5 - dev: true /bech32@1.1.4: resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==} @@ -6071,6 +6319,36 @@ packages: /bech32@2.0.0: resolution: {integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==} + /better-ajv-errors@0.6.7(ajv@5.5.2): + resolution: {integrity: sha512-PYgt/sCzR4aGpyNy5+ViSQ77ognMnWq7745zM+/flYO4/Yisdtp9wDQW2IKCyVYPUxQt3E/b5GBSwfhd1LPdlg==} + peerDependencies: + ajv: 4.11.8 - 6 + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/runtime': 7.26.0 + ajv: 5.5.2 + chalk: 2.4.2 + core-js: 3.40.0 + json-to-ast: 2.1.0 + jsonpointer: 4.1.0 + leven: 3.1.0 + dev: false + + /better-ajv-errors@0.6.7(ajv@6.12.6): + resolution: {integrity: sha512-PYgt/sCzR4aGpyNy5+ViSQ77ognMnWq7745zM+/flYO4/Yisdtp9wDQW2IKCyVYPUxQt3E/b5GBSwfhd1LPdlg==} + peerDependencies: + ajv: 4.11.8 - 6 + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/runtime': 7.26.0 + ajv: 6.12.6 + chalk: 2.4.2 + core-js: 3.40.0 + json-to-ast: 2.1.0 + jsonpointer: 4.1.0 + leven: 3.1.0 + dev: false + /better-sqlite3@9.6.0: resolution: {integrity: sha512-yR5HATnqeYNVnkaUTf4bOP2dJSnyhP4puJN/QPRyx4YkBEEUxib422n2XzPqDEHjQQqazoYoADdAm5vE15+dAQ==} requiresBuild: true @@ -6239,6 +6517,20 @@ packages: bs58: 4.0.1 text-encoding-utf-8: 1.0.2 + /boxen@4.2.0: + resolution: {integrity: sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==} + engines: {node: '>=8'} + dependencies: + ansi-align: 3.0.1 + camelcase: 5.3.1 + chalk: 3.0.0 + cli-boxes: 2.2.1 + string-width: 4.2.3 + term-size: 2.2.1 + type-fest: 0.8.1 + widest-line: 3.1.0 + dev: false + /boxen@5.1.2: resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} engines: {node: '>=10'} @@ -6558,7 +6850,6 @@ packages: lowercase-keys: 2.0.0 normalize-url: 4.5.1 responselike: 1.0.2 - dev: true /cacheable-request@7.0.4: resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} @@ -6614,6 +6905,10 @@ packages: call-bind-apply-helpers: 1.0.1 get-intrinsic: 1.2.7 + /call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + dev: false + /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -6666,7 +6961,6 @@ packages: /caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - dev: true /catering@2.1.1: resolution: {integrity: sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==} @@ -6726,6 +7020,14 @@ packages: escape-string-regexp: 1.0.5 supports-color: 5.5.0 + /chalk@3.0.0: + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: false + /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -6760,6 +7062,10 @@ packages: upper-case-first: 1.1.2 dev: true + /chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: false + /charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} dev: true @@ -6895,6 +7201,18 @@ packages: resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} engines: {node: '>=6'} + /cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: false + + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + dev: false + /cli-table3@0.5.1: resolution: {integrity: sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==} engines: {node: '>=6'} @@ -6921,6 +7239,19 @@ packages: colors: 1.0.3 dev: true + /cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + dev: false + + /cli@1.0.1: + resolution: {integrity: sha512-41U72MB56TfUMGndAKK8vJ78eooOD4Z5NOL4xEfjc0c23s+6EYKXlXsmACBVclLP1yOfWCgEganVzddVrSNoTg==} + engines: {node: '>=0.2.5'} + dependencies: + exit: 0.1.2 + glob: 7.2.3 + dev: false + /cliui@3.2.0: resolution: {integrity: sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==} dependencies: @@ -6935,7 +7266,6 @@ packages: string-width: 2.1.1 strip-ansi: 4.0.0 wrap-ansi: 2.1.0 - dev: true /cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} @@ -6956,7 +7286,12 @@ packages: resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} dependencies: mimic-response: 1.0.1 - dev: true + + /clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + requiresBuild: true + dev: false /clone@2.1.2: resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} @@ -6972,10 +7307,14 @@ packages: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + /code-error-fragment@0.0.230: + resolution: {integrity: sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==} + engines: {node: '>= 4'} + dev: false + /code-point-at@1.1.0: resolution: {integrity: sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==} engines: {node: '>=0.10.0'} - dev: true /collection-visit@1.0.0: resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} @@ -7021,6 +7360,10 @@ packages: color-convert: 1.9.3 color-string: 1.9.1 + /colorful@2.1.0: + resolution: {integrity: sha512-DpDLDvi/vPzqoPX7Dw44ZZf004DCdEcCx1pf5hq5aipVHXjwgRSYGCz3m17rA2XCduW91wJUapge8/3qLvjYcg==} + dev: false + /colors@1.0.3: resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} engines: {node: '>=0.1.90'} @@ -7078,10 +7421,19 @@ packages: resolution: {integrity: sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==} dev: true + /commander@5.1.0: + resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} + engines: {node: '>= 6'} + dev: false + /commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} + /compare-versions@4.1.4: + resolution: {integrity: sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==} + dev: false + /component-emitter@1.3.1: resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} dev: true @@ -7100,6 +7452,35 @@ packages: typedarray: 0.0.6 dev: true + /concurrently@6.5.1: + resolution: {integrity: sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==} + engines: {node: '>=10.0.0'} + hasBin: true + dependencies: + chalk: 4.1.2 + date-fns: 2.30.0 + lodash: 4.17.21 + rxjs: 6.6.7 + spawn-command: 0.0.2 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 16.2.0 + dev: false + + /concurrently@9.1.2: + resolution: {integrity: sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==} + engines: {node: '>=18'} + hasBin: true + dependencies: + chalk: 4.1.2 + lodash: 4.17.21 + rxjs: 7.8.1 + shell-quote: 1.8.2 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 17.7.2 + dev: true + /condense-newlines@0.2.1: resolution: {integrity: sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==} engines: {node: '>=0.10.0'} @@ -7114,15 +7495,44 @@ packages: ini: 1.3.8 proto-list: 1.2.4 + /configstore@5.0.1: + resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} + engines: {node: '>=8'} + dependencies: + dot-prop: 5.3.0 + graceful-fs: 4.2.11 + make-dir: 3.1.0 + unique-string: 2.0.0 + write-file-atomic: 3.0.3 + xdg-basedir: 4.0.0 + dev: false + /confusing-browser-globals@1.0.11: resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} dev: true + /consola@2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + dev: false + + /console-browserify@1.1.0: + resolution: {integrity: sha512-duS7VP5pvfsNLDvL1O4VOEbw37AI3A4ZUQYemvDlnpGrNu9tprR7BYWpDYwC0Xia0Zxz5ZupdiIrUp0GH1aXfg==} + dependencies: + date-now: 0.1.4 + dev: false + /console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} requiresBuild: true optional: true + /console.table@0.10.0: + resolution: {integrity: sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==} + engines: {node: '> 0.10'} + dependencies: + easy-table: 1.1.0 + dev: false + /consolidate@0.16.0(ejs@3.1.10): resolution: {integrity: sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ==} engines: {node: '>= 0.10.0'} @@ -7379,9 +7789,13 @@ packages: requiresBuild: true dev: true + /core-js@3.40.0: + resolution: {integrity: sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==} + requiresBuild: true + dev: false + /core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} - dev: true /core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -7546,7 +7960,6 @@ packages: semver: 5.7.2 shebang-command: 1.2.0 which: 1.3.1 - dev: true /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} @@ -7603,6 +8016,11 @@ packages: /crypto-js@4.2.0: resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + /crypto-random-string@2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + dev: false + /css-select@5.1.0: resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} dependencies: @@ -7631,7 +8049,11 @@ packages: engines: {node: '>=0.10'} dependencies: assert-plus: 1.0.0 - dev: true + + /data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + dev: false /data-view-buffer@1.0.1: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} @@ -7687,7 +8109,18 @@ packages: is-data-view: 1.0.2 dev: true - /death@1.1.0: + /date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + dependencies: + '@babel/runtime': 7.26.0 + dev: false + + /date-now@0.1.4: + resolution: {integrity: sha512-AsElvov3LoNB7tf5k37H2jYSB+ZZPMT5sG2QjJCcdlV5chIv6htBUBUui2IKRjgtKAKtCBN7Zbwa+MtwLjSeNw==} + dev: false + + /death@1.1.0: resolution: {integrity: sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==} dev: true @@ -7758,6 +8191,19 @@ packages: dependencies: ms: 2.1.2 + /debug@4.4.0(supports-color@5.5.0): + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + supports-color: 5.5.0 + dev: true + /debug@4.4.0(supports-color@8.1.1): resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} @@ -7773,7 +8219,6 @@ packages: /decamelize@1.2.0: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} - dev: true /decamelize@4.0.0: resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} @@ -7792,7 +8237,6 @@ packages: engines: {node: '>=4'} dependencies: mimic-response: 1.0.1 - dev: true /decompress-response@4.2.1: resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} @@ -7840,9 +8284,15 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true + /defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + requiresBuild: true + dependencies: + clone: 1.0.4 + dev: false + /defer-to-connect@1.1.3: resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==} - dev: true /defer-to-connect@2.0.1: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} @@ -7907,6 +8357,15 @@ packages: requiresBuild: true dev: true + /degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 + dev: false + /delay@5.0.0: resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} engines: {node: '>=10'} @@ -8051,6 +8510,13 @@ packages: esutils: 2.0.3 dev: true + /dom-serializer@0.2.2: + resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==} + dependencies: + domelementtype: 2.3.0 + entities: 2.2.0 + dev: false + /dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} dependencies: @@ -8064,9 +8530,18 @@ packages: requiresBuild: true dev: true + /domelementtype@1.3.1: + resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} + dev: false + /domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - dev: true + + /domhandler@2.3.0: + resolution: {integrity: sha512-q9bUwjfp7Eif8jWxxxPSykdRZAb6GkguBGSgvvCrhI9wB71W2K/Kvv4E61CF/mcCfnVJDeDWx/Vb/uAqbDj6UQ==} + dependencies: + domelementtype: 1.3.1 + dev: false /domhandler@5.0.3: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} @@ -8075,6 +8550,13 @@ packages: domelementtype: 2.3.0 dev: true + /domutils@1.5.1: + resolution: {integrity: sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==} + dependencies: + dom-serializer: 0.2.2 + domelementtype: 1.3.1 + dev: false + /domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} dependencies: @@ -8095,10 +8577,16 @@ packages: no-case: 3.0.4 tslib: 2.6.3 + /dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + dependencies: + is-obj: 2.0.0 + dev: false + /dotenv@8.6.0: resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} engines: {node: '>=10'} - dev: true /dotignore@0.1.2: resolution: {integrity: sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==} @@ -8133,17 +8621,21 @@ packages: /duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} - dev: true /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + /easy-table@1.1.0: + resolution: {integrity: sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==} + optionalDependencies: + wcwidth: 1.0.1 + dev: false + /ecc-jsbn@0.1.2: resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} dependencies: jsbn: 0.1.1 safer-buffer: 2.1.2 - dev: true /eccrypto@1.1.6: resolution: {integrity: sha512-d78ivVEzu7Tn0ZphUUaL43+jVPKTMPFGtmgtz1D0LrFn7cY3K8CdrvibuLz2AAkHBLKZtR8DMbB2ukRYFk987A==} @@ -8284,6 +8776,14 @@ packages: ansi-colors: 4.1.3 strip-ansi: 6.0.1 + /entities@1.0.0: + resolution: {integrity: sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ==} + dev: false + + /entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + dev: false + /entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -8527,6 +9027,10 @@ packages: es6-symbol: 3.1.4 dev: true + /es6-promise@3.3.1: + resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + dev: false + /es6-promise@4.2.8: resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} @@ -8579,6 +9083,11 @@ packages: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} + /escape-goat@2.1.1: + resolution: {integrity: sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==} + engines: {node: '>=8'} + dev: false + /escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} @@ -8616,6 +9125,18 @@ packages: source-map: 0.2.0 dev: true + /escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + dev: false + /eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.31.0)(eslint@8.57.1): resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} engines: {node: ^10.12.0 || >=12.0.0} @@ -9069,7 +9590,6 @@ packages: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true - dev: true /esquery@1.6.0: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} @@ -9098,12 +9618,10 @@ packages: /estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} - dev: true /esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - dev: true /etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} @@ -9594,6 +10112,14 @@ packages: ethers: 4.0.49 dev: false + /ethers-types@3.18.1(ethers@5.7.2): + resolution: {integrity: sha512-VUHisDlJrg871DxneLe5PJt8Jyg5radEoriym7KDYiwf8ZeXZh8mIGWtQnRMO4exfj2Qu2nX0xjzgyoKHIwkQg==} + peerDependencies: + ethers: ^6.0.0 + dependencies: + ethers: 5.7.2 + dev: false + /ethers@4.0.49: resolution: {integrity: sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg==} dependencies: @@ -9739,7 +10265,11 @@ packages: p-finally: 1.0.0 signal-exit: 3.0.7 strip-eof: 1.0.0 - dev: true + + /exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + dev: false /expand-brackets@0.1.5: resolution: {integrity: sha512-hxx03P2dJxss6ceIeri9cmYOT4SRs3Zk3afZwWpOsRqLqprhTR8u++SlC+sFGsQr7WGFPdMF7Gjc1njDLDK6UA==} @@ -9910,7 +10440,15 @@ packages: /extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - dev: true + + /external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + dev: false /extglob@0.3.2: resolution: {integrity: sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg==} @@ -9938,7 +10476,6 @@ packages: /extsprintf@1.3.0: resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} engines: {'0': node >=0.6.0} - dev: true /eyes@0.1.8: resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} @@ -9960,11 +10497,9 @@ packages: /fast-deep-equal@1.1.0: resolution: {integrity: sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw==} - dev: true /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - dev: true /fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} @@ -9994,7 +10529,6 @@ packages: /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - dev: true /fast-levenshtein@1.0.7: resolution: {integrity: sha512-hYsfI0s4lfQ2rHVFKXwAr/L/ZSbq9TZwgXtZqW7ANcn9o9GKvcbWxOnxx7jykXf/Ezv1V8TvaBEKcGK7DWKX5A==} @@ -10004,6 +10538,10 @@ packages: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true + /fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + dev: false + /fast-stable-stringify@1.0.0: resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} @@ -10036,6 +10574,13 @@ packages: node-fetch: 1.7.3 dev: true + /figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + dependencies: + escape-string-regexp: 1.0.5 + dev: false + /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -10149,6 +10694,13 @@ packages: locate-path: 2.0.0 dev: true + /find-up@3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + dependencies: + locate-path: 3.0.0 + dev: false + /find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -10266,7 +10818,6 @@ packages: /forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} - dev: true /form-data-encoder@1.7.1: resolution: {integrity: sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg==} @@ -10279,7 +10830,6 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: true /form-data@2.5.2: resolution: {integrity: sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==} @@ -10289,7 +10839,6 @@ packages: combined-stream: 1.0.8 mime-types: 2.1.35 safe-buffer: 5.2.1 - dev: true /form-data@4.0.0: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} @@ -10345,7 +10894,6 @@ packages: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - dev: true /fs-extra@4.0.3: resolution: {integrity: sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==} @@ -10560,7 +11108,6 @@ packages: /get-caller-file@1.0.3: resolution: {integrity: sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==} - dev: true /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} @@ -10628,14 +11175,12 @@ packages: engines: {node: '>=6'} dependencies: pump: 3.0.2 - dev: true /get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} dependencies: pump: 3.0.2 - dev: true /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} @@ -10666,6 +11211,17 @@ packages: resolve-pkg-maps: 1.0.0 dev: true + /get-uri@6.0.4: + resolution: {integrity: sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==} + engines: {node: '>= 14'} + dependencies: + basic-ftp: 5.0.5 + data-uri-to-buffer: 6.0.2 + debug: 4.4.0(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: false + /get-value@2.0.6: resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} engines: {node: '>=0.10.0'} @@ -10675,7 +11231,6 @@ packages: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} dependencies: assert-plus: 1.0.0 - dev: true /ghost-testrpc@0.0.2: resolution: {integrity: sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==} @@ -10784,6 +11339,23 @@ packages: minimatch: 5.1.6 once: 1.4.0 + /glob@9.3.5: + resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + fs.realpath: 1.0.0 + minimatch: 8.0.4 + minipass: 4.2.8 + path-scurry: 1.11.1 + dev: false + + /global-dirs@2.1.0: + resolution: {integrity: sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==} + engines: {node: '>=8'} + dependencies: + ini: 1.3.7 + dev: false + /global-modules@2.0.0: resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} engines: {node: '>=6'} @@ -10922,12 +11494,15 @@ packages: p-cancelable: 1.1.0 to-readable-stream: 1.0.0 url-parse-lax: 3.0.0 - dev: true /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} requiresBuild: true + /grapheme-splitter@1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: false + /graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true @@ -10975,7 +11550,6 @@ packages: /har-schema@2.0.0: resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} engines: {node: '>=4'} - dev: true /har-validator@5.1.5: resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} @@ -10984,7 +11558,6 @@ packages: dependencies: ajv: 6.12.6 har-schema: 2.0.0 - dev: true /hardhat-contract-sizer@2.10.0(hardhat@2.22.18): resolution: {integrity: sha512-QiinUgBD5MqJZJh1hl1jc9dNnpJg7eE/w4/4GEnrcmZJJTDbVFNe3+/3Ep24XqISSkYxRz36czcPHKHd/a0dwA==} @@ -11286,6 +11859,11 @@ packages: kind-of: 4.0.0 dev: true + /has-yarn@2.1.0: + resolution: {integrity: sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==} + engines: {node: '>=8'} + dev: false + /has@1.0.4: resolution: {integrity: sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==} engines: {node: '>= 0.4.0'} @@ -11388,6 +11966,16 @@ packages: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true + /htmlparser2@3.8.3: + resolution: {integrity: sha512-hBxEg3CYXe+rPIua8ETe7tmG3XDn9B0edOE/e9wH2nLczxzgdu0m0aNHY+5wFZiviLWLdANPJTssa92dMcXQ5Q==} + dependencies: + domelementtype: 1.3.1 + domhandler: 2.3.0 + domutils: 1.5.1 + entities: 1.0.0 + readable-stream: 1.1.14 + dev: false + /htmlparser2@9.1.0: resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} dependencies: @@ -11464,6 +12052,16 @@ packages: dev: false optional: true + /http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.3 + debug: 4.4.0(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: false + /http-response-object@3.0.2: resolution: {integrity: sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==} dependencies: @@ -11477,13 +12075,16 @@ packages: assert-plus: 1.0.0 jsprim: 1.4.2 sshpk: 1.18.0 - dev: true /http-status-codes@2.3.0: resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} requiresBuild: true optional: true + /http2-client@1.3.5: + resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==} + dev: false + /http2-wrapper@1.0.3: resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} engines: {node: '>=10.19.0'} @@ -11510,6 +12111,16 @@ packages: transitivePeerDependencies: - supports-color + /https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.3 + debug: 4.4.0(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + dev: false + /humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} requiresBuild: true @@ -11551,6 +12162,10 @@ packages: /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + /ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + dev: true + /ignore@4.0.6: resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} engines: {node: '>= 4'} @@ -11590,6 +12205,11 @@ packages: resolve-from: 4.0.0 dev: true + /import-lazy@2.1.0: + resolution: {integrity: sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==} + engines: {node: '>=4'} + dev: false + /imul@1.0.1: resolution: {integrity: sha512-WFAgfwPLAjU66EKt6vRdTlKj4nAgIDQzh29JonLa4Bqtl6D8JrIMvWjCnx7xEjVNmP3U0fM5o8ZObk7d0f62bA==} engines: {node: '>=0.10.0'} @@ -11625,9 +12245,34 @@ packages: /inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + /ini@1.3.7: + resolution: {integrity: sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==} + dev: false + /ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + /inquirer@8.2.6: + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + engines: {node: '>=12.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 6.2.0 + dev: false + /internal-slot@1.0.7: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} @@ -11662,6 +12307,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /invert-kv@2.0.0: + resolution: {integrity: sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==} + engines: {node: '>=4'} + dev: false + /io-ts@1.10.4: resolution: {integrity: sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==} dependencies: @@ -11709,7 +12359,6 @@ packages: jsbn: 1.1.0 sprintf-js: 1.1.3 dev: false - optional: true /ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} @@ -11839,7 +12488,6 @@ packages: hasBin: true dependencies: ci-info: 2.0.0 - dev: true /is-core-module@2.14.0: resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} @@ -11970,12 +12618,10 @@ packages: engines: {node: '>=0.10.0'} dependencies: number-is-nan: 1.0.1 - dev: true /is-fullwidth-code-point@2.0.0: resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} engines: {node: '>=4'} - dev: true /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} @@ -12019,6 +12665,19 @@ packages: resolution: {integrity: sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==} engines: {node: '>=6.5.0', npm: '>=3'} + /is-installed-globally@0.3.2: + resolution: {integrity: sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==} + engines: {node: '>=8'} + dependencies: + global-dirs: 2.1.0 + is-path-inside: 3.0.3 + dev: false + + /is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: false + /is-lambda@1.0.1: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} requiresBuild: true @@ -12041,6 +12700,11 @@ packages: engines: {node: '>= 0.4'} dev: true + /is-npm@4.0.0: + resolution: {integrity: sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==} + engines: {node: '>=8'} + dev: false + /is-number-object@1.0.7: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} @@ -12079,10 +12743,14 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + /is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + dev: false + /is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} - dev: true /is-plain-obj@2.1.0: resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} @@ -12145,7 +12813,6 @@ packages: /is-stream@1.1.0: resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} engines: {node: '>=0.10.0'} - dev: true /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} @@ -12198,7 +12865,6 @@ packages: /is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - dev: true /is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} @@ -12260,6 +12926,10 @@ packages: is-docker: 2.2.1 dev: true + /is-yarn-global@0.3.0: + resolution: {integrity: sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==} + dev: false + /isarray@0.0.1: resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} requiresBuild: true @@ -12296,7 +12966,6 @@ packages: /isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} - dev: true /istanbul-combine@0.3.0: resolution: {integrity: sha512-zWtpO29Qs2ruIRu6qYtsX5guqw+/JFcZlTLrv1XUoBHe30r9v8pJgisf4VbMK3P2gahZjP6SlY1cz4qZ2YHaxw==} @@ -12356,6 +13025,11 @@ packages: wordwrap: 1.0.0 dev: true + /iterare@1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} + dev: false + /jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} dependencies: @@ -12474,13 +13148,11 @@ packages: /jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - dev: true /jsbn@1.1.0: resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} requiresBuild: true dev: false - optional: true /jscrypto@1.0.3: resolution: {integrity: sha512-lryZl0flhodv4SZHOqyb1bx5sKcJxj0VBo0Kzb4QMAg3L021IC9uGpl0RCZa+9KJwlRGSK2C80ITcwbe19OKLQ==} @@ -12498,6 +13170,19 @@ packages: requiresBuild: true dev: true + /jshint@2.13.6: + resolution: {integrity: sha512-IVdB4G0NTTeQZrBoM8C5JFVLjV2KtZ9APgybDA1MK73xb09qFs0jCXyQLnCOp1cSZZZbvhq/6mfXHUTaDkffuQ==} + hasBin: true + dependencies: + cli: 1.0.1 + console-browserify: 1.1.0 + exit: 0.1.2 + htmlparser2: 3.8.3 + lodash: 4.17.21 + minimatch: 3.0.4 + strip-json-comments: 1.0.4 + dev: false + /json-bigint@1.0.0: resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} dependencies: @@ -12505,7 +13190,6 @@ packages: /json-buffer@3.0.0: resolution: {integrity: sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==} - dev: true /json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -12543,11 +13227,9 @@ packages: /json-schema-traverse@0.3.1: resolution: {integrity: sha512-4JD/Ivzg7PoW8NzdrBSr3UFwC9mHgvI7Z6z3QGBsSHgKaRTUDmyZAAKJo2UbG1kUVfS9WS8bi36N49U1xw43DA==} - dev: true /json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - dev: true /json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -12555,7 +13237,6 @@ packages: /json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - dev: true /json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -12580,6 +13261,14 @@ packages: /json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + /json-to-ast@2.1.0: + resolution: {integrity: sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==} + engines: {node: '>= 4'} + dependencies: + code-error-fragment: 0.0.230 + grapheme-splitter: 1.0.4 + dev: false + /json5@0.5.1: resolution: {integrity: sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==} hasBin: true @@ -12626,6 +13315,11 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} + /jsonpointer@4.1.0: + resolution: {integrity: sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg==} + engines: {node: '>=0.10.0'} + dev: false + /jsonschema@1.4.1: resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==} dev: true @@ -12641,7 +13335,6 @@ packages: extsprintf: 1.3.0 json-schema: 0.4.0 verror: 1.10.0 - dev: true /keccak256@1.0.6: resolution: {integrity: sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw==} @@ -12671,7 +13364,6 @@ packages: resolution: {integrity: sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==} dependencies: json-buffer: 3.0.0 - dev: true /keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -12877,6 +13569,13 @@ packages: /kuler@2.0.0: resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + /latest-version@5.1.0: + resolution: {integrity: sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==} + engines: {node: '>=8'} + dependencies: + package-json: 6.5.0 + dev: false + /lcid@1.0.0: resolution: {integrity: sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==} engines: {node: '>=0.10.0'} @@ -12884,6 +13583,13 @@ packages: invert-kv: 1.0.0 dev: true + /lcid@2.0.0: + resolution: {integrity: sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==} + engines: {node: '>=6'} + dependencies: + invert-kv: 2.0.0 + dev: false + /level-codec@7.0.1: resolution: {integrity: sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ==} requiresBuild: true @@ -13036,6 +13742,11 @@ packages: xtend: 4.0.2 dev: true + /leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: false + /levn@0.2.5: resolution: {integrity: sha512-mvp+NO++YH0B+e8cC/SvJxk6k5Z9Ngd3iXuz7tmT8vZCyQZj/5SI1GkFOiZGGPkm5wWGI9SUrqiAfPq7BJH+0w==} engines: {node: '>= 0.8.0'} @@ -13112,6 +13823,14 @@ packages: path-exists: 3.0.0 dev: true + /locate-path@3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + dev: false + /locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -13238,12 +13957,10 @@ packages: /lowercase-keys@1.0.1: resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==} engines: {node: '>=0.10.0'} - dev: true /lowercase-keys@2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} - dev: true /lowercase-keys@3.0.0: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} @@ -13281,6 +13998,11 @@ packages: dev: false optional: true + /lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + dev: false + /lru-cache@9.1.2: resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==} engines: {node: 14 || >=16.14} @@ -13301,6 +14023,13 @@ packages: resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} engines: {node: '>=12'} + /make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + dependencies: + semver: 6.3.1 + dev: false + /make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -13331,6 +14060,13 @@ packages: dev: false optional: true + /map-age-cleaner@0.1.3: + resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==} + engines: {node: '>=6'} + dependencies: + p-defer: 1.0.0 + dev: false + /map-cache@0.2.2: resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} engines: {node: '>=0.10.0'} @@ -13411,6 +14147,15 @@ packages: mimic-fn: 1.2.0 dev: true + /mem@4.3.0: + resolution: {integrity: sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==} + engines: {node: '>=6'} + dependencies: + map-age-cleaner: 0.1.3 + mimic-fn: 2.1.0 + p-is-promise: 2.1.0 + dev: false + /memdown@1.4.1: resolution: {integrity: sha512-iVrGHZB8i4OQfM155xx8akvG9FIj+ht14DX5CQkCTG4EHzZ3d3sgckIf/Lm9ivZalEsFuEVnWv2B2WZvbrro2w==} requiresBuild: true @@ -13568,10 +14313,14 @@ packages: engines: {node: '>=4'} dev: true + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: false + /mimic-response@1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} - dev: true /mimic-response@2.1.0: resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} @@ -13617,7 +14366,6 @@ packages: resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} dependencies: brace-expansion: 1.1.11 - dev: true /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -13631,6 +14379,13 @@ packages: dependencies: brace-expansion: 2.0.1 + /minimatch@8.0.4: + resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: false + /minimatch@9.0.1: resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} engines: {node: '>=16 || 14 >=14.17'} @@ -13713,6 +14468,11 @@ packages: yallist: 4.0.0 dev: false + /minipass@4.2.8: + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} + dev: false + /minipass@5.0.0: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} @@ -13976,10 +14736,20 @@ packages: imul: 1.0.1 dev: true + /mustache@3.2.1: + resolution: {integrity: sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA==} + engines: {npm: '>=1.4.0'} + hasBin: true + dev: false + /mustache@4.2.0: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true + /mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + dev: false + /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -14064,6 +14834,11 @@ packages: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} dev: true + /netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + dev: false + /next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: true @@ -14082,7 +14857,6 @@ packages: /nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - dev: true /no-case@2.3.2: resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} @@ -14146,6 +14920,13 @@ packages: lodash: 4.17.21 dev: true + /node-fetch-h2@2.3.0: + resolution: {integrity: sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==} + engines: {node: 4.x || >=6.0.0} + dependencies: + http2-client: 1.3.5 + dev: false + /node-fetch@1.7.3: resolution: {integrity: sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==} requiresBuild: true @@ -14251,6 +15032,29 @@ packages: prebuild-install: 7.1.3 dev: true + /node-readfiles@0.2.0: + resolution: {integrity: sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==} + dependencies: + es6-promise: 3.3.1 + dev: false + + /nodemon@3.1.9: + resolution: {integrity: sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==} + engines: {node: '>=10'} + hasBin: true + dependencies: + chokidar: 3.6.0 + debug: 4.4.0(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 7.6.3 + simple-update-notifier: 2.0.0 + supports-color: 5.5.0 + touch: 3.1.1 + undefsafe: 2.0.5 + dev: true + /nofilter@1.0.4: resolution: {integrity: sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA==} engines: {node: '>=8'} @@ -14317,7 +15121,6 @@ packages: /normalize-url@4.5.1: resolution: {integrity: sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==} engines: {node: '>=8'} - dev: true /normalize-url@6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} @@ -14329,7 +15132,6 @@ packages: engines: {node: '>=4'} dependencies: path-key: 2.0.1 - dev: true /npmlog@4.1.2: resolution: {integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==} @@ -14370,7 +15172,6 @@ packages: /number-is-nan@1.0.1: resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} engines: {node: '>=0.10.0'} - dev: true /number-to-bn@1.7.0: resolution: {integrity: sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==} @@ -14379,14 +15180,72 @@ packages: bn.js: 4.11.6 strip-hex-prefix: 1.0.0 + /nunjucks@3.2.4: + resolution: {integrity: sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==} + engines: {node: '>= 6.9.0'} + hasBin: true + peerDependencies: + chokidar: ^3.3.0 + peerDependenciesMeta: + chokidar: + optional: true + dependencies: + a-sync-waterfall: 1.0.1 + asap: 2.0.6 + commander: 5.1.0 + dev: false + /o3@1.0.3: resolution: {integrity: sha512-f+4n+vC6s4ysy7YO7O2gslWZBUu8Qj2i2OUJOvjRxQva7jVjYjB29jrr9NCjmxZQR0gzrOcv1RnqoYOeMs5VRQ==} dependencies: capability: 0.2.5 + /oas-kit-common@1.0.8: + resolution: {integrity: sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==} + dependencies: + fast-safe-stringify: 2.1.1 + dev: false + + /oas-linter@3.2.2: + resolution: {integrity: sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==} + dependencies: + '@exodus/schemasafe': 1.3.0 + should: 13.2.3 + yaml: 1.10.2 + dev: false + + /oas-resolver@2.5.6: + resolution: {integrity: sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==} + hasBin: true + dependencies: + node-fetch-h2: 2.3.0 + oas-kit-common: 1.0.8 + reftools: 1.1.9 + yaml: 1.10.2 + yargs: 17.7.2 + dev: false + + /oas-schema-walker@1.1.5: + resolution: {integrity: sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==} + dev: false + + /oas-validator@3.4.0: + resolution: {integrity: sha512-l/SxykuACi2U51osSsBXTxdsFc8Fw41xI7AsZkzgVgWJAzoEFaaNptt35WgY9C3757RUclsm6ye5GvSyYoozLQ==} + dependencies: + ajv: 5.5.2 + better-ajv-errors: 0.6.7(ajv@5.5.2) + call-me-maybe: 1.0.2 + oas-kit-common: 1.0.8 + oas-linter: 3.2.2 + oas-resolver: 2.5.6 + oas-schema-walker: 1.1.5 + reftools: 1.1.9 + should: 13.2.3 + yaml: 1.10.2 + dev: false + /oauth-sign@0.9.0: resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} - dev: true /object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} @@ -14569,6 +15428,13 @@ packages: dependencies: fn.name: 1.1.0 + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: false + /only@0.0.2: resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==} @@ -14580,6 +15446,30 @@ packages: is-wsl: 2.2.0 dev: true + /openapi-generator@0.1.39(ajv@6.12.6): + resolution: {integrity: sha512-/i5245hUAXRyYmX1tWy+ftKtU5G8me2QNyjNS670GUSzTVDnhAWc+uYBZWccsvPH26mvEDnj1sTpfUefLiXPZQ==} + hasBin: true + dependencies: + '@types/nunjucks': 3.2.6 + '@types/request': 2.48.12 + colorful: 2.1.0 + commander: 2.20.3 + debug: 4.4.0(supports-color@8.1.1) + nunjucks: 3.2.4 + openapi3-ts: 1.4.0 + request: 2.88.2 + swagger2openapi: 5.4.0(ajv@6.12.6) + tslib: 1.14.1 + transitivePeerDependencies: + - ajv + - chokidar + - supports-color + dev: false + + /openapi3-ts@1.4.0: + resolution: {integrity: sha512-8DmE2oKayvSkIR3XSZ4+pRliBsx19bSNeIzkTPswY8r4wvjX86bMxsORdqwAwMxE8PefOcSAT2auvi/0TZe9yA==} + dev: false + /optimism@0.18.1: resolution: {integrity: sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==} requiresBuild: true @@ -14626,6 +15516,21 @@ packages: word-wrap: 1.2.5 dev: true + /ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: false + /os-homedir@1.0.2: resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} engines: {node: '>=0.10.0'} @@ -14648,6 +15553,15 @@ packages: mem: 1.1.0 dev: true + /os-locale@3.1.0: + resolution: {integrity: sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==} + engines: {node: '>=6'} + dependencies: + execa: 1.0.0 + lcid: 2.0.0 + mem: 4.3.0 + dev: false + /os-shim@0.1.3: resolution: {integrity: sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==} engines: {node: '>= 0.4.0'} @@ -14669,7 +15583,6 @@ packages: /p-cancelable@1.1.0: resolution: {integrity: sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==} engines: {node: '>=6'} - dev: true /p-cancelable@2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} @@ -14681,10 +15594,19 @@ packages: engines: {node: '>=12.20'} dev: true + /p-defer@1.0.0: + resolution: {integrity: sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==} + engines: {node: '>=4'} + dev: false + /p-finally@1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} engines: {node: '>=4'} - dev: true + + /p-is-promise@2.1.0: + resolution: {integrity: sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==} + engines: {node: '>=6'} + dev: false /p-limit@1.3.0: resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} @@ -14693,6 +15615,13 @@ packages: p-try: 1.0.0 dev: true + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: false + /p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -14706,6 +15635,13 @@ packages: p-limit: 1.3.0 dev: true + /p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + dependencies: + p-limit: 2.3.0 + dev: false + /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -14724,9 +15660,48 @@ packages: engines: {node: '>=4'} dev: true + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: false + + /pac-proxy-agent@7.1.0: + resolution: {integrity: sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==} + engines: {node: '>= 14'} + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.3 + debug: 4.4.0(supports-color@8.1.1) + get-uri: 6.0.4 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + pac-resolver: 7.0.1 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + dev: false + + /pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + dependencies: + degenerator: 5.0.1 + netmask: 2.0.2 + dev: false + /package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + /package-json@6.5.0: + resolution: {integrity: sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==} + engines: {node: '>=8'} + dependencies: + got: 9.6.0 + registry-auth-token: 4.2.2 + registry-url: 5.1.0 + semver: 6.3.1 + dev: false + /pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} dev: true @@ -14892,7 +15867,6 @@ packages: /path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} - dev: true /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -14906,7 +15880,6 @@ packages: /path-key@2.0.1: resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} engines: {node: '>=4'} - dev: true /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} @@ -14929,6 +15902,10 @@ packages: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} dev: false + /path-to-regexp@3.3.0: + resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} + dev: false + /path-to-regexp@6.2.2: resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} @@ -14968,11 +15945,9 @@ packages: /performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - dev: true /picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} - dev: true /picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -15138,7 +16113,6 @@ packages: /prepend-http@2.0.0: resolution: {integrity: sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==} engines: {node: '>=4'} - dev: true /preserve@0.2.0: resolution: {integrity: sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ==} @@ -15346,6 +16320,22 @@ packages: forwarded: 0.2.0 ipaddr.js: 1.9.1 + /proxy-agent@6.4.0: + resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.3 + debug: 4.4.0(supports-color@8.1.1) + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 7.18.3 + pac-proxy-agent: 7.1.0 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + dev: false + /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -15360,6 +16350,9 @@ packages: /psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + + /pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} dev: true /public-encrypt@4.0.3: @@ -15426,7 +16419,6 @@ packages: dependencies: end-of-stream: 1.4.4 once: 1.4.0 - dev: true /punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} @@ -15440,7 +16432,13 @@ packages: /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - dev: true + + /pupa@2.1.1: + resolution: {integrity: sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==} + engines: {node: '>=8'} + dependencies: + escape-goat: 2.1.1 + dev: false /pure-rand@5.0.5: resolution: {integrity: sha512-BwQpbqxSCBJVpamI6ydzcKqyFmnd5msMWUGvzXLm1aXvusbbgkbOto/EUPM00hjveJEaJtdbhUjKSzWRhQVkaw==} @@ -15485,7 +16483,6 @@ packages: /qs@6.5.3: resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} engines: {node: '>=0.6'} - dev: true /query-string@5.1.1: resolution: {integrity: sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==} @@ -15586,7 +16583,6 @@ packages: inherits: 2.0.4 isarray: 0.0.1 string_decoder: 0.10.31 - dev: true /readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -15665,6 +16661,10 @@ packages: dependencies: redis-errors: 1.2.0 + /reflect-metadata@0.1.13: + resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + dev: false + /reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -15679,6 +16679,10 @@ packages: which-builtin-type: 1.2.1 dev: true + /reftools@1.1.9: + resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==} + dev: false + /regenerate@1.4.2: resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} requiresBuild: true @@ -15758,6 +16762,20 @@ packages: regjsparser: 0.1.5 dev: true + /registry-auth-token@4.2.2: + resolution: {integrity: sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==} + engines: {node: '>=6.0.0'} + dependencies: + rc: 1.2.8 + dev: false + + /registry-url@5.1.0: + resolution: {integrity: sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==} + engines: {node: '>=8'} + dependencies: + rc: 1.2.8 + dev: false + /regjsgen@0.2.0: resolution: {integrity: sha512-x+Y3yA24uF68m5GA+tBjbGYo64xXVJpbToBaWCoSNSc1hdk6dfctaRWrNFTVJZIIhL5GxW8zwjoixbnifnK59g==} requiresBuild: true @@ -15869,7 +16887,6 @@ packages: tough-cookie: 2.5.0 tunnel-agent: 0.6.0 uuid: 3.4.0 - dev: true /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} @@ -15887,7 +16904,6 @@ packages: /require-main-filename@1.0.1: resolution: {integrity: sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==} - dev: true /resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} @@ -15959,7 +16975,6 @@ packages: resolution: {integrity: sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==} dependencies: lowercase-keys: 1.0.1 - dev: true /responselike@2.0.1: resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} @@ -15967,6 +16982,14 @@ packages: lowercase-keys: 2.0.0 dev: true + /restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: false + /ret@0.1.15: resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} engines: {node: '>=0.12'} @@ -16054,6 +17077,11 @@ packages: bufferutil: 4.0.9 utf-8-validate: 5.0.10 + /run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + dev: false + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: @@ -16071,7 +17099,6 @@ packages: requiresBuild: true dependencies: tslib: 1.14.1 - dev: true /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} @@ -16244,6 +17271,13 @@ packages: requiresBuild: true dev: true + /semver-diff@3.1.1: + resolution: {integrity: sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==} + engines: {node: '>=8'} + dependencies: + semver: 6.3.1 + dev: false + /semver@5.4.1: resolution: {integrity: sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==} hasBin: true @@ -16443,7 +17477,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: shebang-regex: 1.0.0 - dev: true /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -16454,12 +17487,16 @@ packages: /shebang-regex@1.0.0: resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} engines: {node: '>=0.10.0'} - dev: true /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + /shell-quote@1.8.2: + resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} + engines: {node: '>= 0.4'} + dev: true + /shelljs@0.8.5: resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} engines: {node: '>=4'} @@ -16475,6 +17512,44 @@ packages: nanoid: 3.3.8 dev: true + /should-equal@2.0.0: + resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==} + dependencies: + should-type: 1.4.0 + dev: false + + /should-format@3.0.3: + resolution: {integrity: sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==} + dependencies: + should-type: 1.4.0 + should-type-adaptors: 1.1.0 + dev: false + + /should-type-adaptors@1.1.0: + resolution: {integrity: sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==} + dependencies: + should-type: 1.4.0 + should-util: 1.0.1 + dev: false + + /should-type@1.4.0: + resolution: {integrity: sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==} + dev: false + + /should-util@1.0.1: + resolution: {integrity: sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==} + dev: false + + /should@13.2.3: + resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==} + dependencies: + should-equal: 2.0.0 + should-format: 3.0.3 + should-type: 1.4.0 + should-type-adaptors: 1.1.0 + should-util: 1.0.1 + dev: false + /shx@0.3.4: resolution: {integrity: sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==} engines: {node: '>=6'} @@ -16571,6 +17646,13 @@ packages: dependencies: is-arrayish: 0.3.2 + /simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + dependencies: + semver: 7.6.3 + dev: true + /slack@11.0.2: resolution: {integrity: sha512-rv842+S+AGyZCmMMd8xPtW5DvJ9LzWTAKfxi8Gw57oYlXgcKtFuHd4nqk6lTPpRKdUGn3tx/Drd0rjQR3dQPqw==} deprecated: this library is no longer maintained; if you are interested in taking this package on let us know @@ -16608,7 +17690,6 @@ packages: engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} requiresBuild: true dev: false - optional: true /snake-case@2.1.0: resolution: {integrity: sha512-FMR5YoPFwOLuh4rRz92dywJjyKYZNLpMn1R5ujVpIYkbA9p01fq8RMg0FkO4M+Yobt4MjHeLTJVm5xFFBHSV2Q==} @@ -16677,6 +17758,17 @@ packages: dev: false optional: true + /socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.3 + debug: 4.4.0(supports-color@8.1.1) + socks: 2.8.3 + transitivePeerDependencies: + - supports-color + dev: false + /socks@2.8.3: resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} @@ -16685,7 +17777,6 @@ packages: ip-address: 9.0.5 smart-buffer: 4.2.0 dev: false - optional: true /sol-digger@0.0.2: resolution: {integrity: sha512-oqrw1E/X2WWYUYCzKDM5INDDH2nWOWos4p2Cw2OF52qoZcTDzlKMJQ5pJFXKOCADCg6KggBO5WYE/vNb+kJ0Hg==} @@ -17000,6 +18091,10 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + /spawn-command@0.0.2: + resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + dev: false + /spawn-sync@1.0.15: resolution: {integrity: sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==} requiresBuild: true @@ -17045,7 +18140,6 @@ packages: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} requiresBuild: true dev: false - optional: true /sqlite3@5.1.7(bluebird@3.7.2): resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==} @@ -17083,7 +18177,6 @@ packages: jsbn: 0.1.1 safer-buffer: 2.1.2 tweetnacl: 0.14.5 - dev: true /ssri@8.0.1: resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} @@ -17155,7 +18248,6 @@ packages: code-point-at: 1.1.0 is-fullwidth-code-point: 1.0.0 strip-ansi: 3.0.1 - dev: true /string-width@2.1.1: resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==} @@ -17163,7 +18255,6 @@ packages: dependencies: is-fullwidth-code-point: 2.0.0 strip-ansi: 4.0.0 - dev: true /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} @@ -17251,14 +18342,12 @@ packages: engines: {node: '>=0.10.0'} dependencies: ansi-regex: 2.1.1 - dev: true /strip-ansi@4.0.0: resolution: {integrity: sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==} engines: {node: '>=4'} dependencies: ansi-regex: 3.0.1 - dev: true /strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} @@ -17287,7 +18376,6 @@ packages: /strip-eof@1.0.0: resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} engines: {node: '>=0.10.0'} - dev: true /strip-hex-prefix@1.0.0: resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==} @@ -17300,6 +18388,12 @@ packages: engines: {node: '>=4'} dev: true + /strip-json-comments@1.0.4: + resolution: {integrity: sha512-AOPG8EBc5wAikaG1/7uFCNFJwnKOuQwFTpYBdTW6OvWHeZBQBrAA/amefHGrEiOnCPcLFZK6FUPtWVKpQVIRgg==} + engines: {node: '>=0.8.0'} + hasBin: true + dev: false + /strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -17376,6 +18470,37 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + /swagger-typescript-codegen@3.2.4: + resolution: {integrity: sha512-IEBRPd/Su5AggaHXtGsxJ3gyJU7k4QDb3UN5HziRMNK7ykZnJcwpvh55HOZpH5RBL2XW1DHJuNZs7P+2PUaUHw==} + hasBin: true + dependencies: + commander: 2.20.3 + js-beautify: 1.15.1 + jshint: 2.13.6 + lodash: 4.17.21 + mustache: 3.2.1 + update-notifier: 4.1.3 + dev: false + + /swagger2openapi@5.4.0(ajv@6.12.6): + resolution: {integrity: sha512-f5QqfXawiVijhjMtYqWZ55ESHPZFqrPC8L9idhIiuSX8O2qsa1i4MVGtCM3TQF+Smzr/6WfT/7zBuzG3aTgPAA==} + hasBin: true + dependencies: + better-ajv-errors: 0.6.7(ajv@6.12.6) + call-me-maybe: 1.0.2 + node-fetch-h2: 2.3.0 + node-readfiles: 0.2.0 + oas-kit-common: 1.0.8 + oas-resolver: 2.5.6 + oas-schema-walker: 1.1.5 + oas-validator: 3.4.0 + reftools: 1.1.9 + yaml: 1.10.2 + yargs: 12.0.5 + transitivePeerDependencies: + - ajv + dev: false + /swap-case@1.1.2: resolution: {integrity: sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==} dependencies: @@ -17525,6 +18650,11 @@ packages: dependencies: bintrees: 1.0.2 + /term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + dev: false + /test-value@2.1.0: resolution: {integrity: sha512-+1epbAxtKeXttkGFMTX9H42oqzOTufR1ceCF+GYA5aOmvaPq9wd4PUS8329fn2RRLGNeUkgRLnVpycjx8DsO2w==} engines: {node: '>=0.10.0'} @@ -17653,7 +18783,6 @@ packages: /to-readable-stream@1.0.0: resolution: {integrity: sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==} engines: {node: '>=6'} - dev: true /to-regex-range@2.1.1: resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==} @@ -17686,6 +18815,11 @@ packages: /toml@3.0.0: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + /touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + dev: true + /tough-cookie@0.12.1: resolution: {integrity: sha512-+gd4PklNJsxzu1NoNjhGRfOZZ5llND6VtQZGuaDXdmI0Ii79V5+YCa2sLx8Q6lYhYN2+9frCzUwOLQpuwHvO4Q==} engines: {node: '>=0.4.12'} @@ -17700,7 +18834,6 @@ packages: dependencies: psl: 1.9.0 punycode: 2.3.1 - dev: true /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -17709,6 +18842,10 @@ packages: resolution: {integrity: sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg==} dev: true + /tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + /trim-right@1.0.1: resolution: {integrity: sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==} engines: {node: '>=0.10.0'} @@ -17827,6 +18964,22 @@ packages: yn: 3.1.1 dev: true + /ts-node@9.1.1(typescript@5.7.3): + resolution: {integrity: sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==} + engines: {node: '>=10.0.0'} + hasBin: true + peerDependencies: + typescript: '>=2.7' + dependencies: + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + source-map-support: 0.5.21 + typescript: 5.7.3 + yn: 3.1.1 + dev: true + /ts-poet@6.9.0: resolution: {integrity: sha512-roe6W6MeZmCjRmppyfOURklO5tQFQ6Sg7swURKkwYJvV7dbGCrK28um5+51iW3twdPRKtwarqFAVMU6G1mvnuQ==} dependencies: @@ -17907,7 +19060,6 @@ packages: /tweetnacl@0.14.5: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} - dev: true /tweetnacl@1.0.3: resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} @@ -17948,6 +19100,11 @@ packages: resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} engines: {node: '>=8'} + /type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + dev: false + /type-fest@3.13.1: resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} engines: {node: '>=14.16'} @@ -18074,7 +19231,6 @@ packages: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} dependencies: is-typedarray: 1.0.0 - dev: true /typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} @@ -18144,6 +19300,13 @@ packages: dev: true optional: true + /uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + dependencies: + '@lukeed/csprng': 1.1.0 + dev: false + /ultron@1.1.1: resolution: {integrity: sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==} dev: true @@ -18167,6 +19330,10 @@ packages: which-boxed-primitive: 1.1.1 dev: true + /undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + dev: true + /underscore@1.13.7: resolution: {integrity: sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==} dev: true @@ -18183,6 +19350,9 @@ packages: /undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + /undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + /undici@5.28.4: resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} engines: {node: '>=14.0'} @@ -18227,6 +19397,13 @@ packages: dev: false optional: true + /unique-string@2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + dependencies: + crypto-random-string: 2.0.0 + dev: false + /universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -18258,6 +19435,25 @@ packages: requiresBuild: true optional: true + /update-notifier@4.1.3: + resolution: {integrity: sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==} + engines: {node: '>=8'} + dependencies: + boxen: 4.2.0 + chalk: 3.0.0 + configstore: 5.0.1 + has-yarn: 2.1.0 + import-lazy: 2.1.0 + is-ci: 2.0.0 + is-installed-globally: 0.3.2 + is-npm: 4.0.0 + is-yarn-global: 0.3.0 + latest-version: 5.1.0 + pupa: 2.1.1 + semver-diff: 3.1.1 + xdg-basedir: 4.0.0 + dev: false + /upper-case-first@1.1.2: resolution: {integrity: sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==} dependencies: @@ -18272,7 +19468,6 @@ packages: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.3.1 - dev: true /urix@0.1.0: resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} @@ -18288,7 +19483,6 @@ packages: engines: {node: '>=4'} dependencies: prepend-http: 2.0.0 - dev: true /url-set-query@1.0.0: resolution: {integrity: sha512-3AChu4NiXquPfeckE5R5cGdiHCMWJx1dwCWOmWIL4KHAziJNOFIYJlpGFeKDvwLPHovZRCxK3cYlwzqI9Vp+Gg==} @@ -18384,7 +19578,6 @@ packages: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. hasBin: true - dev: true /uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} @@ -18423,11 +19616,16 @@ packages: assert-plus: 1.0.0 core-util-is: 1.0.2 extsprintf: 1.3.0 - dev: true /vlq@2.0.4: resolution: {integrity: sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA==} + /wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + dependencies: + defaults: 1.0.4 + dev: false + /web3-bzz@1.10.0: resolution: {integrity: sha512-o9IR59io3pDUsXTsps5pO5hW1D5zBmg46iNc2t4j2DkaYHNdDLwk2IP9ukoM2wg47QILfPEJYzhTfkS/CcX0KA==} engines: {node: '>=8.0.0'} @@ -19718,7 +20916,6 @@ packages: /which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - dev: true /which-pm-runs@1.1.0: resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} @@ -19762,7 +20959,6 @@ packages: hasBin: true dependencies: isexe: 2.0.0 - dev: true /which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} @@ -19869,7 +21065,15 @@ packages: dependencies: string-width: 1.0.2 strip-ansi: 3.0.1 - dev: true + + /wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: false /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} @@ -19890,6 +21094,15 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + /write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + dependencies: + imurmurhash: 0.1.4 + is-typedarray: 1.0.0 + signal-exit: 3.0.7 + typedarray-to-buffer: 3.1.5 + dev: false + /ws@3.3.3: resolution: {integrity: sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==} peerDependencies: @@ -19978,6 +21191,11 @@ packages: bufferutil: 4.0.9 utf-8-validate: 5.0.10 + /xdg-basedir@4.0.0: + resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} + engines: {node: '>=8'} + dev: false + /xhr-request-promise@0.1.3: resolution: {integrity: sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==} dependencies: @@ -20040,7 +21258,6 @@ packages: /y18n@3.2.2: resolution: {integrity: sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==} - dev: true /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} @@ -20066,7 +21283,13 @@ packages: /yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} - dev: true + + /yargs-parser@11.1.1: + resolution: {integrity: sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: false /yargs-parser@2.4.1: resolution: {integrity: sha512-9pIKIJhnI5tonzG6OnCFlz/yln8xHYcGl+pn3xR0Vzff0vzN1PbNRaelgfgRUwZ3s4i3jvxT9WhmUGL4whnasA==} @@ -20115,6 +21338,23 @@ packages: yargs-parser: 8.1.0 dev: true + /yargs@12.0.5: + resolution: {integrity: sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==} + dependencies: + cliui: 4.1.0 + decamelize: 1.2.0 + find-up: 3.0.0 + get-caller-file: 1.0.3 + os-locale: 3.1.0 + require-directory: 2.1.1 + require-main-filename: 1.0.1 + set-blocking: 2.0.0 + string-width: 2.1.1 + which-module: 2.0.1 + y18n: 3.2.2 + yargs-parser: 11.1.1 + dev: false + /yargs@16.2.0: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} engines: {node: '>=10'} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 81effa6c94..b5396b3b2a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,4 @@ packages: - 'packages/*' - - 'lib/safe-contracts/' \ No newline at end of file + - 'lib/safe-contracts/' + - 'helpers/wormholescanMock/' \ No newline at end of file diff --git a/scripts/setup-bridging-contracts.js b/scripts/setup-bridging-contracts.js index 4df9626931..804e025688 100644 --- a/scripts/setup-bridging-contracts.js +++ b/scripts/setup-bridging-contracts.js @@ -206,8 +206,19 @@ async function setupBridging(homeRpcUrl, foreignRpcUrls) { stdio: "inherit", }); + const wormholeScanMockProcess = spawn("pnpm", ["exec", "tsx", "./src/index.ts"], { + cwd: path.resolve(__dirname, "..", "helpers", "wormholescanMock"), + stdio: "inherit", + env: { + ...process.env, + PORT: "3001", + PROVIDER_URLS: `${homeRpcUrl},${foreignRpcUrls.join(",")}`, + }, + }); + process.on("exit", () => { relayerProcess.kill(); + wormholeScanMockProcess.kill(); }); // Wait until the bridge monitor has connected to the spy diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index d2ba85fa6e..f2534afc5a 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -32,6 +32,7 @@ const ProxyColony = artifacts.require("ProxyColony"); const MetaTxToken = artifacts.require("MetaTxToken"); const OneTxPayment = artifacts.require("OneTxPayment"); const LiFiFacetProxyMock = artifacts.require("LiFiFacetProxyMock"); +const IWormhole = artifacts.require("IWormhole"); // const { assert } = require("console"); const { setupBridging, setForeignBridgeData, setHomeBridgeData } = require("../../scripts/setup-bridging-contracts"); @@ -1349,4 +1350,41 @@ contract("Cross-chain", (accounts) => { expect(balanceAfter.sub(balanceBefore).toHexString()).to.equal(paymentAmount.toHexString()); }); }); + + describe("Wormholescan API mock should work as expected", async () => { + it("the operations endpoint should return the right joined-up transactions ", async () => { + const colony = await setupColony(homeColonyNetwork); + + // const homeWormholeAddress = await homeBridge.wormhole(); + // const foreignWormholeAddress = await remoteColonyNetwork.wormhole(); + + const events = await homeColonyNetwork.queryFilter(homeColonyNetwork.filters.ColonyAdded()); + // Deploy a proxy colony on the foreign network + + const colonyCreationSalt = await homeColonyNetwork.getColonyCreationSalt({ blockTag: events[events.length - 1].blockNumber }); + + const p = guardianSpy.getPromiseForNextBridgedTransaction(); + + const tx = await colony.createProxyColony(foreignChainId, colonyCreationSalt, { gasLimit: 1000000 }); + const requestReceipt = await tx.wait(); + + const homeWormhole = new ethers.Contract(homeBridge, IWormhole.abi, ethersHomeSigner); + const wormholeEvent = homeWormhole.interface.parseLog(requestReceipt.logs.filter((l) => l.address === homeBridge.address)[0]); + const senderWormholeAddress = ethers.utils.hexZeroPad(wormholeEvent.args.sender, 32); + const executionReceipt = await p; + let apiResponse; + + try { + apiResponse = await fetch( + `http://localhost:3001/api/v1/operations/${wormholeHomeChainId}/${senderWormholeAddress}/${wormholeEvent.args.sequence.toString()}`, + ); + } catch (err) { + console.log(err); + } + const res = await apiResponse.json(); + + expect(res.sourceChain.transaction.txHash).to.equal(requestReceipt.transactionHash); + expect(res.targetChain.transaction.txHash).to.equal(executionReceipt.transactionHash); + }); + }); }); From 6f8b34a2b72ded15a42642e3efd3590f01527bf7 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 16 Dec 2024 17:17:22 +0000 Subject: [PATCH 53/72] Use reworked domain receiving logic --- contracts/bridging/ProxyColony.sol | 11 ++++++++++- test/contracts-network/colony-funding.js | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index 95d5c1fc97..5ee7eb204d 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -80,7 +80,16 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards, BasicMetaTransaction ? address(domainTokenReceiverAddress).balance : ERC20Extended(_token).balanceOf(address(domainTokenReceiverAddress)); - DomainTokenReceiver(domainTokenReceiverAddress).transferToColony(_token); + if (_token == address(0x0)) { + DomainTokenReceiver(domainTokenReceiverAddress).transferNativeToColony(); + } else { + DomainTokenReceiver(domainTokenReceiverAddress).approveTokenToColony(_token); + // slither-disable-next-line arbitrary-send-erc20 + require( + ERC20Extended(_token).transferFrom(domainTokenReceiverAddress, address(this), balance), + "colony-funding-transfer-failed" + ); + } bytes memory payload = abi.encodeWithSignature( "recordClaimedFundsFromBridge(uint256,address,uint256,uint256)", diff --git a/test/contracts-network/colony-funding.js b/test/contracts-network/colony-funding.js index 354b7ae6fd..9931c6efa3 100755 --- a/test/contracts-network/colony-funding.js +++ b/test/contracts-network/colony-funding.js @@ -896,7 +896,7 @@ contract("Colony Funding", (accounts) => { await toggleableToken.toggleLock(); // Try to claim the funds - await checkErrorRevert(colony.claimDomainFunds(toggleableToken.address, 2), "domain-token-receiver-transfer-failed"); + await checkErrorRevert(colony.claimDomainFunds(toggleableToken.address, 2), "colony-funding-transfer-failed"); }); it("If the receiver resolver is updated, then the resolver is updated at the next claim", async () => { From bf75b32fc95588d770d5c7d7806352ec5b1e4921 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Tue, 7 Jan 2025 09:34:49 +0000 Subject: [PATCH 54/72] Have colony emit ProxyColonyRequested as well --- contracts/colony/Colony.sol | 2 ++ contracts/colony/ColonyDataTypes.sol | 6 ++++++ contracts/colonyNetwork/ColonyNetworkDataTypes.sol | 3 ++- contracts/colonyNetwork/ColonyNetworkDeployer.sol | 2 +- test/cross-chain/cross-chain.js | 8 ++++++++ 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index b63685fe33..806b12ecdd 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -354,6 +354,8 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP function createProxyColony(uint256 _destinationChainId, bytes32 _salt) public stoppable { IColonyNetwork(colonyNetworkAddress).createProxyColony(_destinationChainId, _salt); + + emit ProxyColonyRequested(_destinationChainId, _salt); } function getMetatransactionNonce(address _user) public view override returns (uint256 nonce) { diff --git a/contracts/colony/ColonyDataTypes.sol b/contracts/colony/ColonyDataTypes.sol index 43551f77b3..c24a833be9 100755 --- a/contracts/colony/ColonyDataTypes.sol +++ b/contracts/colony/ColonyDataTypes.sol @@ -271,6 +271,12 @@ interface ColonyDataTypes { /// @param tokenPayout Amount of the payout claimed, after network fee was deducted event PayoutClaimed(address agent, uint256 id, uint256 slot, address token, uint256 tokenPayout); + /// @notice Event logged when a colony requests a proxy colony deployment + /// @param destinationChainId The chain id of the destination chain + /// @param salt The salt used to generate the proxy address + /// @dev The address corresponding to the salt must be this colony's address + event ProxyColonyRequested(uint256 destinationChainId, bytes32 salt); + // Structs struct RewardPayoutCycle { diff --git a/contracts/colonyNetwork/ColonyNetworkDataTypes.sol b/contracts/colonyNetwork/ColonyNetworkDataTypes.sol index 65a4b95c93..f9d3ae7600 100755 --- a/contracts/colonyNetwork/ColonyNetworkDataTypes.sol +++ b/contracts/colonyNetwork/ColonyNetworkDataTypes.sol @@ -162,9 +162,10 @@ interface ColonyNetworkDataTypes { event ReputationUpdateSentToBridge(address colony, uint256 count); /// @notice Event emitted when a proxy colony deployment is requested + /// @param colony The address of the colony that has requested the proxy deployment /// @param destinationChainId The chain ID of the destination chain /// @param salt The salt used to generate the proxy address - event ProxyColonyRequested(uint256 destinationChainId, bytes32 salt); + event ProxyColonyRequested(address colony, uint256 destinationChainId, bytes32 salt); struct Skill { diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index ee50dd4366..a74c705a61 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -146,7 +146,7 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage, DomainReceiverManagement IColonyBridge(colonyBridgeAddress).sendMessage(_destinationChainId, address(this), payload), "colony-network-create-proxy-colony-failed" ); - emit ProxyColonyRequested(_destinationChainId, _salt); + emit ProxyColonyRequested(msgSender(), _destinationChainId, _salt); } /** diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index f2534afc5a..992951ad6e 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -348,6 +348,14 @@ contract("Cross-chain", (accounts) => { expect(parsed.name).to.equal("ProxyColonyRequested"); expect(parsed.args.destinationChainId.toHexString()).to.equal(foreignChainId); expect(parsed.args.salt).to.equal(colonyCreationSalt); + expect(parsed.args.colony).to.equal(colonyAddress); + + const colonyProxyRequestEvent = receipt.events.filter((e) => e.address === colonyAddress)[0]; + parsed = homeColony.interface.parseLog(colonyProxyRequestEvent); + expect(parsed.name).to.equal("ProxyColonyRequested"); + expect(parsed.args.destinationChainId.toHexString()).to.equal(foreignChainId); + expect(parsed.args.salt).to.equal(colonyCreationSalt); + receipt = await p; const proxyDeployedEvent = receipt.logs.filter((e) => e.address === remoteColonyNetwork.address)[0]; From b5f3ad359d6fc50bfb18fb8ee1d2bd6eb2b47208 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Thu, 9 Jan 2025 11:34:59 +0000 Subject: [PATCH 55/72] Add VAA to operations endpoint for self-bridging scenarios --- .circleci/config.yml | 2 +- .gitignore | 1 + helpers/wormholescanMock/src/encodeMockVAA.ts | 36 ++++++++++++++++++ helpers/wormholescanMock/src/index.ts | 18 +++++++++ scripts/mockGuardianSpy.ts | 37 ++----------------- test/cross-chain/cross-chain.js | 12 +++++- 6 files changed, 70 insertions(+), 36 deletions(-) create mode 100644 helpers/wormholescanMock/src/encodeMockVAA.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index d5408cec7b..e1362db265 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -87,10 +87,10 @@ jobs: - run: name: "Checking contract storage layout hasn't had additions made" command: rm -rf .storage-layouts-normalized && npx hardhat storage-layout --update && npm run normalize:storageSlots && test -z "$(git status --porcelain)" - - <<: *step_compile_mock_spy - run: name: "Linting JavaScript" command: pnpm run eslint + - <<: *step_compile_mock_spy - run: name: "Linting Solidity" command: pnpm run solhint diff --git a/.gitignore b/.gitignore index 8c1a8e6670..f27d1e47e6 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ coverage-extensions/ .pnpm-store .storage-layouts scripts/mockGuardianSpy.js +helpers/wormholescanMock/src/encodeMockVAA.js \ No newline at end of file diff --git a/helpers/wormholescanMock/src/encodeMockVAA.ts b/helpers/wormholescanMock/src/encodeMockVAA.ts new file mode 100644 index 0000000000..0316a3c16c --- /dev/null +++ b/helpers/wormholescanMock/src/encodeMockVAA.ts @@ -0,0 +1,36 @@ +import { ethers, Contract } from "ethers"; + +function ethereumAddressToWormholeAddress(address: string) { + return ethers.utils.hexZeroPad(ethers.utils.hexStripZeros(ethers.utils.hexlify(address)), 32); +} + +export default async function encodeMockVAA( + sender: string, + sequence: number, + nonce: number, + payload: string, + consistencyLevel: number, + chainId: number, + homeBridgeAddress: string, + homeBridgeProvider: ethers.providers.Provider, +) { + const homeBridge = new Contract( + homeBridgeAddress, + ["function buildVAABody(uint32,uint32,uint16,bytes32,uint64,uint8,bytes) view returns(bytes)"], + homeBridgeProvider, + ); + const timestamp = Math.floor(Date.now() / 1000); + const emitterChainId = chainId; + const emitterAddress = ethereumAddressToWormholeAddress(sender); + + const vaaBody = await homeBridge.buildVAABody(timestamp, nonce, emitterChainId, emitterAddress, sequence, consistencyLevel, payload); + + const vaaHeader = + "0x01" + // version + "00000000" + // guardianSetIndex + "01" + // signature count + "01" + // signature index + "7777000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007777"; + + return vaaHeader + vaaBody.toString("hex").slice(2); +} diff --git a/helpers/wormholescanMock/src/index.ts b/helpers/wormholescanMock/src/index.ts index 0951a23249..36c38423da 100644 --- a/helpers/wormholescanMock/src/index.ts +++ b/helpers/wormholescanMock/src/index.ts @@ -2,6 +2,7 @@ import express, { Express, Request, Response } from "express"; import { LogDescription } from "ethers/lib/utils"; import { ethers } from "ethers"; import { OperationsOperationResponse, VaaChainID } from "../openapi-generated-types/models"; +import encodeMockVAA from "./encodeMockVAA"; const app: Express = express(); const port = process.env.PORT || 3000; @@ -140,9 +141,11 @@ app.get("/api/v1/operations/:chain/:emitter/:sequence", async (req: Request, res } let requestingEventAndLog; + let relevantProvider; for (const provider of providers) { requestingEventAndLog = await getLogMessagePublishedForVAA(provider, parseInt(req.params.chain, 10), emitterAddress, req.params.sequence); if (requestingEventAndLog) { + relevantProvider = provider; break; } } @@ -174,6 +177,21 @@ app.get("/api/v1/operations/:chain/:emitter/:sequence", async (req: Request, res }, }; + const signed = true; + if (signed) { + const rawVAA = await encodeMockVAA( + emitterAddress, + parseInt(req.params.sequence, 10), + 0, + requestingEventAndLog.event.args.payload, + 0, + parseInt(req.params.chain, 10), + requestingEventAndLog.log.address, + relevantProvider, + ); + body.vaa = { raw: rawVAA }; + } + if (receivingEventAndLog) { body.targetChain = { chainId: receivingEventAndLog.chainId, diff --git a/scripts/mockGuardianSpy.ts b/scripts/mockGuardianSpy.ts index 819e256e33..acf883a840 100644 --- a/scripts/mockGuardianSpy.ts +++ b/scripts/mockGuardianSpy.ts @@ -18,6 +18,8 @@ import { abi as bridgeAbi } from "../artifacts/contracts/testHelpers/WormholeMoc // eslint-disable-next-line import/no-unresolved import { abi as wormholeBridgeForColonyAbi } from "../artifacts/contracts/bridging/WormholeBridgeForColony.sol/WormholeBridgeForColony.json"; +import encodeMockVAA from "../helpers/wormholescanMock/src/encodeMockVAA"; + function ethereumAddressToWormholeAddress(address: string) { return ethers.utils.hexZeroPad(ethers.utils.hexStripZeros(ethers.utils.hexlify(address)), 32); } @@ -141,40 +143,7 @@ class MockGuardianSpy { // Note that the documentation sometimes also calls them VMs (as does IWormhole) // I believe VM stands for 'Verified Message' async encodeMockVAA(sender: string, sequence: number, nonce: number, payload: string, consistencyLevel: number, chainId: number) { - const timestamp = Math.floor(Date.now() / 1000); - const emitterChainId = chainId; - const emitterAddress = ethereumAddressToWormholeAddress(sender); - // let signatures: any[] = []; - - // const vaa = await this.homeBridge.buildVM( - // version, - // timestamp, - // nonce, - // emitterChainId, - // emitterAddress, - // sequence.toString(), - // consistencyLevel, - // payload, - // guardianSetIndex, - // signatures, - // hash, - // ); - - // Build the VAA body - const vaaBody = await this.homeBridge.buildVAABody(timestamp, nonce, emitterChainId, emitterAddress, sequence, consistencyLevel, payload); - - // const signatures = guardians.addSignatures(vaaBody, [0]); - // Build the VAA header - - const vaaHeader = - "0x01" + // version - "00000000" + // guardianSetIndex - "01" + // signature count - "01" + // signature index - "7777000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007777"; - - return vaaHeader + vaaBody.toString("hex").slice(2); - // return signatures.toString('hex').slice(2); + return encodeMockVAA(sender, sequence, nonce, payload, consistencyLevel, chainId, this.homeBridge.address, this.homeBridge.provider); } setupForeignBridges(foreignRpc, foreignBridgeAddress, foreignColonyBridgeAddress) { diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 992951ad6e..84f2c443a9 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -1360,7 +1360,7 @@ contract("Cross-chain", (accounts) => { }); describe("Wormholescan API mock should work as expected", async () => { - it("the operations endpoint should return the right joined-up transactions ", async () => { + it("the operations endpoint should return the values ", async () => { const colony = await setupColony(homeColonyNetwork); // const homeWormholeAddress = await homeBridge.wormhole(); @@ -1393,6 +1393,16 @@ contract("Cross-chain", (accounts) => { expect(res.sourceChain.transaction.txHash).to.equal(requestReceipt.transactionHash); expect(res.targetChain.transaction.txHash).to.equal(executionReceipt.transactionHash); + const expectedVaa = await guardianSpy.encodeMockVAA( + homeColonyBridge.address, + wormholeEvent.args.sequence, + 0, + wormholeEvent.args.payload, + 0, + wormholeHomeChainId, + ); + // Check VAA + expect(res.vaa.raw).to.equal(expectedVaa); }); }); }); From fc5f28e011187a18f4549a6d6bb73279abfb03fe Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 13 Jan 2025 11:29:00 +0000 Subject: [PATCH 56/72] Fix function name changed after rebase --- contracts/bridging/ProxyColony.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index 5ee7eb204d..82ce5da086 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -81,7 +81,7 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards, BasicMetaTransaction : ERC20Extended(_token).balanceOf(address(domainTokenReceiverAddress)); if (_token == address(0x0)) { - DomainTokenReceiver(domainTokenReceiverAddress).transferNativeToColony(); + DomainTokenReceiver(domainTokenReceiverAddress).transferChainNativeToColony(); } else { DomainTokenReceiver(domainTokenReceiverAddress).approveTokenToColony(_token); // slither-disable-next-line arbitrary-send-erc20 From 83221c045723c29b7b6d18eb33f5049615483fbc Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 13 Jan 2025 11:52:38 +0000 Subject: [PATCH 57/72] Add agent to ProxyColonyRequested --- .../ColonyNetwork.sol:ColonyNetwork.json | 4 +- ...tworkAuction.sol:ColonyNetworkAuction.json | 4 +- ...orkDeployer.sol:ColonyNetworkDeployer.json | 4 +- ...ColonyNetworkENS.sol:ColonyNetworkENS.json | 4 +- ...xtensions.sol:ColonyNetworkExtensions.json | 4 +- ...NetworkMining.sol:ColonyNetworkMining.json | 4 +- ...NetworkSkills.sol:ColonyNetworkSkills.json | 4 +- ...tworkStorage.sol:ColonyNetworkStorage.json | 4 +- .../CommonDataTypes.sol:CommonDataTypes.json | 3 ++ contracts/colony/Colony.sol | 2 +- contracts/colony/ColonyDataTypes.sol | 7 +++- contracts/colony/ColonyStorage.sol | 3 +- .../colonyNetwork/ColonyNetworkDataTypes.sol | 20 ++-------- contracts/common/CommonDataTypes.sol | 37 +++++++++++++++++++ test/cross-chain/cross-chain.js | 1 + 15 files changed, 67 insertions(+), 38 deletions(-) create mode 100644 .storage-layouts-normalized/contracts/common/CommonDataTypes.sol:CommonDataTypes.json create mode 100644 contracts/common/CommonDataTypes.sol diff --git a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetwork.sol:ColonyNetwork.json b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetwork.sol:ColonyNetwork.json index efa08ea81b..b23f673dc1 100644 --- a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetwork.sol:ColonyNetwork.json +++ b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetwork.sol:ColonyNetwork.json @@ -305,11 +305,11 @@ "label": "uint256", "numberOfBytes": "32" }, - "label": "mapping(uint256 => struct ColonyNetworkDataTypes.Skill)", + "label": "mapping(uint256 => struct CommonDataTypes.Skill)", "numberOfBytes": "32", "value": { "encoding": "inplace", - "label": "struct ColonyNetworkDataTypes.Skill", + "label": "struct CommonDataTypes.Skill", "members": [ { "contract": "contracts/colonyNetwork/ColonyNetwork.sol:ColonyNetwork", diff --git a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkAuction.sol:ColonyNetworkAuction.json b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkAuction.sol:ColonyNetworkAuction.json index f09ac659e5..e2a0f8aded 100644 --- a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkAuction.sol:ColonyNetworkAuction.json +++ b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkAuction.sol:ColonyNetworkAuction.json @@ -305,11 +305,11 @@ "label": "uint256", "numberOfBytes": "32" }, - "label": "mapping(uint256 => struct ColonyNetworkDataTypes.Skill)", + "label": "mapping(uint256 => struct CommonDataTypes.Skill)", "numberOfBytes": "32", "value": { "encoding": "inplace", - "label": "struct ColonyNetworkDataTypes.Skill", + "label": "struct CommonDataTypes.Skill", "members": [ { "contract": "contracts/colonyNetwork/ColonyNetworkAuction.sol:ColonyNetworkAuction", diff --git a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkDeployer.sol:ColonyNetworkDeployer.json b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkDeployer.sol:ColonyNetworkDeployer.json index 205d16a0e5..f170b9070b 100644 --- a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkDeployer.sol:ColonyNetworkDeployer.json +++ b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkDeployer.sol:ColonyNetworkDeployer.json @@ -305,11 +305,11 @@ "label": "uint256", "numberOfBytes": "32" }, - "label": "mapping(uint256 => struct ColonyNetworkDataTypes.Skill)", + "label": "mapping(uint256 => struct CommonDataTypes.Skill)", "numberOfBytes": "32", "value": { "encoding": "inplace", - "label": "struct ColonyNetworkDataTypes.Skill", + "label": "struct CommonDataTypes.Skill", "members": [ { "contract": "contracts/colonyNetwork/ColonyNetworkDeployer.sol:ColonyNetworkDeployer", diff --git a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkENS.sol:ColonyNetworkENS.json b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkENS.sol:ColonyNetworkENS.json index e884e616d7..fde3772693 100644 --- a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkENS.sol:ColonyNetworkENS.json +++ b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkENS.sol:ColonyNetworkENS.json @@ -305,11 +305,11 @@ "label": "uint256", "numberOfBytes": "32" }, - "label": "mapping(uint256 => struct ColonyNetworkDataTypes.Skill)", + "label": "mapping(uint256 => struct CommonDataTypes.Skill)", "numberOfBytes": "32", "value": { "encoding": "inplace", - "label": "struct ColonyNetworkDataTypes.Skill", + "label": "struct CommonDataTypes.Skill", "members": [ { "contract": "contracts/colonyNetwork/ColonyNetworkENS.sol:ColonyNetworkENS", diff --git a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkExtensions.sol:ColonyNetworkExtensions.json b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkExtensions.sol:ColonyNetworkExtensions.json index a9bf72907d..543c70a6b4 100644 --- a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkExtensions.sol:ColonyNetworkExtensions.json +++ b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkExtensions.sol:ColonyNetworkExtensions.json @@ -305,11 +305,11 @@ "label": "uint256", "numberOfBytes": "32" }, - "label": "mapping(uint256 => struct ColonyNetworkDataTypes.Skill)", + "label": "mapping(uint256 => struct CommonDataTypes.Skill)", "numberOfBytes": "32", "value": { "encoding": "inplace", - "label": "struct ColonyNetworkDataTypes.Skill", + "label": "struct CommonDataTypes.Skill", "members": [ { "contract": "contracts/colonyNetwork/ColonyNetworkExtensions.sol:ColonyNetworkExtensions", diff --git a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkMining.sol:ColonyNetworkMining.json b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkMining.sol:ColonyNetworkMining.json index b8e05c9bc9..5ed5d8cde9 100644 --- a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkMining.sol:ColonyNetworkMining.json +++ b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkMining.sol:ColonyNetworkMining.json @@ -305,11 +305,11 @@ "label": "uint256", "numberOfBytes": "32" }, - "label": "mapping(uint256 => struct ColonyNetworkDataTypes.Skill)", + "label": "mapping(uint256 => struct CommonDataTypes.Skill)", "numberOfBytes": "32", "value": { "encoding": "inplace", - "label": "struct ColonyNetworkDataTypes.Skill", + "label": "struct CommonDataTypes.Skill", "members": [ { "contract": "contracts/colonyNetwork/ColonyNetworkMining.sol:ColonyNetworkMining", diff --git a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkSkills.sol:ColonyNetworkSkills.json b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkSkills.sol:ColonyNetworkSkills.json index e31cc4d380..ea8d893db2 100644 --- a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkSkills.sol:ColonyNetworkSkills.json +++ b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkSkills.sol:ColonyNetworkSkills.json @@ -305,11 +305,11 @@ "label": "uint256", "numberOfBytes": "32" }, - "label": "mapping(uint256 => struct ColonyNetworkDataTypes.Skill)", + "label": "mapping(uint256 => struct CommonDataTypes.Skill)", "numberOfBytes": "32", "value": { "encoding": "inplace", - "label": "struct ColonyNetworkDataTypes.Skill", + "label": "struct CommonDataTypes.Skill", "members": [ { "contract": "contracts/colonyNetwork/ColonyNetworkSkills.sol:ColonyNetworkSkills", diff --git a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkStorage.sol:ColonyNetworkStorage.json b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkStorage.sol:ColonyNetworkStorage.json index 14053448f0..20b5a83ab2 100644 --- a/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkStorage.sol:ColonyNetworkStorage.json +++ b/.storage-layouts-normalized/contracts/colonyNetwork/ColonyNetworkStorage.sol:ColonyNetworkStorage.json @@ -305,11 +305,11 @@ "label": "uint256", "numberOfBytes": "32" }, - "label": "mapping(uint256 => struct ColonyNetworkDataTypes.Skill)", + "label": "mapping(uint256 => struct CommonDataTypes.Skill)", "numberOfBytes": "32", "value": { "encoding": "inplace", - "label": "struct ColonyNetworkDataTypes.Skill", + "label": "struct CommonDataTypes.Skill", "members": [ { "contract": "contracts/colonyNetwork/ColonyNetworkStorage.sol:ColonyNetworkStorage", diff --git a/.storage-layouts-normalized/contracts/common/CommonDataTypes.sol:CommonDataTypes.json b/.storage-layouts-normalized/contracts/common/CommonDataTypes.sol:CommonDataTypes.json new file mode 100644 index 0000000000..82b695cebb --- /dev/null +++ b/.storage-layouts-normalized/contracts/common/CommonDataTypes.sol:CommonDataTypes.json @@ -0,0 +1,3 @@ +{ + "storage": [] +} \ No newline at end of file diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index 806b12ecdd..fae875c1cd 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -355,7 +355,7 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP function createProxyColony(uint256 _destinationChainId, bytes32 _salt) public stoppable { IColonyNetwork(colonyNetworkAddress).createProxyColony(_destinationChainId, _salt); - emit ProxyColonyRequested(_destinationChainId, _salt); + emit ProxyColonyRequested(msgSender(), _destinationChainId, _salt); } function getMetatransactionNonce(address _user) public view override returns (uint256 nonce) { diff --git a/contracts/colony/ColonyDataTypes.sol b/contracts/colony/ColonyDataTypes.sol index c24a833be9..9ec25e1242 100755 --- a/contracts/colony/ColonyDataTypes.sol +++ b/contracts/colony/ColonyDataTypes.sol @@ -18,8 +18,10 @@ pragma solidity 0.8.28; +import { CommonDataTypes } from "./../common/CommonDataTypes.sol"; + // prettier-ignore -interface ColonyDataTypes { +interface ColonyDataTypes is CommonDataTypes { // Events /// @notice Event logged when Colony is initialised @@ -272,10 +274,11 @@ interface ColonyDataTypes { event PayoutClaimed(address agent, uint256 id, uint256 slot, address token, uint256 tokenPayout); /// @notice Event logged when a colony requests a proxy colony deployment + /// @param agent The address that is responsible for triggering this event /// @param destinationChainId The chain id of the destination chain /// @param salt The salt used to generate the proxy address /// @dev The address corresponding to the salt must be this colony's address - event ProxyColonyRequested(uint256 destinationChainId, bytes32 salt); + event ProxyColonyRequested(address agent, uint256 destinationChainId, bytes32 salt); // Structs diff --git a/contracts/colony/ColonyStorage.sol b/contracts/colony/ColonyStorage.sol index 9ac320117a..76799c1181 100755 --- a/contracts/colony/ColonyStorage.sol +++ b/contracts/colony/ColonyStorage.sol @@ -24,7 +24,6 @@ import { CommonStorage } from "./../common/CommonStorage.sol"; import { ERC20Extended } from "./../common/ERC20Extended.sol"; import { DomainRoles } from "./../common/DomainRoles.sol"; import { IColonyNetwork } from "./../colonyNetwork/IColonyNetwork.sol"; -import { ColonyNetworkDataTypes } from "./../colonyNetwork/ColonyNetworkDataTypes.sol"; import { ColonyExtension } from "./../extensions/ColonyExtension.sol"; import { PatriciaTreeProofs } from "./../patriciaTree/PatriciaTreeProofs.sol"; import { ColonyAuthority } from "./ColonyAuthority.sol"; @@ -33,7 +32,7 @@ import { ColonyDataTypes } from "./ColonyDataTypes.sol"; // ignore-file-swc-131 // ignore-file-swc-108 -contract ColonyStorage is ColonyDataTypes, ColonyNetworkDataTypes, DSMath, CommonStorage { +contract ColonyStorage is ColonyDataTypes, DSMath, CommonStorage { uint256 constant COLONY_NETWORK_SLOT = 6; uint256 constant ROOT_LOCAL_SKILL_SLOT = 36; diff --git a/contracts/colonyNetwork/ColonyNetworkDataTypes.sol b/contracts/colonyNetwork/ColonyNetworkDataTypes.sol index f9d3ae7600..dc15aacbdf 100755 --- a/contracts/colonyNetwork/ColonyNetworkDataTypes.sol +++ b/contracts/colonyNetwork/ColonyNetworkDataTypes.sol @@ -18,8 +18,10 @@ pragma solidity 0.8.28; +import { CommonDataTypes } from "./../common/CommonDataTypes.sol"; + // prettier-ignore -interface ColonyNetworkDataTypes { +interface ColonyNetworkDataTypes is CommonDataTypes{ /// @notice Event logged when the colony network is intialised. This is only ever emitted once in a network's lifetime /// @param resolver The Resolver contract address used by the Colony version 1 event ColonyNetworkInitialised(address resolver); @@ -167,22 +169,6 @@ interface ColonyNetworkDataTypes { /// @param salt The salt used to generate the proxy address event ProxyColonyRequested(address colony, uint256 destinationChainId, bytes32 salt); - - struct Skill { - // total number of parent skills - uint128 nParents; - // total number of child skills - uint128 nChildren; - // array of `skill_id`s of parent skills starting from the 1st to `n`th, where `n` is an integer power of two larger than or equal to 1 - uint256[] parents; - // array of `skill_id`s of all child skills - uint256[] children; - // `true` for a global skill reused across colonies or `false` for a skill mapped to a single colony's domain - bool DEPRECATED_globalSkill; - // `true` for a skill that is deprecated NB: deprecation is now stored locally on colonies - bool DEPRECATED_deprecated; - } - struct ENSRecord { address addr; string orbitdb; diff --git a/contracts/common/CommonDataTypes.sol b/contracts/common/CommonDataTypes.sol new file mode 100644 index 0000000000..03d56b69c4 --- /dev/null +++ b/contracts/common/CommonDataTypes.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + This file is part of The Colony Network. + + The Colony Network is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + The Colony Network is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with The Colony Network. If not, see . +*/ + +pragma solidity 0.8.27; + +// prettier-ignore +interface CommonDataTypes { + struct Skill { + // total number of parent skills + uint128 nParents; + // total number of child skills + uint128 nChildren; + // array of `skill_id`s of parent skills starting from the 1st to `n`th, where `n` is an integer power of two larger than or equal to 1 + uint256[] parents; + // array of `skill_id`s of all child skills + uint256[] children; + // `true` for a global skill reused across colonies or `false` for a skill mapped to a single colony's domain + bool DEPRECATED_globalSkill; + // `true` for a skill that is deprecated NB: deprecation is now stored locally on colonies + bool DEPRECATED_deprecated; + } +} diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 84f2c443a9..135064c85d 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -353,6 +353,7 @@ contract("Cross-chain", (accounts) => { const colonyProxyRequestEvent = receipt.events.filter((e) => e.address === colonyAddress)[0]; parsed = homeColony.interface.parseLog(colonyProxyRequestEvent); expect(parsed.name).to.equal("ProxyColonyRequested"); + expect(parsed.args.agent).to.equal(receipt.from); expect(parsed.args.destinationChainId.toHexString()).to.equal(foreignChainId); expect(parsed.args.salt).to.equal(colonyCreationSalt); From 272e0536e76c25df0a719b068a100fcd5eee9a03 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 20 Jan 2025 12:12:07 +0000 Subject: [PATCH 58/72] Easy changes following review --- ...yColonyNetwork.sol:ProxyColonyNetwork.json | 2 +- contracts/bridging/ProxyColony.sol | 4 +- contracts/bridging/ProxyColonyNetwork.sol | 6 +- contracts/colony/Colony.sol | 2 +- .../colony/ColonyArbitraryTransaction.sol | 2 +- contracts/colony/ColonyAuthority.sol | 2 +- contracts/colony/ColonyFunding.sol | 8 +-- contracts/colony/IColony.sol | 1 - contracts/colony/IMetaColony.sol | 2 +- .../colonyNetwork/ColonyNetworkDeployer.sol | 3 +- docs/interfaces/imetacolony.md | 26 +++---- hardhat.config.js | 2 +- helpers/wormholescanMock/package.json | 2 +- test/contracts-network/colony-recovery.js | 2 +- test/cross-chain/cross-chain.js | 68 ++----------------- test/deploy-proxy-network-fixture.js | 2 +- 16 files changed, 37 insertions(+), 97 deletions(-) diff --git a/.storage-layouts-normalized/contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork.json b/.storage-layouts-normalized/contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork.json index 9002f3042e..eacdeddbc0 100644 --- a/.storage-layouts-normalized/contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork.json +++ b/.storage-layouts-normalized/contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork.json @@ -68,7 +68,7 @@ }, { "contract": "contracts/bridging/ProxyColonyNetwork.sol:ProxyColonyNetwork", - "label": "shellColonies", + "label": "proxyColonies", "offset": 0, "slot": "6", "type": { diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index 82ce5da086..f4f7142e6c 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -44,7 +44,7 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards, BasicMetaTransaction _; } - event DomainFundsClaimed(address token, uint256 _domainId, uint256 balance); + event DomainFundsClaimed(address token, uint256 domainId, uint256 balance); event TransferMade(address token, address user, uint256 amount); // Public functions @@ -115,7 +115,7 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards, BasicMetaTransaction if (_token == address(0x0)) { payable(_recipient).transfer(_amount); } else { - require(ERC20Extended(_token).transfer(_recipient, _amount), "colony-shell-transfer-failed"); + require(ERC20Extended(_token).transfer(_recipient, _amount), "colony-proxy-transfer-failed"); } emit TransferMade(_token, _recipient, _amount); diff --git a/contracts/bridging/ProxyColonyNetwork.sol b/contracts/bridging/ProxyColonyNetwork.sol index 6c21655c7a..8614da92cb 100644 --- a/contracts/bridging/ProxyColonyNetwork.sol +++ b/contracts/bridging/ProxyColonyNetwork.sol @@ -37,7 +37,7 @@ contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards, DomainReceiver address public colonyBridgeAddress; uint256 public homeChainId; address public proxyColonyResolverAddress; - mapping(address => bool) public shellColonies; + mapping(address => bool) public proxyColonies; address public domainTokenReceiverResolver; /// @notice Event logged when the colony network has data about a bridge contract set. @@ -89,7 +89,7 @@ contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards, DomainReceiver } function msgSenderIsColony() internal view override returns (bool) { - return shellColonies[msgSender()]; + return proxyColonies[msgSender()]; } function setDomainTokenReceiverResolver(address _resolver) public auth { @@ -112,7 +112,7 @@ contract ProxyColonyNetwork is DSAuth, Multicall, CallWithGuards, DomainReceiver ) ); - shellColonies[address(etherRouter)] = true; + proxyColonies[address(etherRouter)] = true; etherRouter.setResolver(proxyColonyResolverAddress); // ignore-swc-113 diff --git a/contracts/colony/Colony.sol b/contracts/colony/Colony.sol index fae875c1cd..7539b4103b 100755 --- a/contracts/colony/Colony.sol +++ b/contracts/colony/Colony.sol @@ -336,7 +336,7 @@ contract Colony is BasicMetaTransaction, Multicall, ColonyStorage, PatriciaTreeP sig = bytes4(keccak256("makeProxyArbitraryTransaction(uint256,address,bytes)")); colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); - sig = bytes4(keccak256("callProxyNetwork(uint256,bytes[])")); + sig = bytes4(keccak256("multicallProxyNetwork(uint256,bytes[])")); colonyAuthority.setRoleCapability(uint8(ColonyRole.Root), address(this), sig, true); sig = bytes4( diff --git a/contracts/colony/ColonyArbitraryTransaction.sol b/contracts/colony/ColonyArbitraryTransaction.sol index 29fe0ef10b..5b82a956d6 100644 --- a/contracts/colony/ColonyArbitraryTransaction.sol +++ b/contracts/colony/ColonyArbitraryTransaction.sol @@ -61,7 +61,7 @@ contract ColonyArbitraryTransaction is ColonyStorage { } } - function callProxyNetwork( + function multicallProxyNetwork( uint256 _chainId, bytes[] memory _actions ) public stoppable auth returns (bool) { diff --git a/contracts/colony/ColonyAuthority.sol b/contracts/colony/ColonyAuthority.sol index ca55f44d1a..b253920726 100644 --- a/contracts/colony/ColonyAuthority.sol +++ b/contracts/colony/ColonyAuthority.sol @@ -141,7 +141,7 @@ contract ColonyAuthority is CommonAuthority { addRoleCapability(ARBITRATION_ROLE, "setExpenditurePayout(uint256,uint256,uint256,uint256,uint256,address,uint256)"); addRoleCapability(FUNDING_ROLE, "moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)"); addRoleCapability(ROOT_ROLE, "makeProxyArbitraryTransaction(uint256,address,bytes)"); - addRoleCapability(ROOT_ROLE, "callProxyNetwork(uint256,bytes[])"); + addRoleCapability(ROOT_ROLE, "multicallProxyNetwork(uint256,bytes[])"); addRoleCapability(FUNDING_ROLE, "exchangeTokensViaLiFi(uint256,uint256,uint256,bytes,uint256,address,uint256)"); addRoleCapability(FUNDING_ROLE, "exchangeProxyHeldTokensViaLiFi(uint256,uint256,uint256,bytes,uint256,uint256,address,uint256)"); diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index b4847941fe..d90ca5fe4a 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -245,7 +245,7 @@ contract ColonyFunding is "colony-insufficient-funds" ); require( - _value <= getFundingPotBalance(domain.fundingPotId, _token), + _value <= getFundingPotBalance(domain.fundingPotId, address(0x0)), "colony-insufficient-funds" ); } @@ -311,7 +311,7 @@ contract ColonyFunding is "colony-insufficient-funds" ); require( - _value <= getFundingPotBalance(d.fundingPotId, _chainId, _token), + _value <= getFundingPotBalance(d.fundingPotId, _chainId, address(0x0)), "colony-insufficient-funds" ); } @@ -601,7 +601,7 @@ contract ColonyFunding is // If we're moving from the root pot, then check we haven't dropped below what we need // to cover any approvals that we've made. require( - getFundingPotBalance(_fromPot, _chainId, _token) >= tokenApprovalTotals[_token], + getFundingPotBalance(_fromPot, block.chainid, _token) >= tokenApprovalTotals[_token], "colony-funding-too-many-approvals" ); } @@ -708,9 +708,7 @@ contract ColonyFunding is for (uint256 i; i < _slots.length; i++) { require(_amounts[i] <= MAX_PAYOUT, "colony-payout-too-large"); uint256 currentPayout = getExpenditureSlotPayout(_id, _slots[i], _chainId, _token); - // uint256 currentPayout = expenditureSlotPayouts[_id][_slots[i]][_token]; setExpenditureSlotPayout(_id, _slots[i], _chainId, _token, _amounts[i]); - // expenditureSlotPayouts[_id][_slots[i]][_token] = _amounts[i]; runningTotal = (runningTotal - currentPayout) + _amounts[i]; emit ExpenditurePayoutSet(msgSender(), _id, _slots[i], _token, _amounts[i]); diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 05e6b2431e..83a1a90bd2 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -567,7 +567,6 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, uint256 _amount ) external; - /// @notice @deprecated /// @notice Set the token payout on an expenditure slot. Can only be called by expenditure owner. /// @dev Can only be called while expenditure is in draft state. /// @param _id Id of the expenditure diff --git a/contracts/colony/IMetaColony.sol b/contracts/colony/IMetaColony.sol index 4a0adb610f..077b3b5816 100644 --- a/contracts/colony/IMetaColony.sol +++ b/contracts/colony/IMetaColony.sol @@ -70,5 +70,5 @@ interface IMetaColony is IColony { /// @notice Call (a) function(s) on the proxyColonyNetwork on a different chain /// @param _chainId The chainId of the chain the function is being called on /// @param _actions The actions to be called - function callProxyNetwork(uint256 _chainId, bytes[] memory _actions) external; + function multicallProxyNetwork(uint256 _chainId, bytes[] memory _actions) external; } diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index a74c705a61..96b3ab9a64 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -239,8 +239,7 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage, DomainReceiverManagement function msgSenderIsColony() internal view override returns (bool) { require(_isColony[msgSender()], "colony-caller-must-be-colony"); - assert(msgSender() == msg.sender); - return true; + return msgSender() == msg.sender; } function isStopped() internal view override returns (bool) { diff --git a/docs/interfaces/imetacolony.md b/docs/interfaces/imetacolony.md index aeeefa263a..9264ba07c8 100644 --- a/docs/interfaces/imetacolony.md +++ b/docs/interfaces/imetacolony.md @@ -146,19 +146,6 @@ Burn tokens held by the colony. Can only burn tokens held in the root funding po |amount|uint256|The amount of tokens to burn -### ▸ `callProxyNetwork(uint256 _chainId, bytes[] memory _actions)` - -Call (a) function(s) on the proxyColonyNetwork on a different chain - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_chainId|uint256|The chainId of the chain the function is being called on -|_actions|bytes[]|The actions to be called - - ### ▸ `cancelExpenditure(uint256 _id)` Cancels the expenditure and prevents further editing. Can only be called by expenditure owner. @@ -1420,6 +1407,19 @@ Call multiple functions in the current contract and return the data from all of |---|---|---| |results|bytes[]|The results from each of the calls passed in via data +### ▸ `multicallProxyNetwork(uint256 _chainId, bytes[] memory _actions)` + +Call (a) function(s) on the proxyColonyNetwork on a different chain + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_chainId|uint256|The chainId of the chain the function is being called on +|_actions|bytes[]|The actions to be called + + ### ▸ `numRecoveryRoles():uint64 numRoles` Return number of recovery roles. diff --git a/hardhat.config.js b/hardhat.config.js index 89b3f6d700..b582deb7ac 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -81,7 +81,7 @@ task("test", "Run tests").setAction(async () => { } }); -task("ensureCreateXDeployed", "Ensure CreateX is deployed").setAction(async () => { +task("ensure-createx-deployed", "Ensure CreateX is deployed").setAction(async () => { const { idempotentDeployCreateX } = require("./helpers/test-helper"); // eslint-disable-line global-require await idempotentDeployCreateX(); diff --git a/helpers/wormholescanMock/package.json b/helpers/wormholescanMock/package.json index 15242d0314..fa84372447 100644 --- a/helpers/wormholescanMock/package.json +++ b/helpers/wormholescanMock/package.json @@ -1,5 +1,5 @@ { - "name": "wormholescanmock", + "name": "wormholescan-mock", "version": "1.0.0", "main": "dist/index.js", "scripts": { diff --git a/test/contracts-network/colony-recovery.js b/test/contracts-network/colony-recovery.js index 245e3a9127..8fc4b7beae 100644 --- a/test/contracts-network/colony-recovery.js +++ b/test/contracts-network/colony-recovery.js @@ -214,7 +214,7 @@ contract("Colony Recovery", (accounts) => { await checkErrorRevert(metaColony.makeArbitraryTransactions([], [], true), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.makeSingleArbitraryTransaction(ADDRESS_ZERO, HASHZERO), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.makeProxyArbitraryTransaction(1, ADDRESS_ZERO, HASHZERO), "colony-in-recovery-mode"); - await checkErrorRevert(metaColony.callProxyNetwork(1, []), "colony-in-recovery-mode"); + await checkErrorRevert(metaColony.multicallProxyNetwork(1, []), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.updateApprovalAmount(ADDRESS_ZERO, ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.finalizeRewardPayout(1), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.claimDomainFunds(ADDRESS_ZERO, 1), "colony-in-recovery-mode"); diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 135064c85d..fa09ff2187 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -144,62 +144,6 @@ contract("Cross-chain", (accounts) => { homeChainId = await ethersHomeSigner.provider.send("eth_chainId", []); wormholeHomeChainId = evmChainIdToWormholeChainId(homeChainId); - // const foreignNetworkId = await ethersForeignSigner.provider.send("net_version", []); - // foreignChainId = await ethersForeignSigner.provider.send("eth_chainId", []); - // wormholeForeignChainId = evmChainIdToWormholeChainId(foreignChainId); - - // Deploy shell colonyNetwork to whichever chain truffle hasn't already deployed to. - // try { - // if (process.env.HARDHAT_FOREIGN === "true") { - // await exec(`CHAIN_ID=${parseInt(foreignChainId, 16)} npx hardhat ensureCreateXDeployed --network development`); - // } else { - // await exec(`CHAIN_ID=${parseInt(foreignChainId, 16)} npx hardhat ensureCreateXDeployed --network development2`); - // } - - // const createX = await new ethers.Contract(CREATEX_ADDRESS, ICreateX.abi, ethersForeignSigner); - - // // This is a fake instance of an etherRouter, just so we can call encodeABs - // const fakeEtherRouter = await EtherRouterCreate3.at(CREATEX_ADDRESS); - // const setOwnerData = fakeEtherRouter.contract.methods.setOwner(accounts[0]).encodeABI(); - - // const tx = await createX["deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))"]( - // `0xb77d57f4959eafa0339424b83fcfaf9c15407461005e95d52076387600e2c1e9`, - // EtherRouterCreate3.bytecode, - // setOwnerData, - // [0, 0], - // { from: accounts[0] }, - // ); - - // const receipt = await tx.wait(); - - // const etherRouter = await new ethers.Contract( - // receipt.events.filter((log) => log.event === "ContractCreation")[0].args.newContract, - // EtherRouter.abi, - // ethersForeignSigner, - // ); - // let resolver = await new ethers.ContractFactory(Resolver.abi, Resolver.bytecode, ethersForeignSigner).deploy(); - // const proxyColonyNetworkImplementation = await new ethers.ContractFactory( - // ProxyColonyNetwork.abi, - // ProxyColonyNetwork.bytecode, - // ethersForeignSigner, - // ).deploy(); - - // await setupProxyColonyNetwork(etherRouter, proxyColonyNetworkImplementation, resolver); - // console.log("**** shell colony network set up"); - - // // Set up the resolver for shell colonies - // resolver = await new ethers.ContractFactory(Resolver.abi, Resolver.bytecode, ethersForeignSigner).deploy(); - // const proxyColonyImplementation = await new ethers.ContractFactory(ProxyColony.abi, ProxyColony.bytecode, ethersForeignSigner).deploy(); - - // await setupEtherRouter("bridging", "ProxyColony", { ProxyColony: proxyColonyImplementation.address }, resolver); - // const proxyColonyNetwork = new ethers.Contract(etherRouter.address, ProxyColonyNetwork.abi, ethersForeignSigner); - - // await proxyColonyNetwork.setProxyColonyResolverAddress(resolver.address); - // } catch (err) { - // console.log(err); - // process.exit(1); - // } - // 0x539 is the chain id used by truffle by default (regardless of networkid), and if // we see it in our tests that's the coverage chain, which builds the contract artifacts // in to a different location. If we see another chain id, we assume it's non-coverage @@ -414,7 +358,7 @@ contract("Cross-chain", (accounts) => { await checkErrorRevertEthers(tx.wait(), "colony-caller-must-be-meta-colony"); }); - it("callProxyNetwork can only be called through the metacolony", async () => { + it("multicallProxyNetwork can only be called through the metacolony", async () => { const payload = homeColonyNetwork.interface.encodeFunctionData("setColonyBridgeAddress", [ADDRESS_ZERO]); let tx = await homeColonyNetwork.createColonyForFrontend(ADDRESS_ZERO, "A", "A", 18, CURR_VERSION, "", ""); await tx.wait(); @@ -423,14 +367,14 @@ contract("Cross-chain", (accounts) => { const colonyAddress = await homeColonyNetwork.getColony(colonyCount); const fakeMetaColony = new ethers.Contract(colonyAddress, IMetaColony.abi, ethersHomeSigner); - tx = await fakeMetaColony.callProxyNetwork(foreignChainId, [payload], { gasLimit: 1000000 }); + tx = await fakeMetaColony.multicallProxyNetwork(foreignChainId, [payload], { gasLimit: 1000000 }); await checkErrorRevertEthers(tx.wait(), "colony-caller-must-be-meta-colony"); }); - it("callProxyNetwork can only be called by root permissions on the metacolony", async () => { + it("multicallProxyNetwork can only be called by root permissions on the metacolony", async () => { const payload = remoteColonyNetwork.interface.encodeFunctionData("setColonyBridgeAddress", [ADDRESS_ZERO]); const homeMetacolony2 = new ethers.Contract(homeMetacolony.address, IMetaColony.abi, ethersHomeSigner2); - let tx = await homeMetacolony2.callProxyNetwork(foreignChainId, [payload], { gasLimit: 1000000 }); + let tx = await homeMetacolony2.multicallProxyNetwork(foreignChainId, [payload], { gasLimit: 1000000 }); await checkErrorRevertEthers(tx.wait(), "ds-auth-unauthorized"); // Add root permissions @@ -446,7 +390,7 @@ contract("Cross-chain", (accounts) => { // Can now call const p = guardianSpy.getPromiseForNextBridgedTransaction(); - const tx3 = await homeMetacolony2.callProxyNetwork(foreignChainId, [payload]); + const tx3 = await homeMetacolony2.multicallProxyNetwork(foreignChainId, [payload]); await tx3.wait(); await p; @@ -463,7 +407,7 @@ contract("Cross-chain", (accounts) => { const bridgeAddress = await remoteColonyNetwork.colonyBridgeAddress(); const payload = remoteColonyNetwork.interface.encodeFunctionData("setColonyBridgeAddress", [ADDRESS_ZERO]); const p = guardianSpy.getPromiseForNextBridgedTransaction(); - const tx = await homeMetacolony.callProxyNetwork(foreignChainId, [payload]); + const tx = await homeMetacolony.multicallProxyNetwork(foreignChainId, [payload]); await tx.wait(); await p; const bridgeAddressAfter = await remoteColonyNetwork.colonyBridgeAddress(); diff --git a/test/deploy-proxy-network-fixture.js b/test/deploy-proxy-network-fixture.js index 1aafbd92a4..f2ef0364a0 100644 --- a/test/deploy-proxy-network-fixture.js +++ b/test/deploy-proxy-network-fixture.js @@ -21,7 +21,7 @@ const LiFiFacetProxyMock = artifacts.require("LiFiFacetProxyMock"); module.exports = async () => { const accounts = await web3.eth.getAccounts(); - await hre.run("ensureCreateXDeployed"); + await hre.run("ensure-createx-deployed"); const CreateX = truffleContract({ abi: createXABI.abi }); CreateX.setProvider(web3.currentProvider); const createX = await CreateX.at(CREATEX_ADDRESS); From 304b36dc47dd03c61cee9758374d26e331ce2949 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 20 Jan 2025 14:54:26 +0000 Subject: [PATCH 59/72] Single LiFi function --- contracts/colony/ColonyAuthority.sol | 3 +- contracts/colony/ColonyFunding.sol | 122 +++++++++-------------- contracts/colony/IColony.sol | 20 +--- docs/interfaces/icolony.md | 20 +--- docs/interfaces/imetacolony.md | 20 +--- test/contracts-network/colony-funding.js | 15 +-- test/cross-chain/cross-chain.js | 4 +- 7 files changed, 63 insertions(+), 141 deletions(-) diff --git a/contracts/colony/ColonyAuthority.sol b/contracts/colony/ColonyAuthority.sol index b253920726..b6f840e472 100644 --- a/contracts/colony/ColonyAuthority.sol +++ b/contracts/colony/ColonyAuthority.sol @@ -143,8 +143,7 @@ contract ColonyAuthority is CommonAuthority { addRoleCapability(ROOT_ROLE, "makeProxyArbitraryTransaction(uint256,address,bytes)"); addRoleCapability(ROOT_ROLE, "multicallProxyNetwork(uint256,bytes[])"); - addRoleCapability(FUNDING_ROLE, "exchangeTokensViaLiFi(uint256,uint256,uint256,bytes,uint256,address,uint256)"); - addRoleCapability(FUNDING_ROLE, "exchangeProxyHeldTokensViaLiFi(uint256,uint256,uint256,bytes,uint256,uint256,address,uint256)"); + addRoleCapability(FUNDING_ROLE, "exchangeTokensViaLiFi(uint256,uint256,uint256,bytes,uint256,uint256,address,uint256)"); } function addRoleCapability(uint8 role, bytes memory sig) private { diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index d90ca5fe4a..10e9eef116 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -219,73 +219,8 @@ contract ColonyFunding is } address constant LIFI_ADDRESS = 0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE; - function exchangeTokensViaLiFi( - uint256 _permissionDomainId, - uint256 _childSkillIndex, - uint256 _domainId, - bytes memory _txdata, - uint256 _value, - address _token, - uint256 _amount - ) public stoppable authDomain(_permissionDomainId, _childSkillIndex, _domainId) { - // TODO: Colony Network fee - Domain storage domain = domains[_domainId]; - - uint256 priorApproval = ERC20Extended(_token).allowance(address(this), LIFI_ADDRESS); - // Check the domain has enough for what is - if (_token == address(0x0)) { - require( - _value + _amount <= getFundingPotBalance(domain.fundingPotId, _token), - "colony-insufficient-funds" - ); - } else { - require( - _amount <= getFundingPotBalance(domain.fundingPotId, _token), - "colony-insufficient-funds" - ); - require( - _value <= getFundingPotBalance(domain.fundingPotId, address(0x0)), - "colony-insufficient-funds" - ); - } - - if (_domainId == 1) { - // Check that we have enough not-already-approved tokens - require( - getFundingPotBalance(domain.fundingPotId, _token) >= tokenApprovalTotals[_token] + _amount, - "colony-insufficient-funds" - ); - } - - // Deduct the amount from the domain - decrementFundingPotBalance(domain.fundingPotId, block.chainid, _token, _amount); - decrementFundingPotBalance(domain.fundingPotId, block.chainid, address(0x0), _value); - - require( - ERC20Extended(_token).approve(LIFI_ADDRESS, _amount + priorApproval), - "colony-approve-failed" - ); - (bool success, ) = LIFI_ADDRESS.call{ value: _value }(_txdata); - - require(success, "colony-exchange-tokens-failed"); - - // Check the allowances afterwards, ensuring that at least some of the tokens we approved were spent by - // the LiFi transaction - if not, a different token might have been spent, so revert. - uint256 postApproval = ERC20Extended(_token).allowance(address(this), LIFI_ADDRESS); - require(_amount + priorApproval > postApproval, "colony-unexpected-exchange"); - require(postApproval >= priorApproval, "colony-more-than-intended-allowance-used"); - - // If the LiFi transaction didn't use all the tokens, reduce the allowance back to what it was before - if (postApproval > priorApproval) { - require( - ERC20Extended(_token).approve(LIFI_ADDRESS, priorApproval), - "colony-post-exchange-approve-failed" - ); - } - } - - function exchangeProxyHeldTokensViaLiFi( + function exchangeTokensViaLiFi( uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, @@ -326,17 +261,56 @@ contract ColonyFunding is if (_token == address(0)) { revert("not yet implemented"); } else { - bytes[] memory actions = new bytes[](2); + if (block.chainid == _chainId) { + if (_domainId == 1) { + // Check that we have enough not-already-approved tokens + require( + getFundingPotBalance(d.fundingPotId, _token) >= tokenApprovalTotals[_token] + _amount, + "colony-insufficient-funds" + ); + } - actions[0] = abi.encodeCall( - ProxyColony.makeArbitraryTransaction, - (_token, abi.encodeCall(ERC20.approve, (LIFI_ADDRESS, _amount))) - ); - actions[1] = abi.encodeCall(ProxyColony.makeArbitraryTransaction, (LIFI_ADDRESS, _txdata)); + uint256 priorApproval = ERC20Extended(_token).allowance(address(this), LIFI_ADDRESS); - bytes memory multicallData = abi.encodeWithSignature("multicall(bytes[])", actions); + require( + ERC20Extended(_token).approve(LIFI_ADDRESS, _amount + priorApproval), + "colony-approve-failed" + ); + (bool success, ) = LIFI_ADDRESS.call{ value: _value }(_txdata); + + require(success, "colony-exchange-tokens-failed"); + + // Check the allowances afterwards, ensuring that at least some of the tokens we approved were spent by + // the LiFi transaction - if not, a different token might have been spent, so revert. + uint256 postApproval = ERC20Extended(_token).allowance(address(this), LIFI_ADDRESS); + require(_amount + priorApproval > postApproval, "colony-unexpected-exchange"); + require(postApproval >= priorApproval, "colony-more-than-intended-allowance-used"); + + // If the LiFi transaction didn't use all the tokens, reduce the allowance back to what it was before + if (postApproval > priorApproval) { + require( + ERC20Extended(_token).approve(LIFI_ADDRESS, priorApproval), + "colony-post-exchange-approve-failed" + ); + } + } else { + // Exchange is to happen on another chain + bytes[] memory actions = new bytes[](2); + + actions[0] = abi.encodeCall( + ProxyColony.makeArbitraryTransaction, + (_token, abi.encodeCall(ERC20.approve, (LIFI_ADDRESS, _amount))) + ); + actions[1] = abi.encodeCall(ProxyColony.makeArbitraryTransaction, (LIFI_ADDRESS, _txdata)); - IColony(address(this)).makeProxyArbitraryTransaction(_chainId, address(this), multicallData); + bytes memory multicallData = abi.encodeWithSignature("multicall(bytes[])", actions); + + IColony(address(this)).makeProxyArbitraryTransaction( + _chainId, + address(this), + multicallData + ); + } } } diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 83a1a90bd2..9f37a8a348 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -941,24 +941,6 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, /// @return uint256 amount Amount of the token that the domain can receive function getAllowedDomainReputationReceipt(uint256 _domainId) external view returns (uint256); - /// @notice Exchange funds between two tokens, potentially between chains - /// @param _permissionDomainId The domainId in which I have the permission to take this action - /// @param _childSkillIndex The child index in `_permissionDomainId` where we can find `_domainId` - /// @param _domainId Id of the domain - /// @param _txdata Transaction data for the exchange - /// @param _value Value of the transaction - /// @param _token Address of the token, `0x0` value indicates Ether - /// @param _amount Amount of tokens to exchange - function exchangeTokensViaLiFi( - uint256 _permissionDomainId, - uint256 _childSkillIndex, - uint256 _domainId, - bytes memory _txdata, - uint256 _value, - address _token, - uint256 _amount - ) external; - /// @notice Exchange funds between two tokens, potentially between chains /// The tokens being swapped are held by a proxy contract /// @param _permissionDomainId The domainId in which I have the permission to take this action @@ -969,7 +951,7 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, /// @param _chainId The chainId of the token /// @param _token Address of the token. If the native token is being swapped, can be anything and _amount should be 0. /// @param _amount Amount of tokens to exchange - function exchangeProxyHeldTokensViaLiFi( + function exchangeTokensViaLiFi( uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index 623809053b..063cf1f72f 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -419,7 +419,7 @@ Put colony network mining into recovery mode. Can only be called by user with re -### ▸ `exchangeProxyHeldTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, uint256 _chainId, address _token, uint256 _amount)` +### ▸ `exchangeTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, uint256 _chainId, address _token, uint256 _amount)` Exchange funds between two tokens, potentially between chains The tokens being swapped are held by a proxy contract @@ -438,24 +438,6 @@ Exchange funds between two tokens, potentially between chains The tokens being s |_amount|uint256|Amount of tokens to exchange -### ▸ `exchangeTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, address _token, uint256 _amount)` - -Exchange funds between two tokens, potentially between chains - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_permissionDomainId|uint256|The domainId in which I have the permission to take this action -|_childSkillIndex|uint256|The child index in `_permissionDomainId` where we can find `_domainId` -|_domainId|uint256|Id of the domain -|_txdata|bytes|Transaction data for the exchange -|_value|uint256|Value of the transaction -|_token|address|Address of the token, `0x0` value indicates Ether -|_amount|uint256|Amount of tokens to exchange - - ### ▸ `executeMetaTransaction(address userAddress, bytes memory payload, bytes32 sigR, bytes32 sigS, uint8 sigV):bytes returnData` Executes a metatransaction targeting this contract diff --git a/docs/interfaces/imetacolony.md b/docs/interfaces/imetacolony.md index 9264ba07c8..a7142c2076 100644 --- a/docs/interfaces/imetacolony.md +++ b/docs/interfaces/imetacolony.md @@ -442,7 +442,7 @@ Put colony network mining into recovery mode. Can only be called by user with re -### ▸ `exchangeProxyHeldTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, uint256 _chainId, address _token, uint256 _amount)` +### ▸ `exchangeTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, uint256 _chainId, address _token, uint256 _amount)` Exchange funds between two tokens, potentially between chains The tokens being swapped are held by a proxy contract @@ -461,24 +461,6 @@ Exchange funds between two tokens, potentially between chains The tokens being s |_amount|uint256|Amount of tokens to exchange -### ▸ `exchangeTokensViaLiFi(uint256 _permissionDomainId, uint256 _childSkillIndex, uint256 _domainId, bytes memory _txdata, uint256 _value, address _token, uint256 _amount)` - -Exchange funds between two tokens, potentially between chains - - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_permissionDomainId|uint256|The domainId in which I have the permission to take this action -|_childSkillIndex|uint256|The child index in `_permissionDomainId` where we can find `_domainId` -|_domainId|uint256|Id of the domain -|_txdata|bytes|Transaction data for the exchange -|_value|uint256|Value of the transaction -|_token|address|Address of the token, `0x0` value indicates Ether -|_amount|uint256|Amount of tokens to exchange - - ### ▸ `executeMetaTransaction(address userAddress, bytes memory payload, bytes32 sigR, bytes32 sigS, uint8 sigV):bytes returnData` Executes a metatransaction targeting this contract diff --git a/test/contracts-network/colony-funding.js b/test/contracts-network/colony-funding.js index 9931c6efa3..a9d278201b 100755 --- a/test/contracts-network/colony-funding.js +++ b/test/contracts-network/colony-funding.js @@ -958,7 +958,7 @@ contract("Colony Funding", (accounts) => { 50, ).encodeABI(); - const tx = await colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, token.address, 50); + const tx = await colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, chainId, token.address, 50); const swapEvent = tx.receipt.rawLogs .filter((e) => e.address === LIFI_ADDRESS) .map((e) => lifiEthers.interface.parseLog(e)) @@ -992,7 +992,7 @@ contract("Colony Funding", (accounts) => { 40, ).encodeABI(); - await colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, token.address, 50); + await colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, chainId, token.address, 50); await otherToken.mint(domain2ReceiverAddress, 50); // Better than 1:1 exchange rate const approval = await colony.getTokenApproval(token.address, LIFI_ADDRESS); @@ -1018,7 +1018,7 @@ contract("Colony Funding", (accounts) => { domain2ReceiverAddress, 40, ).encodeABI(); - await checkErrorRevert(colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, token.address, 50), "colony-unexpected-exchange"); + await checkErrorRevert(colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, chainId, token.address, 50), "colony-unexpected-exchange"); }); it("'lying' calls of exchangeTokensViaLiFi trying to spend already-approved tokens are caught", async () => { @@ -1033,7 +1033,10 @@ contract("Colony Funding", (accounts) => { domain2ReceiverAddress, 60, ).encodeABI(); - await checkErrorRevert(colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, token.address, 50), "colony-more-than-intended-allowance-used"); + await checkErrorRevert( + colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, chainId, token.address, 50), + "colony-more-than-intended-allowance-used", + ); }); it("If LiFi transaction was cheaper than expected, shouldn't leave extra allowance behind", async () => { @@ -1046,7 +1049,7 @@ contract("Colony Funding", (accounts) => { 40, ).encodeABI(); - await colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, token.address, 50); + await colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, chainId, token.address, 50); const approval = await token.allowance(colony.address, LIFI_ADDRESS); expect(approval).to.be.eq.BN(0); @@ -1067,7 +1070,7 @@ contract("Colony Funding", (accounts) => { domain2ReceiverAddress, 50, ).encodeABI(); - await checkErrorRevert(colony.exchangeTokensViaLiFi(1, UINT256_MAX, 1, txdata, 0, token.address, 50), "colony-insufficient-funds"); + await checkErrorRevert(colony.exchangeTokensViaLiFi(1, UINT256_MAX, 1, txdata, 0, chainId, token.address, 50), "colony-insufficient-funds"); }); }); }); diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index fa09ff2187..a5717fe428 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -867,7 +867,7 @@ contract("Cross-chain", (accounts) => { ethers.utils.parseEther("50"), ]); - tx = await homeColony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, homeToken.address, ethers.utils.parseEther("50")); + tx = await homeColony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, homeChainId, homeToken.address, ethers.utils.parseEther("50")); const receipt = await tx.wait(); const swapEvent = receipt.events @@ -951,7 +951,7 @@ contract("Cross-chain", (accounts) => { ]); p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await colony.exchangeProxyHeldTokensViaLiFi(1, 0, 2, txdata, 0, foreignChainId, foreignToken.address, ethers.utils.parseEther("70")); + tx = await colony.exchangeTokensViaLiFi(1, 0, 2, txdata, 0, foreignChainId, foreignToken.address, ethers.utils.parseEther("70")); await tx.wait(); const receipt = await p; From 5f7108f0a7989be851221b32d52ab416742c640a Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Tue, 21 Jan 2025 11:03:46 +0000 Subject: [PATCH 60/72] checkDomainTokenReceiverDeployed in line with previous PRs --- contracts/bridging/ProxyColony.sol | 5 +- .../colonyNetwork/ColonyNetworkDeployer.sol | 48 ----------------- contracts/colonyNetwork/IColonyNetwork.sol | 11 +--- contracts/common/DomainReceiverManagement.sol | 54 +++++++++++-------- docs/interfaces/icolonynetwork.md | 18 ------- test/contracts-network/colony-funding.js | 4 +- .../colony-network-recovery.js | 4 +- 7 files changed, 39 insertions(+), 105 deletions(-) diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index f4f7142e6c..91c1b576b6 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -72,9 +72,8 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards, BasicMetaTransaction } function claimTokensForDomain(address _token, uint256 _domainId) public { - address domainTokenReceiverAddress = ProxyColonyNetwork(owner).checkDomainTokenReceiverDeployed( - _domainId - ); + address domainTokenReceiverAddress = ProxyColonyNetwork(owner) + .idempotentDeployDomainTokenReceiver(_domainId); uint256 balance = (_token == address(0x0)) ? address(domainTokenReceiverAddress).balance diff --git a/contracts/colonyNetwork/ColonyNetworkDeployer.sol b/contracts/colonyNetwork/ColonyNetworkDeployer.sol index 96b3ab9a64..e0d7446193 100644 --- a/contracts/colonyNetwork/ColonyNetworkDeployer.sol +++ b/contracts/colonyNetwork/ColonyNetworkDeployer.sol @@ -203,40 +203,6 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage, DomainReceiverManagement return domainReceiverResolverAddress; } - function idempotentDeployDomainTokenReceiver( - uint256 _domainId - ) public stoppable calledByColony returns (address domainTokenReceiverAddress) { - // Calculate the address the domain should be receiving funds at - domainTokenReceiverAddress = getDomainTokenReceiverAddress(msgSender(), _domainId); - - if (!isContract(domainTokenReceiverAddress)) { - // Then deploy the contract - bytes32 salt = getDomainTokenReceiverDeploySalt(msgSender(), _domainId); - address deployedAddress = deployEtherRouterViaCreateX(salt); - require( - deployedAddress == domainTokenReceiverAddress, - "colony-network-domain-receiver-deploy-wrong-address" - ); - - // Set up the deployed contract - EtherRouter(payable(domainTokenReceiverAddress)).setResolver(domainReceiverResolverAddress); - DomainTokenReceiver(domainTokenReceiverAddress).setColony(msgSender()); - } else { - // Contract is deployed, check it's got the right resolver - try EtherRouter(payable(domainTokenReceiverAddress)).resolver() returns (Resolver resolver) { - if (address(resolver) != domainReceiverResolverAddress) { - EtherRouter(payable(domainTokenReceiverAddress)).setResolver( - domainReceiverResolverAddress - ); - } - } catch { - revert("colony-network-domain-receiver-not-etherrouter"); - } - } - - return domainTokenReceiverAddress; - } - function msgSenderIsColony() internal view override returns (bool) { require(_isColony[msgSender()], "colony-caller-must-be-colony"); return msgSender() == msg.sender; @@ -300,18 +266,4 @@ contract ColonyNetworkDeployer is ColonyNetworkStorage, DomainReceiverManagement DSAuth dsauth = DSAuth(_colonyAddress); dsauth.setOwner(address(0x0)); } - - function deployEtherRouterViaCreateX(bytes32 _salt) internal returns (address) { - EtherRouter etherRouter = EtherRouter( - payable( - ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( - _salt, - type(EtherRouterCreate3).creationCode, - abi.encodeWithSignature("setOwner(address)", (address(this))), - ICreateX.Values(0, 0) - ) - ) - ); - return address(etherRouter); - } } diff --git a/contracts/colonyNetwork/IColonyNetwork.sol b/contracts/colonyNetwork/IColonyNetwork.sol index 768bf934dd..9636c57e6b 100644 --- a/contracts/colonyNetwork/IColonyNetwork.sol +++ b/contracts/colonyNetwork/IColonyNetwork.sol @@ -541,15 +541,6 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery, IBasicMetaTransac /// @dev This should only be able to be called by the metacolony function bridgeMessageToNetwork(uint256 _chainId, bytes memory _payload) external; - /// @notice Function called by a colony to ensure that a DomainTokenReceiver has been deployed and set up correctly - /// for a particular domain. - /// @dev Should only be called by a colony. - /// @param _domainId The domainId of the domain to check the deployment for - /// @return domainTokenReceiverAddress The address of the DomainTokenReceiver - function idempotentDeployDomainTokenReceiver( - uint256 _domainId - ) external returns (address domainTokenReceiverAddress); - /// @notice Handles calls to create a new colony on another chain /// @dev Should only be called by a colony, if you're trying to call this directly you're doing something wrong /// @param _destinationChainId The chainId of the chain to create the colony on @@ -561,7 +552,7 @@ interface IColonyNetwork is ColonyNetworkDataTypes, IRecovery, IBasicMetaTransac /// @dev Should only be called by a colony. /// @param _domainId The domainId of the domain to check the deployment for /// @return domainTokenReceiverAddress The address of the DomainTokenReceiver - function checkDomainTokenReceiverDeployed( + function idempotentDeployDomainTokenReceiver( uint256 _domainId ) external returns (address domainTokenReceiverAddress); diff --git a/contracts/common/DomainReceiverManagement.sol b/contracts/common/DomainReceiverManagement.sol index f6a540c1d1..8e5edfd7c6 100644 --- a/contracts/common/DomainReceiverManagement.sol +++ b/contracts/common/DomainReceiverManagement.sol @@ -34,44 +34,40 @@ abstract contract DomainReceiverManagement is MetaTransactionMsgSender, IsContra function msgSenderIsColony() internal view virtual returns (bool); function isStopped() internal view virtual returns (bool); - function checkDomainTokenReceiverDeployed( + function idempotentDeployDomainTokenReceiver( uint256 _domainId ) public returns (address domainTokenReceiverAddress) { require(!isStopped(), "colony-domain-receiver-management-stopped"); require(msgSenderIsColony(), "colony-domain-receiver-management-not-colony"); + address domainReceiverResolverAddress = getDomainTokenReceiverResolver(); + // Calculate the address the domain should be receiving funds at domainTokenReceiverAddress = getDomainTokenReceiverAddress(msgSender(), _domainId); if (!isContract(domainTokenReceiverAddress)) { // Then deploy the contract bytes32 salt = getDomainTokenReceiverDeploySalt(msgSender(), _domainId); - address newContract = ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( - salt, - type(EtherRouterCreate3).creationCode, - abi.encodeWithSignature("setOwner(address)", (address(this))), - ICreateX.Values(0, 0) - ); + address deployedAddress = deployEtherRouterViaCreateX(salt); require( - newContract == domainTokenReceiverAddress, + deployedAddress == domainTokenReceiverAddress, "colony-network-domain-receiver-deploy-wrong-address" ); - } - // Check it's got the right resolver - try EtherRouter(payable(domainTokenReceiverAddress)).resolver() returns (Resolver resolver) { - if (address(resolver) != getDomainTokenReceiverResolver()) { - EtherRouter(payable(domainTokenReceiverAddress)).setResolver( - getDomainTokenReceiverResolver() - ); - } - } catch { - revert("colony-network-domain-receiver-not-etherrouter"); - } - - // Check it's set up correctly - if (DomainTokenReceiver(domainTokenReceiverAddress).getColonyAddress() != msgSender()) { + // Set up the deployed contract + EtherRouter(payable(domainTokenReceiverAddress)).setResolver(domainReceiverResolverAddress); DomainTokenReceiver(domainTokenReceiverAddress).setColonyAddress(msgSender()); + } else { + // Contract is deployed, check it's got the right resolver + try EtherRouter(payable(domainTokenReceiverAddress)).resolver() returns (Resolver resolver) { + if (address(resolver) != domainReceiverResolverAddress) { + EtherRouter(payable(domainTokenReceiverAddress)).setResolver( + domainReceiverResolverAddress + ); + } + } catch { + revert("colony-network-domain-receiver-not-etherrouter"); + } } return domainTokenReceiverAddress; @@ -109,4 +105,18 @@ abstract contract DomainReceiverManagement is MetaTransactionMsgSender, IsContra // This is intentional, as we want to allow the same receiver to be deployed on different chains return salt; } + + function deployEtherRouterViaCreateX(bytes32 _salt) internal returns (address) { + EtherRouter etherRouter = EtherRouter( + payable( + ICreateX(CREATEX_ADDRESS).deployCreate3AndInit( + _salt, + type(EtherRouterCreate3).creationCode, + abi.encodeWithSignature("setOwner(address)", (address(this))), + ICreateX.Values(0, 0) + ) + ) + ); + return address(etherRouter); + } } diff --git a/docs/interfaces/icolonynetwork.md b/docs/interfaces/icolonynetwork.md index 37731bb04c..beb49898a3 100644 --- a/docs/interfaces/icolonynetwork.md +++ b/docs/interfaces/icolonynetwork.md @@ -152,24 +152,6 @@ Calculate raw miner weight in WADs. |---|---|---| |_minerWeight|uint256|The weight of miner reward -### ▸ `checkDomainTokenReceiverDeployed(uint256 _domainId):address domainTokenReceiverAddress` - -Function called by a colony to ensure that a DomainTokenReceiver has been deployed and set up correctly for a particular domain. - -*Note: Should only be called by a colony.* - -**Parameters** - -|Name|Type|Description| -|---|---|---| -|_domainId|uint256|The domainId of the domain to check the deployment for - -**Return Parameters** - -|Name|Type|Description| -|---|---|---| -|domainTokenReceiverAddress|address|The address of the DomainTokenReceiver - ### ▸ `checkNotAdditionalProtectedVariable(uint256 _slot)` Check whether the supplied slot is a protected variable specific to this contract diff --git a/test/contracts-network/colony-funding.js b/test/contracts-network/colony-funding.js index a9d278201b..069ea62323 100755 --- a/test/contracts-network/colony-funding.js +++ b/test/contracts-network/colony-funding.js @@ -882,8 +882,8 @@ contract("Colony Funding", (accounts) => { await checkErrorRevert(colony.claimDomainFunds(ethers.constants.AddressZero, 2), "colony-funding-domain-does-not-exist"); }); - it("only a colony can call checkDomainTokenReceiverDeployed on Network", async () => { - await checkErrorRevert(colonyNetwork.checkDomainTokenReceiverDeployed(2), "colony-caller-must-be-colony"); + it("only a colony can call idempotentDeployDomainTokenReceiver on Network", async () => { + await checkErrorRevert(colonyNetwork.idempotentDeployDomainTokenReceiver(2), "colony-caller-must-be-colony"); }); it("If transfer fails from receiver, then the funds are not claimed", async () => { diff --git a/test/contracts-network/colony-network-recovery.js b/test/contracts-network/colony-network-recovery.js index 23c7753db2..219359ab9e 100644 --- a/test/contracts-network/colony-network-recovery.js +++ b/test/contracts-network/colony-network-recovery.js @@ -194,11 +194,11 @@ contract("Colony Network Recovery", (accounts) => { await checkErrorRevert(colonyNetwork.claimMiningReward(ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.startTokenAuction(ADDRESS_ZERO), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.setDomainTokenReceiverResolver(ADDRESS_ZERO), "colony-in-recovery-mode"); - await checkErrorRevert(colonyNetwork.idempotentDeployDomainTokenReceiver(ADDRESS_ZERO), "colony-in-recovery-mode"); + await checkErrorRevert(colonyNetwork.idempotentDeployDomainTokenReceiver(ADDRESS_ZERO), "colony-domain-receiver-management-stopped"); await checkErrorRevert(colonyNetwork.bridgeMessage(1, "0x00000000"), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.bridgeMessageToNetwork(1, "0x00000000"), "colony-in-recovery-mode"); await checkErrorRevert(colonyNetwork.createProxyColony(1, HASHZERO), "colony-in-recovery-mode"); - await checkErrorRevert(colonyNetwork.checkDomainTokenReceiverDeployed(1), "colony-domain-receiver-management-stopped"); + await checkErrorRevert(colonyNetwork.idempotentDeployDomainTokenReceiver(1), "colony-domain-receiver-management-stopped"); await colonyNetwork.approveExitRecovery(); await colonyNetwork.exitRecoveryMode(); From 5a1cf70a571686e7ad157c6e13f77be59bfa3754 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Tue, 21 Jan 2025 11:42:09 +0000 Subject: [PATCH 61/72] Add common/mixins directory --- .../{ => mixins}/Bytes4Includes.sol:Bytes4Includes.json | 0 .../{ => mixins}/CallWithGuards.sol:CallWithGuards.json | 0 .../{ => mixins}/ExtractCallData.sol:ExtractCallData.json | 0 .../common/{ => mixins}/IsContract.sol:IsContract.json | 0 contracts/bridging/ProxyColony.sol | 2 +- contracts/bridging/ProxyColonyNetwork.sol | 2 +- contracts/bridging/WormholeBridgeForColony.sol | 2 +- contracts/common/DomainReceiverManagement.sol | 2 +- contracts/common/GetActionSummary.sol | 4 ++-- contracts/common/{ => mixins}/Bytes4Includes.sol | 0 contracts/common/{ => mixins}/CallWithGuards.sol | 0 contracts/common/{ => mixins}/ExtractCallData.sol | 0 contracts/common/{ => mixins}/IsContract.sol | 0 contracts/extensions/MultisigPermissions.sol | 2 +- contracts/testHelpers/RequireExecuteCall.sol | 2 +- 15 files changed, 8 insertions(+), 8 deletions(-) rename .storage-layouts-normalized/contracts/common/{ => mixins}/Bytes4Includes.sol:Bytes4Includes.json (100%) rename .storage-layouts-normalized/contracts/common/{ => mixins}/CallWithGuards.sol:CallWithGuards.json (100%) rename .storage-layouts-normalized/contracts/common/{ => mixins}/ExtractCallData.sol:ExtractCallData.json (100%) rename .storage-layouts-normalized/contracts/common/{ => mixins}/IsContract.sol:IsContract.json (100%) rename contracts/common/{ => mixins}/Bytes4Includes.sol (100%) rename contracts/common/{ => mixins}/CallWithGuards.sol (100%) rename contracts/common/{ => mixins}/ExtractCallData.sol (100%) rename contracts/common/{ => mixins}/IsContract.sol (100%) diff --git a/.storage-layouts-normalized/contracts/common/Bytes4Includes.sol:Bytes4Includes.json b/.storage-layouts-normalized/contracts/common/mixins/Bytes4Includes.sol:Bytes4Includes.json similarity index 100% rename from .storage-layouts-normalized/contracts/common/Bytes4Includes.sol:Bytes4Includes.json rename to .storage-layouts-normalized/contracts/common/mixins/Bytes4Includes.sol:Bytes4Includes.json diff --git a/.storage-layouts-normalized/contracts/common/CallWithGuards.sol:CallWithGuards.json b/.storage-layouts-normalized/contracts/common/mixins/CallWithGuards.sol:CallWithGuards.json similarity index 100% rename from .storage-layouts-normalized/contracts/common/CallWithGuards.sol:CallWithGuards.json rename to .storage-layouts-normalized/contracts/common/mixins/CallWithGuards.sol:CallWithGuards.json diff --git a/.storage-layouts-normalized/contracts/common/ExtractCallData.sol:ExtractCallData.json b/.storage-layouts-normalized/contracts/common/mixins/ExtractCallData.sol:ExtractCallData.json similarity index 100% rename from .storage-layouts-normalized/contracts/common/ExtractCallData.sol:ExtractCallData.json rename to .storage-layouts-normalized/contracts/common/mixins/ExtractCallData.sol:ExtractCallData.json diff --git a/.storage-layouts-normalized/contracts/common/IsContract.sol:IsContract.json b/.storage-layouts-normalized/contracts/common/mixins/IsContract.sol:IsContract.json similarity index 100% rename from .storage-layouts-normalized/contracts/common/IsContract.sol:IsContract.json rename to .storage-layouts-normalized/contracts/common/mixins/IsContract.sol:IsContract.json diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index 91c1b576b6..ed66ae8bb7 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -19,7 +19,7 @@ pragma solidity 0.8.27; pragma experimental ABIEncoderV2; -import { CallWithGuards } from "../common/CallWithGuards.sol"; +import { CallWithGuards } from "../common/mixins/CallWithGuards.sol"; import { DSAuth } from "./../../lib/dappsys/auth.sol"; import { ERC20Extended } from "./../common/ERC20Extended.sol"; import { Multicall } from "./../common/Multicall.sol"; diff --git a/contracts/bridging/ProxyColonyNetwork.sol b/contracts/bridging/ProxyColonyNetwork.sol index 8614da92cb..49823f2f4e 100644 --- a/contracts/bridging/ProxyColonyNetwork.sol +++ b/contracts/bridging/ProxyColonyNetwork.sol @@ -20,7 +20,7 @@ pragma solidity 0.8.27; pragma experimental ABIEncoderV2; import { BasicMetaTransaction } from "./../common/BasicMetaTransaction.sol"; -import { CallWithGuards } from "../common/CallWithGuards.sol"; +import { CallWithGuards } from "../common/mixins/CallWithGuards.sol"; import { DSAuth } from "./../../lib/dappsys/auth.sol"; import { ERC20Extended } from "./../common/ERC20Extended.sol"; import { Multicall } from "./../common/Multicall.sol"; diff --git a/contracts/bridging/WormholeBridgeForColony.sol b/contracts/bridging/WormholeBridgeForColony.sol index 1abba39587..eaf03cb7a3 100644 --- a/contracts/bridging/WormholeBridgeForColony.sol +++ b/contracts/bridging/WormholeBridgeForColony.sol @@ -21,7 +21,7 @@ pragma solidity 0.8.28; import { IWormhole } from "../../lib/wormhole/ethereum/contracts/interfaces/IWormhole.sol"; import { IColonyNetwork } from "../colonyNetwork/IColonyNetwork.sol"; import { IColonyBridge } from "./IColonyBridge.sol"; -import { CallWithGuards } from "../common/CallWithGuards.sol"; +import { CallWithGuards } from "../common/mixins/CallWithGuards.sol"; import { DSAuth } from "../../lib/dappsys/auth.sol"; contract WormholeBridgeForColony is DSAuth, IColonyBridge, CallWithGuards { diff --git a/contracts/common/DomainReceiverManagement.sol b/contracts/common/DomainReceiverManagement.sol index 8e5edfd7c6..b907e66855 100644 --- a/contracts/common/DomainReceiverManagement.sol +++ b/contracts/common/DomainReceiverManagement.sol @@ -25,7 +25,7 @@ import { ICreateX } from "./../../lib/createx/src/ICreateX.sol"; import { EtherRouterCreate3 } from "./EtherRouterCreate3.sol"; import { EtherRouter } from "./EtherRouter.sol"; import { Resolver } from "./Resolver.sol"; -import { IsContract } from "./IsContract.sol"; +import { IsContract } from "./mixins/IsContract.sol"; abstract contract DomainReceiverManagement is MetaTransactionMsgSender, IsContract { address constant CREATEX_ADDRESS = 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed; diff --git a/contracts/common/GetActionSummary.sol b/contracts/common/GetActionSummary.sol index 4eb970b9d3..fa42a366d2 100644 --- a/contracts/common/GetActionSummary.sol +++ b/contracts/common/GetActionSummary.sol @@ -16,11 +16,11 @@ */ import { IColony } from "./../colony/IColony.sol"; -import { ExtractCallData } from "./ExtractCallData.sol"; +import { ExtractCallData } from "./mixins/ExtractCallData.sol"; import { GetActionDomainSkillId } from "./GetActionDomainSkillId.sol"; import { ColonyDataTypes } from "./../colony/ColonyDataTypes.sol"; import { ColonyRoles } from "./../colony/ColonyRoles.sol"; -import { Bytes4Includes } from "./Bytes4Includes.sol"; +import { Bytes4Includes } from "./mixins/Bytes4Includes.sol"; pragma solidity 0.8.28; pragma experimental ABIEncoderV2; diff --git a/contracts/common/Bytes4Includes.sol b/contracts/common/mixins/Bytes4Includes.sol similarity index 100% rename from contracts/common/Bytes4Includes.sol rename to contracts/common/mixins/Bytes4Includes.sol diff --git a/contracts/common/CallWithGuards.sol b/contracts/common/mixins/CallWithGuards.sol similarity index 100% rename from contracts/common/CallWithGuards.sol rename to contracts/common/mixins/CallWithGuards.sol diff --git a/contracts/common/ExtractCallData.sol b/contracts/common/mixins/ExtractCallData.sol similarity index 100% rename from contracts/common/ExtractCallData.sol rename to contracts/common/mixins/ExtractCallData.sol diff --git a/contracts/common/IsContract.sol b/contracts/common/mixins/IsContract.sol similarity index 100% rename from contracts/common/IsContract.sol rename to contracts/common/mixins/IsContract.sol diff --git a/contracts/extensions/MultisigPermissions.sol b/contracts/extensions/MultisigPermissions.sol index 3053f10ef6..06fa238ea3 100644 --- a/contracts/extensions/MultisigPermissions.sol +++ b/contracts/extensions/MultisigPermissions.sol @@ -24,7 +24,7 @@ import { IColonyNetwork } from "./../colonyNetwork/IColonyNetwork.sol"; import { ColonyNetworkDataTypes } from "./../colonyNetwork/ColonyNetworkDataTypes.sol"; import { ColonyExtensionMeta } from "./ColonyExtensionMeta.sol"; import { GetActionSummary, ActionSummary } from "./../common/GetActionSummary.sol"; -import { Bytes4Includes } from "./../common/Bytes4Includes.sol"; +import { Bytes4Includes } from "./../common/mixins/Bytes4Includes.sol"; // ignore-file-swc-108 diff --git a/contracts/testHelpers/RequireExecuteCall.sol b/contracts/testHelpers/RequireExecuteCall.sol index 9b8b3eda6d..e5a8ecfa66 100644 --- a/contracts/testHelpers/RequireExecuteCall.sol +++ b/contracts/testHelpers/RequireExecuteCall.sol @@ -19,7 +19,7 @@ pragma solidity 0.8.28; pragma experimental ABIEncoderV2; -import { CallWithGuards } from "./../common/CallWithGuards.sol"; +import { CallWithGuards } from "./../common/mixins/CallWithGuards.sol"; contract RequireExecuteCall is CallWithGuards { function executeCall(address target, bytes memory action) public { From 7d9a491ccfaa471a4d0abd779fb6b9bb3571b1b7 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 22 Jan 2025 11:57:15 +0000 Subject: [PATCH 62/72] Fix spy for chains we don't pay for for dev --- scripts/mockGuardianSpy.ts | 28 ++++++++++++++++++---------- scripts/setup-bridging-contracts.js | 5 +++-- test/cross-chain/cross-chain.js | 1 + 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/scripts/mockGuardianSpy.ts b/scripts/mockGuardianSpy.ts index acf883a840..b205d33170 100644 --- a/scripts/mockGuardianSpy.ts +++ b/scripts/mockGuardianSpy.ts @@ -39,6 +39,8 @@ class MockGuardianSpy { foreignColonyBridgeAddresses: string[]; + spyWaitsForBridgedTransaction: boolean; + homeBridge: Contract; foreignBridges: Contract[]; @@ -80,6 +82,7 @@ class MockGuardianSpy { * @param {string} foreignBridgeAddress The address of the foreign bridge contract * @param {string} homeColonyBridgeAddress The address of the home colony bridge contract * @param {string} foreignColonyBridgeAddress The address of the foreign colony bridge contract + * @param {string} spyWaitsForBridgedTransaction Whether the spy waits for the bridging transaction to be mined */ constructor( homeRpc: string, @@ -88,6 +91,7 @@ class MockGuardianSpy { foreignBridgeAddresses: string[], homeColonyBridgeAddress: string, foreignColonyBridgeAddresses: string[], + spyWaitsForBridgedTransaction: boolean, ) { this.homeRpc = homeRpc; this.foreignRpcs = foreignRpcs; @@ -95,6 +99,7 @@ class MockGuardianSpy { this.foreignBridgeAddresses = foreignBridgeAddresses; this.homeColonyBridgeAddress = homeColonyBridgeAddress; this.foreignColonyBridgeAddresses = foreignColonyBridgeAddresses; + this.spyWaitsForBridgedTransaction = spyWaitsForBridgedTransaction; this.setupListeners(); @@ -287,13 +292,14 @@ class MockGuardianSpy { const relayerNonce = await bridge.provider.getTransactionCount(this.relayerAddress, "pending"); this.subscription.write({ vaaBytes: Buffer.from(vaa.slice(2), "hex") }); + if (this.spyWaitsForBridgedTransaction) { + let newRelayerNonce = -1; + while (newRelayerNonce <= relayerNonce) { + newRelayerNonce = await bridge.provider.getTransactionCount(this.relayerAddress, "pending"); + } - let newRelayerNonce = -1; - while (newRelayerNonce <= relayerNonce) { - newRelayerNonce = await bridge.provider.getTransactionCount(this.relayerAddress, "pending"); + tx = await MockGuardianSpy.getTransactionFromAddressWithNonce(bridge.provider, this.relayerAddress, relayerNonce); } - - tx = await MockGuardianSpy.getTransactionFromAddressWithNonce(bridge.provider, this.relayerAddress, relayerNonce); } else { console.log("not sending, didnt pass filter"); } @@ -328,12 +334,14 @@ class MockGuardianSpy { const relayerNonce = await bridge.provider.getTransactionCount(this.relayerAddress, "pending"); this.subscription.write({ vaaBytes: Buffer.from(vaa.slice(2), "hex") }); - let newRelayerNonce = -1; - while (newRelayerNonce <= relayerNonce) { - newRelayerNonce = await bridge.provider.getTransactionCount(this.relayerAddress, "pending"); - } + if (this.spyWaitsForBridgedTransaction) { + let newRelayerNonce = -1; + while (newRelayerNonce <= relayerNonce) { + newRelayerNonce = await bridge.provider.getTransactionCount(this.relayerAddress, "pending"); + } - tx = await MockGuardianSpy.getTransactionFromAddressWithNonce(bridge.provider, this.relayerAddress, relayerNonce); + tx = await MockGuardianSpy.getTransactionFromAddressWithNonce(bridge.provider, this.relayerAddress, relayerNonce); + } } this.bridgingPromiseCount -= 1; diff --git a/scripts/setup-bridging-contracts.js b/scripts/setup-bridging-contracts.js index 804e025688..6d9f9a6be5 100644 --- a/scripts/setup-bridging-contracts.js +++ b/scripts/setup-bridging-contracts.js @@ -19,7 +19,7 @@ const loader = new TruffleLoader({ const ADDRESS_ZERO = ethers.constants.AddressZero; const MockGuardianSpy = require("./mockGuardianSpy").default; -async function setupBridging(homeRpcUrl, foreignRpcUrls) { +async function setupBridging(homeRpcUrl, foreignRpcUrls, spyWaitsForBridgedTransaction) { console.log("setup-bridging-contracts: Not to be used in production"); if (process.env.NODE_ENV === "production") { process.exit(1); @@ -167,6 +167,7 @@ async function setupBridging(homeRpcUrl, foreignRpcUrls) { foreignBridgeAddresses, homeColonyBridge.address, remoteColonyBridgeAddresses, + spyWaitsForBridgedTransaction, ); // eslint-disable-line no-unused-vars // TODO: Start the bridge monitor @@ -369,7 +370,7 @@ async function deployBridge(signer) { } if (process.argv.includes("start-bridging-environment")) { - setupBridging("http://127.0.0.1:8545", "http://127.0.0.1:8546"); + setupBridging("http://127.0.0.1:8545", "http://127.0.0.1:8546", true); } module.exports = { setupBridging, deployBridge, setHomeBridgeData, setForeignBridgeData }; diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index a5717fe428..8066722834 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -133,6 +133,7 @@ contract("Cross-chain", (accounts) => { ({ guardianSpy, resetRelayer, gnosisSafe, zodiacBridge, homeBridge, foreignBridge, remoteColonyBridge, homeColonyBridge } = await setupBridging( homeRpcUrl, [foreignRpcUrl], + true, )); // Add bridge to the foreign colony network From 4b79937a559d540fb30f47f1678287bb8dd70506 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Thu, 23 Jan 2025 15:14:09 +0000 Subject: [PATCH 63/72] Track nonRewardPotTotals on proxy chains --- .../contracts/colony/Colony.sol:Colony.json | 31 ++++++++ ...action.sol:ColonyArbitraryTransaction.json | 31 ++++++++ .../ColonyDomains.sol:ColonyDomains.json | 31 ++++++++ ...lonyExpenditure.sol:ColonyExpenditure.json | 31 ++++++++ .../ColonyFunding.sol:ColonyFunding.json | 31 ++++++++ .../ColonyRewards.sol:ColonyRewards.json | 31 ++++++++ .../colony/ColonyRoles.sol:ColonyRoles.json | 31 ++++++++ .../ColonyStorage.sol:ColonyStorage.json | 31 ++++++++ ...ony.sol:FunctionsNotAvailableOnColony.json | 31 ++++++++ ...LimitSubdomains.sol:NoLimitSubdomains.json | 31 ++++++++ contracts/colony/ColonyFunding.sol | 67 +++++++++++----- contracts/colony/ColonyStorage.sol | 3 + contracts/colony/IColony.sol | 34 +++++--- docs/interfaces/icolony.md | 49 ++++++++---- docs/interfaces/imetacolony.md | 49 ++++++++---- helpers/constants.js | 5 ++ helpers/test-helper.js | 7 +- test/cross-chain/cross-chain.js | 79 ++++++++++++++++--- 18 files changed, 530 insertions(+), 73 deletions(-) diff --git a/.storage-layouts-normalized/contracts/colony/Colony.sol:Colony.json b/.storage-layouts-normalized/contracts/colony/Colony.sol:Colony.json index d28f85c94b..908d8f0c2d 100644 --- a/.storage-layouts-normalized/contracts/colony/Colony.sol:Colony.json +++ b/.storage-layouts-normalized/contracts/colony/Colony.sol:Colony.json @@ -1543,6 +1543,37 @@ } } } + }, + { + "contract": "contracts/colony/Colony.sol:Colony", + "label": "chainNonRewardPotsTotals", + "offset": 0, + "slot": "41", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyArbitraryTransaction.sol:ColonyArbitraryTransaction.json b/.storage-layouts-normalized/contracts/colony/ColonyArbitraryTransaction.sol:ColonyArbitraryTransaction.json index 79facb58b5..ae97490c36 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyArbitraryTransaction.sol:ColonyArbitraryTransaction.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyArbitraryTransaction.sol:ColonyArbitraryTransaction.json @@ -1543,6 +1543,37 @@ } } } + }, + { + "contract": "contracts/colony/ColonyArbitraryTransaction.sol:ColonyArbitraryTransaction", + "label": "chainNonRewardPotsTotals", + "offset": 0, + "slot": "41", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyDomains.sol:ColonyDomains.json b/.storage-layouts-normalized/contracts/colony/ColonyDomains.sol:ColonyDomains.json index aac0c5e7e1..2e9c3eab58 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyDomains.sol:ColonyDomains.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyDomains.sol:ColonyDomains.json @@ -1543,6 +1543,37 @@ } } } + }, + { + "contract": "contracts/colony/ColonyDomains.sol:ColonyDomains", + "label": "chainNonRewardPotsTotals", + "offset": 0, + "slot": "41", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyExpenditure.sol:ColonyExpenditure.json b/.storage-layouts-normalized/contracts/colony/ColonyExpenditure.sol:ColonyExpenditure.json index b656fd99dc..cefb3cc1e8 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyExpenditure.sol:ColonyExpenditure.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyExpenditure.sol:ColonyExpenditure.json @@ -1543,6 +1543,37 @@ } } } + }, + { + "contract": "contracts/colony/ColonyExpenditure.sol:ColonyExpenditure", + "label": "chainNonRewardPotsTotals", + "offset": 0, + "slot": "41", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyFunding.sol:ColonyFunding.json b/.storage-layouts-normalized/contracts/colony/ColonyFunding.sol:ColonyFunding.json index 1f874847ef..1ee5a9a68c 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyFunding.sol:ColonyFunding.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyFunding.sol:ColonyFunding.json @@ -1543,6 +1543,37 @@ } } } + }, + { + "contract": "contracts/colony/ColonyFunding.sol:ColonyFunding", + "label": "chainNonRewardPotsTotals", + "offset": 0, + "slot": "41", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyRewards.sol:ColonyRewards.json b/.storage-layouts-normalized/contracts/colony/ColonyRewards.sol:ColonyRewards.json index cf26a01c95..f20d4814fe 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyRewards.sol:ColonyRewards.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyRewards.sol:ColonyRewards.json @@ -1543,6 +1543,37 @@ } } } + }, + { + "contract": "contracts/colony/ColonyRewards.sol:ColonyRewards", + "label": "chainNonRewardPotsTotals", + "offset": 0, + "slot": "41", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyRoles.sol:ColonyRoles.json b/.storage-layouts-normalized/contracts/colony/ColonyRoles.sol:ColonyRoles.json index d25dd781d8..e9a38d86ee 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyRoles.sol:ColonyRoles.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyRoles.sol:ColonyRoles.json @@ -1543,6 +1543,37 @@ } } } + }, + { + "contract": "contracts/colony/ColonyRoles.sol:ColonyRoles", + "label": "chainNonRewardPotsTotals", + "offset": 0, + "slot": "41", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/colony/ColonyStorage.sol:ColonyStorage.json b/.storage-layouts-normalized/contracts/colony/ColonyStorage.sol:ColonyStorage.json index ac5f587879..449b9e4a62 100644 --- a/.storage-layouts-normalized/contracts/colony/ColonyStorage.sol:ColonyStorage.json +++ b/.storage-layouts-normalized/contracts/colony/ColonyStorage.sol:ColonyStorage.json @@ -1543,6 +1543,37 @@ } } } + }, + { + "contract": "contracts/colony/ColonyStorage.sol:ColonyStorage", + "label": "chainNonRewardPotsTotals", + "offset": 0, + "slot": "41", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/testHelpers/FunctionsNotAvailableOnColony.sol:FunctionsNotAvailableOnColony.json b/.storage-layouts-normalized/contracts/testHelpers/FunctionsNotAvailableOnColony.sol:FunctionsNotAvailableOnColony.json index 52b584f005..3afcf2daa8 100644 --- a/.storage-layouts-normalized/contracts/testHelpers/FunctionsNotAvailableOnColony.sol:FunctionsNotAvailableOnColony.json +++ b/.storage-layouts-normalized/contracts/testHelpers/FunctionsNotAvailableOnColony.sol:FunctionsNotAvailableOnColony.json @@ -1543,6 +1543,37 @@ } } } + }, + { + "contract": "contracts/testHelpers/FunctionsNotAvailableOnColony.sol:FunctionsNotAvailableOnColony", + "label": "chainNonRewardPotsTotals", + "offset": 0, + "slot": "41", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ] } \ No newline at end of file diff --git a/.storage-layouts-normalized/contracts/testHelpers/NoLimitSubdomains.sol:NoLimitSubdomains.json b/.storage-layouts-normalized/contracts/testHelpers/NoLimitSubdomains.sol:NoLimitSubdomains.json index 74dd6e94ed..c60f47d217 100644 --- a/.storage-layouts-normalized/contracts/testHelpers/NoLimitSubdomains.sol:NoLimitSubdomains.json +++ b/.storage-layouts-normalized/contracts/testHelpers/NoLimitSubdomains.sol:NoLimitSubdomains.json @@ -1543,6 +1543,37 @@ } } } + }, + { + "contract": "contracts/testHelpers/NoLimitSubdomains.sol:NoLimitSubdomains", + "label": "chainNonRewardPotsTotals", + "offset": 0, + "slot": "41", + "type": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + }, + "label": "mapping(uint256 => mapping(address => uint256))", + "numberOfBytes": "32", + "value": { + "encoding": "mapping", + "key": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "label": "mapping(address => uint256)", + "numberOfBytes": "32", + "value": { + "encoding": "inplace", + "label": "uint256", + "numberOfBytes": "32" + } + } + } } ] } \ No newline at end of file diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index 10e9eef116..e692b69be1 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -129,7 +129,8 @@ contract ColonyFunding is // to 0 via recovery mode, but a) That's not why MythX is balking here and b) There's only so much we can stop people being // able to do with recovery mode. remainder = toClaim - feeToPay; - nonRewardPotsTotal[_token] += remainder; + incrementNonRewardPotsTotal(block.chainid, _token, remainder); + // nonRewardPotsTotal[_token] += remainder; incrementFundingPotBalance(0, block.chainid, _token, feeToPay); incrementFundingPotBalance(1, block.chainid, _token, remainder); @@ -154,7 +155,8 @@ contract ColonyFunding is // to 0 via recovery mode, but a) That's not why MythX is balking here and b) There's only so much we can stop people being // able to do with recovery mode. uint256 remainder = claimAmount - feeToPay; - nonRewardPotsTotal[_token] += remainder; + incrementNonRewardPotsTotal(block.chainid, _token, remainder); + // nonRewardPotsTotal[_token] += remainder; // fundingPots[0].balance[_token] += feeToPay; incrementFundingPotBalance(0, block.chainid, _token, feeToPay); @@ -315,7 +317,39 @@ contract ColonyFunding is } function getNonRewardPotsTotal(address _token) public view returns (uint256) { - return nonRewardPotsTotal[_token]; + return getNonRewardPotsTotal(block.chainid, _token); + } + + function getNonRewardPotsTotal(uint256 _chainId, address _token) public view returns (uint256) { + if (_chainId == block.chainid) { + return nonRewardPotsTotal[_token]; + } else { + return chainNonRewardPotsTotals[_chainId][_token]; + } + } + + function incrementNonRewardPotsTotal( + uint256 _chainId, + address _token, + uint256 _increment + ) internal { + if (_chainId == block.chainid) { + nonRewardPotsTotal[_token] += _increment; + } else { + chainNonRewardPotsTotals[_chainId][_token] += _increment; + } + } + + function decrementNonRewardPotsTotal( + uint256 _chainId, + address _token, + uint256 _decrement + ) internal { + if (_chainId == block.chainid) { + nonRewardPotsTotal[_token] -= _decrement; + } else { + chainNonRewardPotsTotals[_chainId][_token] -= _decrement; + } } /// @notice For owners to update payouts with one token and many slots @@ -494,11 +528,14 @@ contract ColonyFunding is return getFundingPotBalance(_potId, block.chainid, _token); } - function getFundingPotProxyBalance( + function getFundingPotBalance( uint256 _potId, uint256 _chainId, address _token ) public view returns (uint256) { + if (_chainId == block.chainid) { + return fundingPots[_potId].balance[_token]; + } return fundingPots[_potId].chainBalances[_chainId][_token]; } @@ -510,6 +547,9 @@ contract ColonyFunding is ) public stoppable { Domain storage d = domains[_domainId]; fundingPots[d.fundingPotId].chainBalances[_chainId][_token] += _amount; + // TODO: Reward pot? + + incrementNonRewardPotsTotal(_chainId, _token, _amount); emit ProxyColonyFundsClaimed(_chainId, _token, _amount); } @@ -600,7 +640,7 @@ contract ColonyFunding is } if (_toPot == 0) { - nonRewardPotsTotal[_token] -= _amount; + decrementNonRewardPotsTotal(_chainId, _token, _amount); } emit ColonyFundsMovedBetweenFundingPots(msgSender(), _fromPot, _toPot, _amount, _token); @@ -720,10 +760,9 @@ contract ColonyFunding is ); uint256 payoutToUser; + decrementNonRewardPotsTotal(_chainId, _token, _payout); if (_chainId == block.chainid) { - nonRewardPotsTotal[_token] -= _payout; - uint256 fee = isOwnExtension(_user) ? 0 : calculateNetworkFeeForPayout(_payout); payoutToUser = _payout - fee; @@ -745,7 +784,7 @@ contract ColonyFunding is } } } else { - // TODO: Shell colony payout + // TODO: Network fee bytes memory payload = abi.encodeWithSignature( "transferFromBridge(address,address,uint256)", _token, @@ -753,6 +792,7 @@ contract ColonyFunding is _payout ); IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, payload); + payoutToUser = _payout; } // slither-disable-next-line reentrancy-unlimited-gas @@ -780,17 +820,6 @@ contract ColonyFunding is } } - function getFundingPotBalance( - uint256 _potId, - uint256 _chainId, - address _token - ) internal view returns (uint256) { - if (_chainId == block.chainid) { - return fundingPots[_potId].balance[_token]; - } - return fundingPots[_potId].chainBalances[_chainId][_token]; - } - function incrementFundingPotBalance( uint256 _potId, uint256 _chainId, diff --git a/contracts/colony/ColonyStorage.sol b/contracts/colony/ColonyStorage.sol index 76799c1181..83ee502936 100755 --- a/contracts/colony/ColonyStorage.sol +++ b/contracts/colony/ColonyStorage.sol @@ -119,6 +119,9 @@ contract ColonyStorage is ColonyDataTypes, DSMath, CommonStorage { // Expenditure Id > Slot Id > Chain Id > Token Address > Amount mapping(uint256 => mapping(uint256 => mapping(uint256 => mapping(address => uint256)))) expenditureSlotChainPayouts; // Storage slot 40 + // Chain Id > Token Address > Amount + mapping(uint256 => mapping(address => uint256)) chainNonRewardPotsTotals; // Storage slot 41 + // Constants uint256 constant MAX_PAYOUT = 2 ** 128 - 1; // 340,282,366,920,938,463,463 WADs diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 9f37a8a348..72a8907803 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -832,6 +832,18 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, /// @notice Get the `_token` balance of pot with id `_potId`. /// @param _potId Id of the funding pot + /// @param _chainId The chainId of the token + /// @param _token Address of the token, `0x0` value indicates Ether + /// @return balance Funding pot supply balance + function getFundingPotBalance( + uint256 _potId, + uint256 _chainId, + address _token + ) external view returns (uint256 balance); + + /// @notice Get the `_token` balance of pot with id `_potId`. + /// @notice Deprecated - use version with explicit chainId + /// @param _potId Id of the funding pot /// @param _token Address of the token, `0x0` value indicates Ether /// @return balance Funding pot supply balance function getFundingPotBalance( @@ -974,27 +986,27 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, uint256 _amount ) external; - /// @notice Get the balance of a funding pot for a specific token on a specific chain - /// @param _potId Id of the funding pot - /// @param _chainId Chain id of the token - /// @param _token Address of the token, `0x0` value indicates Ether - /// @return balance Balance of the funding pot - function getFundingPotProxyBalance( - uint256 _potId, - uint256 _chainId, - address _token - ) external view returns (uint256 balance); - /// @notice Create a proxy colony on another chain /// @param _destinationChainId Chain id of the destination chain /// @param _salt The colony creation salt that was used on creation of the colony function createProxyColony(uint256 _destinationChainId, bytes32 _salt) external; + /// @notice Deprecated. Use the version with explicit _chainId /// @notice Get the total amount of tokens `_token` minus amount reserved to be paid to the reputation and token holders as rewards. /// @param _token Address of the token, `0x0` value indicates Ether /// @return amount Total amount of tokens in funding pots other than the rewards pot (id 0) function getNonRewardPotsTotal(address _token) external view returns (uint256 amount); + /// @notice Get the total amount of tokens `_token` minus amount reserved to be paid to the reputation and token holders as rewards. + /// @param _chainId Chain id to query the total for + /// @param _token Address of the token, `0x0` value indicates Ether + /// @return amount Total amount of tokens in funding pots other than the rewards pot (id 0) + /// @dev NB This only returns totals that the colony knows about - unclaimed funds will not be included + function getNonRewardPotsTotal( + uint256 _chainId, + address _token + ) external view returns (uint256 amount); + /// @notice Allow the _approvee to obligate some amount of tokens as a stake. /// @param _approvee Address of the account we are willing to let obligate us. /// @param _domainId Domain in which we are willing to be obligated. diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index 063cf1f72f..6355d41a1f 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -712,6 +712,24 @@ Get the non-mapping properties of a pot by id. ### ▸ `getFundingPotBalance(uint256 _potId, address _token):uint256 balance` +Deprecated - use version with explicit chainId + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_potId|uint256|Id of the funding pot +|_token|address|Address of the token, `0x0` value indicates Ether + +**Return Parameters** + +|Name|Type|Description| +|---|---|---| +|balance|uint256|Funding pot supply balance + +### ▸ `getFundingPotBalance(uint256 _potId, uint256 _chainId, address _token):uint256 balance` + Get the `_token` balance of pot with id `_potId`. @@ -720,6 +738,7 @@ Get the `_token` balance of pot with id `_potId`. |Name|Type|Description| |---|---|---| |_potId|uint256|Id of the funding pot +|_chainId|uint256|The chainId of the token |_token|address|Address of the token, `0x0` value indicates Ether **Return Parameters** @@ -758,68 +777,68 @@ Get the assigned `_token` payouts of pot with id `_potId`. |---|---|---| |payout|uint256|Funding pot payout amount -### ▸ `getFundingPotProxyBalance(uint256 _potId, uint256 _chainId, address _token):uint256 balance` +### ▸ `getLocalSkill(uint256 localSkillId):LocalSkill localSkill` -Get the balance of a funding pot for a specific token on a specific chain +Get the local skill **Parameters** |Name|Type|Description| |---|---|---| -|_potId|uint256|Id of the funding pot -|_chainId|uint256|Chain id of the token -|_token|address|Address of the token, `0x0` value indicates Ether +|localSkillId|uint256|Id for the local skill **Return Parameters** |Name|Type|Description| |---|---|---| -|balance|uint256|Balance of the funding pot +|localSkill|LocalSkill|The local skill -### ▸ `getLocalSkill(uint256 localSkillId):LocalSkill localSkill` +### ▸ `getMetatransactionNonce(address userAddress):uint256 nonce` -Get the local skill +Gets the next metatransaction nonce for user that should be used targeting this contract **Parameters** |Name|Type|Description| |---|---|---| -|localSkillId|uint256|Id for the local skill +|userAddress|address|The address of the user that will sign the metatransaction **Return Parameters** |Name|Type|Description| |---|---|---| -|localSkill|LocalSkill|The local skill +|nonce|uint256|The nonce that should be used for the next metatransaction -### ▸ `getMetatransactionNonce(address userAddress):uint256 nonce` +### ▸ `getNonRewardPotsTotal(address _token):uint256 amount` -Gets the next metatransaction nonce for user that should be used targeting this contract +Get the total amount of tokens `_token` minus amount reserved to be paid to the reputation and token holders as rewards. **Parameters** |Name|Type|Description| |---|---|---| -|userAddress|address|The address of the user that will sign the metatransaction +|_token|address|Address of the token, `0x0` value indicates Ether **Return Parameters** |Name|Type|Description| |---|---|---| -|nonce|uint256|The nonce that should be used for the next metatransaction +|amount|uint256|Total amount of tokens in funding pots other than the rewards pot (id 0) -### ▸ `getNonRewardPotsTotal(address _token):uint256 amount` +### ▸ `getNonRewardPotsTotal(uint256 _chainId, address _token):uint256 amount` Get the total amount of tokens `_token` minus amount reserved to be paid to the reputation and token holders as rewards. +*Note: NB This only returns totals that the colony knows about - unclaimed funds will not be included* **Parameters** |Name|Type|Description| |---|---|---| +|_chainId|uint256|Chain id to query the total for |_token|address|Address of the token, `0x0` value indicates Ether **Return Parameters** diff --git a/docs/interfaces/imetacolony.md b/docs/interfaces/imetacolony.md index a7142c2076..3ec33dd60d 100644 --- a/docs/interfaces/imetacolony.md +++ b/docs/interfaces/imetacolony.md @@ -735,6 +735,24 @@ Get the non-mapping properties of a pot by id. ### ▸ `getFundingPotBalance(uint256 _potId, address _token):uint256 balance` +Deprecated - use version with explicit chainId + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_potId|uint256|Id of the funding pot +|_token|address|Address of the token, `0x0` value indicates Ether + +**Return Parameters** + +|Name|Type|Description| +|---|---|---| +|balance|uint256|Funding pot supply balance + +### ▸ `getFundingPotBalance(uint256 _potId, uint256 _chainId, address _token):uint256 balance` + Get the `_token` balance of pot with id `_potId`. @@ -743,6 +761,7 @@ Get the `_token` balance of pot with id `_potId`. |Name|Type|Description| |---|---|---| |_potId|uint256|Id of the funding pot +|_chainId|uint256|The chainId of the token |_token|address|Address of the token, `0x0` value indicates Ether **Return Parameters** @@ -781,68 +800,68 @@ Get the assigned `_token` payouts of pot with id `_potId`. |---|---|---| |payout|uint256|Funding pot payout amount -### ▸ `getFundingPotProxyBalance(uint256 _potId, uint256 _chainId, address _token):uint256 balance` +### ▸ `getLocalSkill(uint256 localSkillId):LocalSkill localSkill` -Get the balance of a funding pot for a specific token on a specific chain +Get the local skill **Parameters** |Name|Type|Description| |---|---|---| -|_potId|uint256|Id of the funding pot -|_chainId|uint256|Chain id of the token -|_token|address|Address of the token, `0x0` value indicates Ether +|localSkillId|uint256|Id for the local skill **Return Parameters** |Name|Type|Description| |---|---|---| -|balance|uint256|Balance of the funding pot +|localSkill|LocalSkill|The local skill -### ▸ `getLocalSkill(uint256 localSkillId):LocalSkill localSkill` +### ▸ `getMetatransactionNonce(address userAddress):uint256 nonce` -Get the local skill +Gets the next metatransaction nonce for user that should be used targeting this contract **Parameters** |Name|Type|Description| |---|---|---| -|localSkillId|uint256|Id for the local skill +|userAddress|address|The address of the user that will sign the metatransaction **Return Parameters** |Name|Type|Description| |---|---|---| -|localSkill|LocalSkill|The local skill +|nonce|uint256|The nonce that should be used for the next metatransaction -### ▸ `getMetatransactionNonce(address userAddress):uint256 nonce` +### ▸ `getNonRewardPotsTotal(address _token):uint256 amount` -Gets the next metatransaction nonce for user that should be used targeting this contract +Get the total amount of tokens `_token` minus amount reserved to be paid to the reputation and token holders as rewards. **Parameters** |Name|Type|Description| |---|---|---| -|userAddress|address|The address of the user that will sign the metatransaction +|_token|address|Address of the token, `0x0` value indicates Ether **Return Parameters** |Name|Type|Description| |---|---|---| -|nonce|uint256|The nonce that should be used for the next metatransaction +|amount|uint256|Total amount of tokens in funding pots other than the rewards pot (id 0) -### ▸ `getNonRewardPotsTotal(address _token):uint256 amount` +### ▸ `getNonRewardPotsTotal(uint256 _chainId, address _token):uint256 amount` Get the total amount of tokens `_token` minus amount reserved to be paid to the reputation and token holders as rewards. +*Note: NB This only returns totals that the colony knows about - unclaimed funds will not be included* **Parameters** |Name|Type|Description| |---|---|---| +|_chainId|uint256|Chain id to query the total for |_token|address|Address of the token, `0x0` value indicates Ether **Return Parameters** diff --git a/helpers/constants.js b/helpers/constants.js index 279e1a4bda..a5b0f12647 100644 --- a/helpers/constants.js +++ b/helpers/constants.js @@ -85,6 +85,10 @@ const CREATEX_ADDRESS = "0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed"; const LIFI_ADDRESS = "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE"; const NETWORK_ADDRESS = "0x777760996135F0791E2e1a74aFAa060711197777"; +const PANIC = { + ARITHMETHIC_OVERFLOW: 17, +}; + module.exports = { UINT256_MAX, UINT128_MAX, @@ -144,4 +148,5 @@ module.exports = { CREATEX_ADDRESS, NETWORK_ADDRESS, LIFI_ADDRESS, + PANIC, }; diff --git a/helpers/test-helper.js b/helpers/test-helper.js index b40e975378..b4a9bcb512 100644 --- a/helpers/test-helper.js +++ b/helpers/test-helper.js @@ -272,7 +272,12 @@ exports.checkErrorRevertEthers = async function checkErrorRevertEthers(promise, }, receipt.blockNumber, ); - reason = web3.eth.abi.decodeParameter("string", callResult.slice(10)); + console.log("callResult", callResult); + if (typeof errorMessage === "number") { + reason = parseInt(callResult.slice(10), 16); + } else { + reason = web3.eth.abi.decodeParameter("string", callResult.slice(10)); + } } catch (err2) { reason = web3.eth.abi.decodeParameter("string", err2.error.error.data.slice(10)); } diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 8066722834..21326e554d 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -49,6 +49,7 @@ const { ARBITRATION_ROLE, FUNDING_ROLE, ADMINISTRATION_ROLE, + PANIC, } = require("../../helpers/constants"); const { forwardTime, checkErrorRevertEthers, revert, snapshot, evmChainIdToWormholeChainId, rolesToBytes32 } = require("../../helpers/test-helper"); const ReputationMinerTestWrapper = require("../../packages/reputation-miner/test/ReputationMinerTestWrapper"); @@ -564,8 +565,62 @@ contract("Cross-chain", (accounts) => { // Check bookkeeping on the home chain - const balance = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + const balance = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, foreignToken.address); expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); + + const nonRewardPotTotal = await colony["getNonRewardPotsTotal(uint256,address)"](foreignChainId, foreignToken.address); + expect(nonRewardPotTotal.toHexString()).to.equal(tokenAmount.toHexString()); + }); + + it("Reward payout pot is respected even cross-chain", async () => { + const tokenAmount = ethers.utils.parseEther("100"); + + // Mint tokens to proxy colony on foreign chain + let tx = await foreignToken["mint(address,uint256)"](proxyColony.address, tokenAmount); + await tx.wait(); + + // Claim tokens on foreign chain + const p = guardianSpy.getPromiseForNextBridgedTransaction(); + tx = await proxyColony.claimTokens(foreignToken.address); + await tx.wait(); + await p; + + // Move 30 tokens in to the reward pot 0 + await ( + await colony["moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)"]( + 1, + UINT256_MAX_ETHERS, + 1, + UINT256_MAX_ETHERS, + UINT256_MAX_ETHERS, + 1, + 0, + ethers.utils.parseEther("30"), + foreignChainId, + foreignToken.address, + ) + ).wait(); + + await colony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); + + // Try and move tokens from root domain that are in the reward pot + const domain1 = await colony.getDomain(1); + const domain2 = await colony.getDomain(2); + + tx = await colony["moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)"]( + 1, + UINT256_MAX_ETHERS, + 1, + UINT256_MAX_ETHERS, + 0, + domain1.fundingPotId, + domain2.fundingPotId, + ethers.utils.parseEther("80"), + foreignChainId, + foreignToken.address, + { gasLimit: 1000000 }, + ); + await checkErrorRevertEthers(tx.wait(), PANIC.ARITHMETHIC_OVERFLOW); }); it("Can claim tokens received on foreign chain via cross-chain request", async () => { @@ -585,7 +640,7 @@ contract("Cross-chain", (accounts) => { // Check bookkeeping on the home chain - const balance = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + const balance = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, foreignToken.address); expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); }); @@ -609,7 +664,7 @@ contract("Cross-chain", (accounts) => { await p; // Check bookkeeping on the home chain - const balance = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + const balance = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, foreignToken.address); expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); // Check nonce incremented @@ -636,7 +691,7 @@ contract("Cross-chain", (accounts) => { // Check bookkeeping on the home chain - const balance = await colony.getFundingPotProxyBalance(1, foreignChainId, ADDRESS_ZERO); + const balance = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, ADDRESS_ZERO); expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); }); @@ -698,7 +753,7 @@ contract("Cross-chain", (accounts) => { await p; // Check bookkeeping on the home chain - const balance1 = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + const balance1 = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, foreignToken.address); expect(balance1.toHexString()).to.equal(ethers.utils.parseEther("70").toHexString()); // Check actually paid on foreign chain @@ -772,7 +827,7 @@ contract("Cross-chain", (accounts) => { await p; // Check bookkeeping on the home chain - const balance1 = await colony.getFundingPotProxyBalance(1, foreignChainId, ADDRESS_ZERO); + const balance1 = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, ADDRESS_ZERO); expect(balance1.toHexString()).to.equal(ethers.utils.parseEther("0.7").toHexString()); // Check actually paid on foreign chain @@ -890,7 +945,7 @@ contract("Cross-chain", (accounts) => { // See if bookkeeping was tracked correctly const domain = await colony.getDomain(2); - const balance = await colony.getFundingPotProxyBalance(domain.fundingPotId, foreignChainId, foreignToken.address); + const balance = await colony["getFundingPotBalance(uint256,uint256,address)"](domain.fundingPotId, foreignChainId, foreignToken.address); expect(balance.toHexString()).to.equal(ethers.utils.parseEther("50").toHexString()); }); @@ -909,7 +964,7 @@ contract("Cross-chain", (accounts) => { await p; // Check bookkeeping on the home chain - const balance = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + const balance = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, foreignToken.address); expect(balance.toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); // Move tokens from domain 1 to domain 2 @@ -976,8 +1031,8 @@ contract("Cross-chain", (accounts) => { await p; // Check bookkeeping on the home chain - const balance1 = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); - const balance2 = await colony.getFundingPotProxyBalance(2, foreignChainId, foreignToken2.address); + const balance1 = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, foreignToken.address); + const balance2 = await colony["getFundingPotBalance(uint256,uint256,address)"](2, foreignChainId, foreignToken2.address); expect(balance1.toHexString()).to.equal(ethers.utils.parseEther("30").toHexString()); expect(balance2.toHexString()).to.equal(ethers.utils.parseEther("70").toHexString()); @@ -1054,7 +1109,7 @@ contract("Cross-chain", (accounts) => { it("can make multiple arbitrary transactions on the foreign chain in one go", async () => { const shellBalanceBefore = await foreignToken.balanceOf(proxyColony.address); - const colonyBalanceBefore = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + const colonyBalanceBefore = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, foreignToken.address); const p = guardianSpy.getPromiseForNextBridgedTransaction(2); @@ -1073,7 +1128,7 @@ contract("Cross-chain", (accounts) => { // Check that the second transaction was successful - const colonyBalanceAfter = await colony.getFundingPotProxyBalance(1, foreignChainId, foreignToken.address); + const colonyBalanceAfter = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, foreignToken.address); expect(colonyBalanceAfter.sub(colonyBalanceBefore).toHexString()).to.equal(ethers.utils.parseEther("100").toHexString()); }); From dabb45f0822091ea9161d3927c307c31ed709e6a Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 24 Jan 2025 11:05:42 +0000 Subject: [PATCH 64/72] claimTokens -> claimColonyFunds for consistency --- contracts/bridging/ProxyColony.sol | 4 +-- contracts/colony/ColonyFunding.sol | 9 +++++ contracts/colony/IColony.sol | 7 ++++ docs/interfaces/icolony.md | 13 +++++++ docs/interfaces/imetacolony.md | 13 +++++++ test/cross-chain/cross-chain.js | 54 ++++++++++++++++++++---------- 6 files changed, 81 insertions(+), 19 deletions(-) diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index ed66ae8bb7..1d22813216 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -49,7 +49,7 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards, BasicMetaTransaction // Public functions - function claimTokens(address _token) public { + function claimColonyFunds(address _token) public { uint256 balance = (_token == address(0x0)) ? address(this).balance : ERC20Extended(_token).balanceOf(address(this)); @@ -71,7 +71,7 @@ contract ProxyColony is DSAuth, Multicall, CallWithGuards, BasicMetaTransaction emit DomainFundsClaimed(_token, 1, balance); } - function claimTokensForDomain(address _token, uint256 _domainId) public { + function claimDomainFunds(address _token, uint256 _domainId) public { address domainTokenReceiverAddress = ProxyColonyNetwork(owner) .idempotentDeployDomainTokenReceiver(_domainId); diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index e692b69be1..631d07e3d9 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -107,6 +107,15 @@ contract ColonyFunding is moveFundsBetweenPotsFunctionality(_fromPot, _toPot, _amount, block.chainid, _token); } + function claimColonyFunds(uint256 _chainId, address _token) public stoppable { + if (_chainId == block.chainid) { + claimColonyFunds(_token); + } else { + bytes memory payload = abi.encodeWithSignature("claimColonyFunds(address)", _token); + IColony(address(this)).makeProxyArbitraryTransaction(_chainId, address(this), payload); + } + } + function claimColonyFunds(address _token) public stoppable { uint256 toClaim; uint256 feeToPay; diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 72a8907803..d26f9d438b 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -927,6 +927,13 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, address _token ) external; + /// @notice Move any funds received by the colony in `_token` denomination to the top-level domain pot, + /// siphoning off a small amount to the reward pot. If called against a colony's own token, no fee is taken. + /// @param _chainId Chain id of the chain where the funds need to be claimed + /// @param _token Address of the token, `0x0` value indicates Ether + function claimColonyFunds(uint256 _chainId, address _token) external; + + /// @notice Deprecated /// @notice Move any funds received by the colony in `_token` denomination to the top-level domain pot, /// siphoning off a small amount to the reward pot. If called against a colony's own token, no fee is taken. /// @param _token Address of the token, `0x0` value indicates Ether diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index 6355d41a1f..eb91f8c135 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -174,6 +174,19 @@ Move any funds received by the colony in `_token` denomination to the top-level |_token|address|Address of the token, `0x0` value indicates Ether +### ▸ `claimColonyFunds(uint256 _chainId, address _token)` + +Move any funds received by the colony in `_token` denomination to the top-level domain pot, siphoning off a small amount to the reward pot. If called against a colony's own token, no fee is taken. + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_chainId|uint256|Chain id of the chain where the funds need to be claimed +|_token|address|Address of the token, `0x0` value indicates Ether + + ### ▸ `claimDomainFunds(address _token, uint256 _domainId)` Move any funds received by the colony for a specific domain to that domain's pot Currently no fees are taken diff --git a/docs/interfaces/imetacolony.md b/docs/interfaces/imetacolony.md index 3ec33dd60d..ead19f17e8 100644 --- a/docs/interfaces/imetacolony.md +++ b/docs/interfaces/imetacolony.md @@ -197,6 +197,19 @@ Move any funds received by the colony in `_token` denomination to the top-level |_token|address|Address of the token, `0x0` value indicates Ether +### ▸ `claimColonyFunds(uint256 _chainId, address _token)` + +Move any funds received by the colony in `_token` denomination to the top-level domain pot, siphoning off a small amount to the reward pot. If called against a colony's own token, no fee is taken. + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|_chainId|uint256|Chain id of the chain where the funds need to be claimed +|_token|address|Address of the token, `0x0` value indicates Ether + + ### ▸ `claimDomainFunds(address _token, uint256 _domainId)` Move any funds received by the colony for a specific domain to that domain's pot Currently no fees are taken diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 21326e554d..eb6f21cf0b 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -557,7 +557,7 @@ contract("Cross-chain", (accounts) => { const p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await proxyColony.claimTokens(foreignToken.address); + tx = await proxyColony.claimColonyFunds(foreignToken.address); await tx.wait(); const receipt = await p; @@ -581,7 +581,7 @@ contract("Cross-chain", (accounts) => { // Claim tokens on foreign chain const p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await proxyColony.claimTokens(foreignToken.address); + tx = await proxyColony.claimColonyFunds(foreignToken.address); await tx.wait(); await p; @@ -623,7 +623,7 @@ contract("Cross-chain", (accounts) => { await checkErrorRevertEthers(tx.wait(), PANIC.ARITHMETHIC_OVERFLOW); }); - it("Can claim tokens received on foreign chain via cross-chain request", async () => { + it("Can claim tokens received on foreign chain via cross-chain request (manually)", async () => { const tokenAmount = ethers.utils.parseEther("100"); let tx = await foreignToken["mint(address,uint256)"](proxyColony.address, tokenAmount); @@ -633,7 +633,7 @@ contract("Cross-chain", (accounts) => { const p = guardianSpy.getPromiseForNextBridgedTransaction(2); // One bridged transaction will be the request across, one will be reporting what was claimed back - const payload = proxyColony.interface.encodeFunctionData("claimTokens", [foreignToken.address]); + const payload = proxyColony.interface.encodeFunctionData("claimColonyFunds", [foreignToken.address]); tx = await colony.makeProxyArbitraryTransaction(foreignChainId, proxyColony.address, payload); await tx.wait(); await p; @@ -644,6 +644,26 @@ contract("Cross-chain", (accounts) => { expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); }); + it("Can claim tokens received on foreign chain via cross-chain request (via claimColonyFunds)", async () => { + const tokenAmount = ethers.utils.parseEther("100"); + + let tx = await foreignToken["mint(address,uint256)"](proxyColony.address, tokenAmount); + await tx.wait(); + + // Claim on the foreign chain + const p = guardianSpy.getPromiseForNextBridgedTransaction(2); + // One bridged transaction will be the request across, one will be reporting what was claimed back + + tx = await colony["claimColonyFunds(uint256,address)"](foreignChainId, foreignToken.address); + await tx.wait(); + await p; + + // Check bookkeeping on the home chain + + const balance = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, foreignToken.address); + expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); + }); + it("Can claim tokens received on foreign chain via metatransaction", async () => { const tokenAmount = ethers.utils.parseEther("100"); @@ -655,7 +675,7 @@ contract("Cross-chain", (accounts) => { // Claim on the foreign chain via metatransaction const p = guardianSpy.getPromiseForNextBridgedTransaction(); - const payload = proxyColony.interface.encodeFunctionData("claimTokens", [foreignToken.address]); + const payload = proxyColony.interface.encodeFunctionData("claimColonyFunds", [foreignToken.address]); const { r, s, v } = await getMetaTransactionParameters(payload, accounts[1], proxyColony.address, foreignChainId); @@ -683,7 +703,7 @@ contract("Cross-chain", (accounts) => { // Claim on foreign chain const p = guardianSpy.getPromiseForNextBridgedTransaction(); - const tx = await proxyColony.claimTokens(ADDRESS_ZERO); + const tx = await proxyColony.claimColonyFunds(ADDRESS_ZERO); await tx.wait(); const receipt = await p; @@ -703,7 +723,7 @@ contract("Cross-chain", (accounts) => { // Claim on the foreign chain let p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await proxyColony.claimTokens(foreignToken.address); + tx = await proxyColony.claimColonyFunds(foreignToken.address); await tx.wait(); await p; @@ -775,7 +795,7 @@ contract("Cross-chain", (accounts) => { // Claim on the foreign chain let p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await proxyColony.claimTokens(ADDRESS_ZERO); + tx = await proxyColony.claimColonyFunds(ADDRESS_ZERO); await tx.wait(); await p; @@ -846,7 +866,7 @@ contract("Cross-chain", (accounts) => { // Claim on the foreign chain const p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await proxyColony.claimTokens(foreignToken.address); + tx = await proxyColony.claimColonyFunds(foreignToken.address); await tx.wait(); await p; @@ -861,7 +881,7 @@ contract("Cross-chain", (accounts) => { ethers.utils.hexZeroPad(ethers.utils.parseEther("30").toHexString(), 32), ]); - tx = await proxyColony.claimTokens(foreignToken.address, { gasLimit: 1000000 }); + tx = await proxyColony.claimColonyFunds(foreignToken.address, { gasLimit: 1000000 }); await checkErrorRevertEthers(tx.wait(), "colony-shell-token-bookkeeping-error"); // Now return the tokens @@ -877,7 +897,7 @@ contract("Cross-chain", (accounts) => { // Can now claim const p2 = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await proxyColony.claimTokens(foreignToken.address); + tx = await proxyColony.claimColonyFunds(foreignToken.address); await tx.wait(); await p2; }); @@ -891,7 +911,7 @@ contract("Cross-chain", (accounts) => { await (await foreignToken.unlock()).wait(); await homeToken["mint(address,uint256)"](homeColony.address, ethers.utils.parseEther("100")); - await homeColony.claimColonyFunds(homeToken.address); + await homeColony["claimColonyFunds(address)"](homeToken.address); await homeColony["addDomain(uint256,uint256,uint256)"](1, UINT256_MAX_ETHERS, 1); const domain1 = await homeColony.getDomain(1); const domain2 = await homeColony.getDomain(2); @@ -939,7 +959,7 @@ contract("Cross-chain", (accounts) => { // Now claim the tokens on the foreign chain const p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await proxyColony.claimTokensForDomain(foreignToken.address, 2, { gasLimit: 1000000 }); + tx = await proxyColony.claimDomainFunds(foreignToken.address, 2, { gasLimit: 1000000 }); await tx.wait(); await p; @@ -959,7 +979,7 @@ contract("Cross-chain", (accounts) => { await tx.wait(); let p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await proxyColony.claimTokens(foreignToken.address); + tx = await proxyColony.claimColonyFunds(foreignToken.address); await tx.wait(); await p; @@ -1024,7 +1044,7 @@ contract("Cross-chain", (accounts) => { // Sweep token in to the proxy p = guardianSpy.getPromiseForNextBridgedTransaction(); - tx = await proxyColony.claimTokensForDomain(foreignToken2.address, 2, { gasLimit: 1000000 }); + tx = await proxyColony.claimDomainFunds(foreignToken2.address, 2, { gasLimit: 1000000 }); await tx.wait(); // Wait for the sweep to be bridged @@ -1115,7 +1135,7 @@ contract("Cross-chain", (accounts) => { const payload1 = foreignToken.interface.encodeFunctionData("mint(address,uint256)", [proxyColony.address, ethers.utils.parseEther("100")]); const arbitraryCallPayload1 = proxyColony.interface.encodeFunctionData("makeArbitraryTransaction", [foreignToken.address, payload1]); - const payload2 = proxyColony.interface.encodeFunctionData("claimTokens(address)", [foreignToken.address]); + const payload2 = proxyColony.interface.encodeFunctionData("claimColonyFunds(address)", [foreignToken.address]); const multicallPayload = proxyColony.interface.encodeFunctionData("multicall", [[arbitraryCallPayload1, payload2]]); @@ -1321,7 +1341,7 @@ contract("Cross-chain", (accounts) => { let p = guardianSpy.getPromiseForNextBridgedTransaction(); - let tx = await proxyColony.claimTokens(foreignToken.address); + let tx = await proxyColony.claimColonyFunds(foreignToken.address); await tx.wait(); await p; From 6225cbc9663d37c472dcb75641580713fd87ae3d Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 24 Jan 2025 17:25:25 +0000 Subject: [PATCH 65/72] Bump contract versions as using viaIR --- contracts/colonyNetwork/badcommit.txt | 1 + contracts/extensions/CoinMachine.sol | 2 +- contracts/extensions/EvaluatedExpenditure.sol | 2 +- contracts/extensions/FundingQueue.sol | 2 +- contracts/extensions/MultisigPermissions.sol | 2 +- contracts/extensions/OneTxPayment.sol | 2 +- contracts/extensions/ReputationBootstrapper.sol | 2 +- contracts/extensions/StagedExpenditure.sol | 2 +- contracts/extensions/StakedExpenditure.sol | 2 +- contracts/extensions/StreamingPayments.sol | 2 +- contracts/extensions/TokenSupplier.sol | 2 +- contracts/extensions/Whitelist.sol | 2 +- .../extensions/votingReputation/VotingReputationStorage.sol | 2 +- 13 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 contracts/colonyNetwork/badcommit.txt diff --git a/contracts/colonyNetwork/badcommit.txt b/contracts/colonyNetwork/badcommit.txt new file mode 100644 index 0000000000..4f333ad188 --- /dev/null +++ b/contracts/colonyNetwork/badcommit.txt @@ -0,0 +1 @@ +e85aa86b446ac7df65ad8bdf7d146868c677255e \ No newline at end of file diff --git a/contracts/extensions/CoinMachine.sol b/contracts/extensions/CoinMachine.sol index 20087c933e..da56ed0f9b 100644 --- a/contracts/extensions/CoinMachine.sol +++ b/contracts/extensions/CoinMachine.sol @@ -102,7 +102,7 @@ contract CoinMachine is ColonyExtension, BasicMetaTransaction { /// @notice Returns the version of the extension /// @return _version The extension's version number function version() public pure override returns (uint256 _version) { - return 12; + return 13; } /// @notice Called when upgrading the extension diff --git a/contracts/extensions/EvaluatedExpenditure.sol b/contracts/extensions/EvaluatedExpenditure.sol index 9ae94470a7..ec69f21e83 100644 --- a/contracts/extensions/EvaluatedExpenditure.sol +++ b/contracts/extensions/EvaluatedExpenditure.sol @@ -42,7 +42,7 @@ contract EvaluatedExpenditure is ColonyExtension, BasicMetaTransaction { /// @notice Returns the version of the extension /// @return _version The extension's version number function version() public pure override returns (uint256 _version) { - return 8; + return 9; } /// @notice Gets the next nonce for a meta-transaction diff --git a/contracts/extensions/FundingQueue.sol b/contracts/extensions/FundingQueue.sol index 821a7a08d8..81e9219a7f 100644 --- a/contracts/extensions/FundingQueue.sol +++ b/contracts/extensions/FundingQueue.sol @@ -113,7 +113,7 @@ contract FundingQueue is ColonyExtension, BasicMetaTransaction { /// @notice Returns the version of the extension /// @return _version The extension's version number function version() public pure override returns (uint256 _version) { - return 9; + return 10; } /// @notice Configures the extension diff --git a/contracts/extensions/MultisigPermissions.sol b/contracts/extensions/MultisigPermissions.sol index 06fa238ea3..947fc2a70e 100644 --- a/contracts/extensions/MultisigPermissions.sol +++ b/contracts/extensions/MultisigPermissions.sol @@ -103,7 +103,7 @@ contract MultisigPermissions is ColonyExtensionMeta, ColonyDataTypes, GetActionS /// @notice Returns the version of the extension /// @return _version The extension's version number function version() public pure override returns (uint256 _version) { - return 2; + return 3; } /// @notice Configures the extension diff --git a/contracts/extensions/OneTxPayment.sol b/contracts/extensions/OneTxPayment.sol index cff4e41621..fd9258ed03 100644 --- a/contracts/extensions/OneTxPayment.sol +++ b/contracts/extensions/OneTxPayment.sol @@ -58,7 +58,7 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { /// @notice Returns the version of the extension /// @return _version The extension's version number function version() public pure override returns (uint256 _version) { - return 9; + return 10; } /// @notice Called when upgrading the extension diff --git a/contracts/extensions/ReputationBootstrapper.sol b/contracts/extensions/ReputationBootstrapper.sol index bfb266ef95..81d640971c 100644 --- a/contracts/extensions/ReputationBootstrapper.sol +++ b/contracts/extensions/ReputationBootstrapper.sol @@ -77,7 +77,7 @@ contract ReputationBootstrapper is ColonyExtensionMeta { /// @notice Returns the version of the extension /// @return _version The extension's version number function version() public pure override returns (uint256) { - return 6; + return 7; } /// @notice Configures the extension diff --git a/contracts/extensions/StagedExpenditure.sol b/contracts/extensions/StagedExpenditure.sol index 8aa8aa4965..6ab2d1adab 100644 --- a/contracts/extensions/StagedExpenditure.sol +++ b/contracts/extensions/StagedExpenditure.sol @@ -45,7 +45,7 @@ contract StagedExpenditure is ColonyExtensionMeta, ColonyDataTypes { /// @notice Returns the version of the extension /// @return _version The extension's version number function version() public pure override returns (uint256 _version) { - return 4; + return 5; } // Public diff --git a/contracts/extensions/StakedExpenditure.sol b/contracts/extensions/StakedExpenditure.sol index 8b5a29ba8c..1b482cd4fc 100644 --- a/contracts/extensions/StakedExpenditure.sol +++ b/contracts/extensions/StakedExpenditure.sol @@ -68,7 +68,7 @@ contract StakedExpenditure is ColonyExtensionMeta { /// @notice Returns the version of the extension /// @return _version The extension's version number function version() public pure override returns (uint256 _version) { - return 7; + return 8; } // Public diff --git a/contracts/extensions/StreamingPayments.sol b/contracts/extensions/StreamingPayments.sol index a42e9f88b2..012f7bf90a 100644 --- a/contracts/extensions/StreamingPayments.sol +++ b/contracts/extensions/StreamingPayments.sol @@ -116,7 +116,7 @@ contract StreamingPayments is ColonyExtensionMeta { /// @notice Returns the version of the extension /// @return _version The extension's version number function version() public pure override returns (uint256 _version) { - return 6; + return 7; } /// @notice Called when upgrading the extension diff --git a/contracts/extensions/TokenSupplier.sol b/contracts/extensions/TokenSupplier.sol index b9d4b46223..76887df174 100644 --- a/contracts/extensions/TokenSupplier.sol +++ b/contracts/extensions/TokenSupplier.sol @@ -74,7 +74,7 @@ contract TokenSupplier is ColonyExtension, BasicMetaTransaction { /// @notice Returns the version of the extension /// @return _version The extension's version number function version() public pure override returns (uint256 _version) { - return 9; + return 10; } /// @notice Configures the extension diff --git a/contracts/extensions/Whitelist.sol b/contracts/extensions/Whitelist.sol index 5a7bc4fb95..c628bad5d3 100644 --- a/contracts/extensions/Whitelist.sol +++ b/contracts/extensions/Whitelist.sol @@ -72,7 +72,7 @@ contract Whitelist is ColonyExtension, BasicMetaTransaction { /// @notice Returns the version of the extension /// @return _version The extension's version number function version() public pure override returns (uint256 _version) { - return 8; + return 9; } // Public diff --git a/contracts/extensions/votingReputation/VotingReputationStorage.sol b/contracts/extensions/votingReputation/VotingReputationStorage.sol index d4d731974d..cb01d57508 100644 --- a/contracts/extensions/votingReputation/VotingReputationStorage.sol +++ b/contracts/extensions/votingReputation/VotingReputationStorage.sol @@ -128,7 +128,7 @@ contract VotingReputationStorage is /// @notice Returns the version of the extension /// @return _version The extension's version number function version() public pure override returns (uint256 _version) { - return 13; + return 14; } function install(address _colony) public override { From f8fbca4f12eb0c0f4ca60918354b53f1131ce94f Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Wed, 29 Jan 2025 18:06:57 +0000 Subject: [PATCH 66/72] Add multicall claim test --- test/cross-chain/cross-chain.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index eb6f21cf0b..782a88bb73 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -664,6 +664,32 @@ contract("Cross-chain", (accounts) => { expect(balance.toHexString()).to.equal(tokenAmount.toHexString()); }); + it("Can multicall claimColonyFunds and receive both tokens", async () => { + const tokenFactory = new ethers.ContractFactory(Token.abi, Token.bytecode, ethersForeignSigner); + const foreignToken2 = await tokenFactory.deploy("Test Token", "TT", 18); + await (await foreignToken2.unlock()).wait(); + const tokenAmount = ethers.utils.parseEther("100"); + let tx = await foreignToken["mint(address,uint256)"](proxyColony.address, tokenAmount); + await tx.wait(); + tx = await foreignToken2["mint(address,uint256)"](proxyColony.address, tokenAmount); + await tx.wait(); + + const p = guardianSpy.getPromiseForNextBridgedTransaction(4); + + const call1 = colony.interface.encodeFunctionData("claimColonyFunds(uint256,address)", [foreignChainId, foreignToken.address]); + const call2 = colony.interface.encodeFunctionData("claimColonyFunds(uint256,address)", [foreignChainId, foreignToken2.address]); + tx = await colony.multicall([call1, call2]); + + await tx.wait(); + await p; + + const balance1 = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, foreignToken.address); + expect(balance1.toHexString()).to.equal(tokenAmount.toHexString()); + + const balance2 = await colony["getFundingPotBalance(uint256,uint256,address)"](1, foreignChainId, foreignToken2.address); + expect(balance2.toHexString()).to.equal(tokenAmount.toHexString()); + }); + it("Can claim tokens received on foreign chain via metatransaction", async () => { const tokenAmount = ethers.utils.parseEther("100"); From f22a0c221ce16e6dee8a07f7da62ba7c510b0ef3 Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Thu, 30 Jan 2025 09:12:05 +0000 Subject: [PATCH 67/72] Try new ExtendedNonceManager --- .../package-utils/ExtendedNonceManager.js | 53 ++++++++++--------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/packages/package-utils/ExtendedNonceManager.js b/packages/package-utils/ExtendedNonceManager.js index cb5d8091c7..2e8b18042b 100644 --- a/packages/package-utils/ExtendedNonceManager.js +++ b/packages/package-utils/ExtendedNonceManager.js @@ -1,4 +1,5 @@ const { NonceManager } = require("@ethersproject/experimental"); +const { parseTransaction } = require("ethers/lib/utils"); class ExtendedNonceManager extends NonceManager { constructor(signer) { @@ -9,8 +10,23 @@ class ExtendedNonceManager extends NonceManager { Object.keys(this.signedTransactions).map(async (txHash) => { const nodeTx = await this.signer.provider.getTransaction(txHash); if (!nodeTx) { - this.signer.provider.sendTransaction(this.signedTransactions[txHash]); - return; + const txCount = await this.signer.getTransactionCount("pending"); + const parsedTransaction = parseTransaction(this.signedTransactions[txHash]); + if (parsedTransaction.nonce < txCount) { + // It's not been mined, but it's been replaced by another tx. + // Resend, with a new nonce + delete this.signedTransactions[txHash]; + this.sendTransaction({ + from: parsedTransaction.from, + to: parsedTransaction.to, + value: parsedTransaction.value, + data: parsedTransaction.data, + }); + } else { + // No reason to think it's been replaced, so rebroadcast. + this.signer.provider.sendTransaction(this.signedTransactions[txHash]); + return; + } } if (nodeTx.blockNumber) { // It's been mined, so forget it. @@ -22,29 +38,18 @@ class ExtendedNonceManager extends NonceManager { } async sendTransaction(transactionRequest) { - // What nonce are we going to attach to this? - // Definitely not any we've sent and are pending - // const pendingNonces = Object.keys(this.signedTransactions).map((txhash) => this.signedTransactions[txhash].nonce); - - // At least whatever the endpoint says, or whatever we've already sent if higher - let nonce = await this.signer.getTransactionCount(); - - // Note the order we did the above two lines in - if a tx is mined between these two lines, - // and got removed by the `on block` handler above, by doing it in this order we won't be tripped up - // And we'll skip any nonces we've already used - while ( - Object.keys(this.signedTransactions) - .map((txhash) => this.signedTransactions[txhash].nonce) - .includes(nonce) - ) { - nonce += 1; + try { + const populatedTransaction = await this.populateTransaction(transactionRequest); + const signedTransaction = await this.signTransaction(populatedTransaction); + const response = super.sendTransaction(transactionRequest); + const tx = await response; + this.signedTransactions[tx.hash] = signedTransaction; + return response; + } catch (e) { + const txCount = await this.signer.getTransactionCount("pending"); + this.setTransactionCount(txCount); + return this.sendTransaction(transactionRequest); } - transactionRequest.nonce = nonce; // eslint-disable-line no-param-reassign - this.nonce = nonce + 1; - const response = super.sendTransaction(transactionRequest); - const tx = await response; - this.signedTransactions[tx.hash] = transactionRequest; - return response; } } From cbd44ff4bf6f6b2760995efc040452f50b7beecf Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Thu, 30 Jan 2025 14:13:57 +0000 Subject: [PATCH 68/72] Post-rebase fixes --- contracts/bridging/ProxyColony.sol | 2 +- contracts/bridging/ProxyColonyNetwork.sol | 2 +- contracts/common/CommonDataTypes.sol | 2 +- contracts/common/DomainReceiverManagement.sol | 4 +- contracts/common/mixins/IsContract.sol | 2 +- contracts/testHelpers/LiFiFacetProxyMock.sol | 2 +- docs/interfaces/icolony.md | 12 ++++ docs/interfaces/imetacolony.md | 12 ++++ pnpm-lock.yaml | 70 ++++++++++++++++++- 9 files changed, 98 insertions(+), 10 deletions(-) diff --git a/contracts/bridging/ProxyColony.sol b/contracts/bridging/ProxyColony.sol index 1d22813216..464d1d7338 100644 --- a/contracts/bridging/ProxyColony.sol +++ b/contracts/bridging/ProxyColony.sol @@ -16,7 +16,7 @@ along with The Colony Network. If not, see . */ -pragma solidity 0.8.27; +pragma solidity 0.8.28; pragma experimental ABIEncoderV2; import { CallWithGuards } from "../common/mixins/CallWithGuards.sol"; diff --git a/contracts/bridging/ProxyColonyNetwork.sol b/contracts/bridging/ProxyColonyNetwork.sol index 49823f2f4e..09a2186385 100644 --- a/contracts/bridging/ProxyColonyNetwork.sol +++ b/contracts/bridging/ProxyColonyNetwork.sol @@ -16,7 +16,7 @@ along with The Colony Network. If not, see . */ -pragma solidity 0.8.27; +pragma solidity 0.8.28; pragma experimental ABIEncoderV2; import { BasicMetaTransaction } from "./../common/BasicMetaTransaction.sol"; diff --git a/contracts/common/CommonDataTypes.sol b/contracts/common/CommonDataTypes.sol index 03d56b69c4..5422338e63 100644 --- a/contracts/common/CommonDataTypes.sol +++ b/contracts/common/CommonDataTypes.sol @@ -16,7 +16,7 @@ along with The Colony Network. If not, see . */ -pragma solidity 0.8.27; +pragma solidity 0.8.28; // prettier-ignore interface CommonDataTypes { diff --git a/contracts/common/DomainReceiverManagement.sol b/contracts/common/DomainReceiverManagement.sol index b907e66855..a42d8ac974 100644 --- a/contracts/common/DomainReceiverManagement.sol +++ b/contracts/common/DomainReceiverManagement.sol @@ -16,7 +16,7 @@ along with The Colony Network. If not, see . */ -pragma solidity 0.8.27; +pragma solidity 0.8.28; pragma experimental "ABIEncoderV2"; import { MetaTransactionMsgSender } from "./MetaTransactionMsgSender.sol"; @@ -56,7 +56,7 @@ abstract contract DomainReceiverManagement is MetaTransactionMsgSender, IsContra // Set up the deployed contract EtherRouter(payable(domainTokenReceiverAddress)).setResolver(domainReceiverResolverAddress); - DomainTokenReceiver(domainTokenReceiverAddress).setColonyAddress(msgSender()); + DomainTokenReceiver(domainTokenReceiverAddress).setColony(msgSender()); } else { // Contract is deployed, check it's got the right resolver try EtherRouter(payable(domainTokenReceiverAddress)).resolver() returns (Resolver resolver) { diff --git a/contracts/common/mixins/IsContract.sol b/contracts/common/mixins/IsContract.sol index 679bcc1b28..b4c216f03c 100644 --- a/contracts/common/mixins/IsContract.sol +++ b/contracts/common/mixins/IsContract.sol @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with The Colony Network. If not, see . */ -pragma solidity 0.8.27; +pragma solidity 0.8.28; contract IsContract { function isContract(address addr) internal view returns (bool) { diff --git a/contracts/testHelpers/LiFiFacetProxyMock.sol b/contracts/testHelpers/LiFiFacetProxyMock.sol index 5261d9f965..edbf1236ef 100644 --- a/contracts/testHelpers/LiFiFacetProxyMock.sol +++ b/contracts/testHelpers/LiFiFacetProxyMock.sol @@ -16,7 +16,7 @@ along with The Colony Network. If not, see . */ -pragma solidity 0.8.27; +pragma solidity 0.8.28; import { ColonyExtensionMeta } from "./../extensions/ColonyExtensionMeta.sol"; import { IColony } from "../colony/IColony.sol"; diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index eb91f8c135..624aa5ae5d 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -1768,6 +1768,18 @@ Set new colony funding role. Can be called by root role or architecture role. |_setTo|bool|The state of the role permission (true assign the permission, false revokes it) +### ▸ `setOwner(address owner_)` + +Set the owner of the contract + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|owner_|address|The new owner of the contract + + ### ▸ `setRecoveryRole(address _user)` Set new colony recovery role. Can be called by root. diff --git a/docs/interfaces/imetacolony.md b/docs/interfaces/imetacolony.md index ead19f17e8..9e2e7ae5d1 100644 --- a/docs/interfaces/imetacolony.md +++ b/docs/interfaces/imetacolony.md @@ -1844,6 +1844,18 @@ Set the Colony Network fee inverse amount. |_feeInverse|uint256|Nonzero amount for the fee inverse +### ▸ `setOwner(address owner_)` + +Set the owner of the contract + + +**Parameters** + +|Name|Type|Description| +|---|---|---| +|owner_|address|The new owner of the contract + + ### ▸ `setPayoutWhitelist(address _token, bool _status)` Set a token's status in the payout whitelist on the Colony Network diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0f2adf01dc..47001c9582 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -499,7 +499,7 @@ importers: version: 0.10.17(fastestsmallesttextencoderdecoder@1.0.22)(google-protobuf@3.21.4) '@wormhole-foundation/relayer-engine': specifier: ^0.3.2 - version: 0.3.2(@bull-board/ui@5.21.0)(@improbable-eng/grpc-web@0.15.0)(@types/node@20.17.16)(fastestsmallesttextencoderdecoder@1.0.22)(google-protobuf@3.21.4)(typescript@5.7.3) + version: 0.3.2(@bull-board/ui@5.21.0)(@improbable-eng/grpc-web@0.15.0)(@types/node@22.10.10)(fastestsmallesttextencoderdecoder@1.0.22)(google-protobuf@3.21.4)(typescript@5.7.3) ethers: specifier: '5' version: 5.7.2 @@ -4645,7 +4645,7 @@ packages: resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} dev: true - /@wormhole-foundation/relayer-engine@0.3.2(@bull-board/ui@5.21.0)(@improbable-eng/grpc-web@0.15.0)(@types/node@20.17.16)(fastestsmallesttextencoderdecoder@1.0.22)(google-protobuf@3.21.4)(typescript@5.7.3): + /@wormhole-foundation/relayer-engine@0.3.2(@bull-board/ui@5.21.0)(@improbable-eng/grpc-web@0.15.0)(@types/node@22.10.10)(fastestsmallesttextencoderdecoder@1.0.22)(google-protobuf@3.21.4)(typescript@5.7.3): resolution: {integrity: sha512-6CAGZdxDpPZph+Gx1Tob5wEarEzvD27G7khoGhB9sc8KiOhRz8zDiBspFfLetzfBEHMiTyvAOibETV5G0ULilA==} dependencies: '@bull-board/api': 5.21.0(@bull-board/ui@5.21.0) @@ -4656,7 +4656,7 @@ packages: '@improbable-eng/grpc-web-node-http-transport': 0.15.0(@improbable-eng/grpc-web@0.15.0) '@mysten/sui.js': 0.32.2 '@sei-js/core': 1.5.0 - '@xlabs-xyz/wallet-monitor': 0.2.16(@types/node@20.17.16)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) + '@xlabs-xyz/wallet-monitor': 0.2.16(@types/node@22.10.10)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3) bech32: 2.0.0 bullmq: 3.15.8 ethers: 5.7.2 @@ -4892,6 +4892,38 @@ packages: - supports-color - typescript - utf-8-validate + dev: true + + /@xlabs-xyz/wallet-monitor@0.2.16(@types/node@22.10.10)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.3): + resolution: {integrity: sha512-Fup24DcONFy3o5lrJP3i5gc5XXj7675gWhco3JkweWTFj86c17UrXYQOhZF1Z09b7WYt4rnXZx+wQ4W19HzOoQ==} + dependencies: + '@ethersproject/bignumber': 5.7.0 + '@mysten/sui.js': 0.32.2 + '@solana/spl-token': 0.3.11(@solana/web3.js@1.95.0)(fastestsmallesttextencoderdecoder@1.0.22) + '@solana/web3.js': 1.95.0 + bluebird: 3.7.2 + bs58: 5.0.0 + ethers: 5.7.2 + google-protobuf: 3.21.2 + koa: 2.15.3 + koa-router: 12.0.1 + nice-grpc: 2.1.9 + prom-client: 14.2.0 + ts-node: 10.9.2(@types/node@22.10.10)(typescript@5.7.3) + ts-proto: 1.181.0 + winston: 3.13.1 + zod: 3.23.8 + transitivePeerDependencies: + - '@swc/core' + - '@swc/wasm' + - '@types/node' + - bufferutil + - encoding + - fastestsmallesttextencoderdecoder + - supports-color + - typescript + - utf-8-validate + dev: false /@xpla/xpla.js@0.2.3: resolution: {integrity: sha512-Tfk7hCGWXtwr08reY3Pi6dmzIqFbzri9jcyzJdfNmdo4cN0PMwpRJuZZcPmtxiIUnNef3AN1E/6nJUD5MKniuA==} @@ -18932,6 +18964,38 @@ packages: typescript: 5.7.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + dev: true + + /ts-node@10.9.2(@types/node@22.10.10)(typescript@5.7.3): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.10.10 + acorn: 8.12.1 + acorn-walk: 8.3.3 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.7.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: false /ts-node@8.10.2(typescript@5.7.3): resolution: {integrity: sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==} From 8fdfba93774fbad0560114c06f1949af41d472fd Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 31 Jan 2025 10:31:08 +0000 Subject: [PATCH 69/72] Fix makePaymentFundedFromDomain for multichain --- contracts/extensions/OneTxPayment.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/extensions/OneTxPayment.sol b/contracts/extensions/OneTxPayment.sol index fd9258ed03..3356f73eeb 100644 --- a/contracts/extensions/OneTxPayment.sol +++ b/contracts/extensions/OneTxPayment.sol @@ -289,7 +289,7 @@ contract OneTxPayment is ColonyExtension, BasicMetaTransaction { require(_tokens[idx] > _tokens[idx - 1], "one-tx-payment-bad-token-order"); } - colony.setExpenditurePayout(expenditureId, slot, _tokens[idx], _amounts[idx]); + colony.setExpenditurePayout(expenditureId, slot, _chainIds[idx], _tokens[idx], _amounts[idx]); } finalizeAndClaim( From f3a1de666d7626a389ac24468300ac0bbd10ecfb Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Fri, 31 Jan 2025 18:07:39 +0000 Subject: [PATCH 70/72] Fees for cross-chain payouts --- contracts/colony/ColonyFunding.sol | 35 ++++++++++++++--------- test/cross-chain/cross-chain.js | 46 +++++++++++++++++------------- 2 files changed, 47 insertions(+), 34 deletions(-) diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index 631d07e3d9..a65ba1b8ea 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -768,13 +768,15 @@ contract ColonyFunding is getFundingPotPayout(_fundingPotId, _chainId, _token) - _payout ); - uint256 payoutToUser; decrementNonRewardPotsTotal(_chainId, _token, _payout); + uint256 fee = isOwnExtension(_user) ? 0 : calculateNetworkFeeForPayout(_payout); + uint256 payoutToUser = _payout - fee; - if (_chainId == block.chainid) { - uint256 fee = isOwnExtension(_user) ? 0 : calculateNetworkFeeForPayout(_payout); - payoutToUser = _payout - fee; + address feeReceiver = colonyNetworkContract.getPayoutWhitelist(_token) + ? metaColonyAddress + : colonyNetworkAddress; + if (_chainId == block.chainid) { if (_token == address(0x0)) { // Payout ether // Fee goes directly to Meta Colony @@ -786,22 +788,27 @@ contract ColonyFunding is // If it's any other token, goes to the colonyNetwork contract first to be auctioned. ERC20Extended payoutToken = ERC20Extended(_token); assert(payoutToken.transfer(_user, payoutToUser)); - if (colonyNetworkContract.getPayoutWhitelist(_token)) { - assert(payoutToken.transfer(metaColonyAddress, fee)); - } else { - assert(payoutToken.transfer(colonyNetworkAddress, fee)); - } + assert(payoutToken.transfer(feeReceiver, fee)); } } else { - // TODO: Network fee - bytes memory payload = abi.encodeWithSignature( + bytes[] memory multicallArgs = new bytes[](2); + + multicallArgs[0] = abi.encodeWithSignature( "transferFromBridge(address,address,uint256)", _token, _user, - _payout + payoutToUser ); - IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, payload); - payoutToUser = _payout; + + multicallArgs[1] = abi.encodeWithSignature( + "transferFromBridge(address,address,uint256)", + _token, + feeReceiver, + fee + ); + + bytes memory multicallData = abi.encodeWithSignature("multicall(bytes[])", multicallArgs); + IColonyNetwork(colonyNetworkAddress).bridgeMessage(_chainId, multicallData); } // slither-disable-next-line reentrancy-unlimited-gas diff --git a/test/cross-chain/cross-chain.js b/test/cross-chain/cross-chain.js index 782a88bb73..f462e1c6e0 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -162,6 +162,12 @@ contract("Cross-chain", (accounts) => { web3HomeProvider = new web3.eth.providers.HttpProvider(ethersHomeSigner.provider.connection.url); web3ForeignProvider = new web3.eth.providers.HttpProvider(ethersForeignSigner.provider.connection.url); + if (homeSnapshotId) { + await revert(web3HomeProvider, homeSnapshotId); + await revert(web3ForeignProvider, foreignSnapshotId); + await resetRelayer(); + } + homeSnapshotId = await snapshot(web3HomeProvider); foreignSnapshotId = await snapshot(web3ForeignProvider); guardianSpy.reset(); @@ -189,7 +195,6 @@ contract("Cross-chain", (accounts) => { // await tx.wait(); // await p; // } - console.log("setting up mining client "); // Set up mining client client = new ReputationMinerTestWrapper({ loader: contractLoader, @@ -200,8 +205,6 @@ contract("Cross-chain", (accounts) => { await client.initialise(homeColonyNetwork.address); - console.log("initialised"); - await forwardTime(MINING_CYCLE_DURATION + CHALLENGE_RESPONSE_WINDOW_DURATION, undefined, web3HomeProvider); await client.addLogContentsToReputationTree(); await client.submitRootHash(); @@ -236,12 +239,6 @@ contract("Cross-chain", (accounts) => { return colony; } - afterEach(async () => { - await revert(web3HomeProvider, homeSnapshotId); - await revert(web3ForeignProvider, foreignSnapshotId); - await resetRelayer(); - }); - after(async () => { await guardianSpy.close(); }); @@ -506,7 +503,6 @@ contract("Cross-chain", (accounts) => { // So 'just' call that on the colony... - console.log("tx to home bridge address:", homeBridge.address); const tx = await homeColony.makeArbitraryTransaction(homeBridge.address, txDataToBeSentToAMB); await tx.wait(); await p; @@ -807,7 +803,7 @@ contract("Cross-chain", (accounts) => { const recipientBalance = await foreignToken.balanceOf(accounts[0]); expect(colonyBalance.toHexString()).to.equal(ethers.utils.parseEther("70").toHexString()); - expect(recipientBalance.toHexString()).to.equal(ethers.utils.parseEther("30").toHexString()); + expect(recipientBalance.toHexString()).to.equal(ethers.utils.parseEther("30").sub(ethers.utils.parseEther("0.3").add(1)).toHexString()); }); it("Can track native tokens sent on the foreign chain", async () => { @@ -881,7 +877,9 @@ contract("Cross-chain", (accounts) => { const recipientBalanceAfter = await ethersForeignProvider.getBalance(accounts[0]); expect(colonyBalance.toHexString()).to.equal(ethers.utils.parseEther("0.7").toHexString()); - expect(recipientBalanceAfter.sub(receipientBalanceBefore).toHexString()).to.equal(ethers.utils.parseEther("0.3").toHexString()); + expect(recipientBalanceAfter.sub(receipientBalanceBefore).toHexString()).to.equal( + ethers.utils.parseEther("0.3").sub(ethers.utils.parseEther("0.003").add(1)).toHexString(), + ); }); it("a bookkeeping error will mean that tokens can no longer be claimed until tokens are returned", async () => { @@ -1019,9 +1017,6 @@ contract("Cross-chain", (accounts) => { const domain1 = await colony.getDomain(1); const domain2 = await colony.getDomain(2); - console.log(domain2); - const fundingPot = await colony.getFundingPot(domain2.fundingPotId); - console.log(fundingPot); tx = await colony["moveFundsBetweenPots(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,address)"]( 1, @@ -1036,7 +1031,7 @@ contract("Cross-chain", (accounts) => { foreignToken.address, ); await tx.wait(); - console.log("moved"); + // Exchange tokens const domain2ReceiverAddress = await homeColonyNetwork.getDomainTokenReceiverAddress(colony.address, 2); @@ -1357,7 +1352,11 @@ contract("Cross-chain", (accounts) => { // Deploy a token on the foreign network }); - it("Can make a OneTxPayment cross-chain", async () => { + it("Can make a OneTxPayment cross-chain, including the fee", async () => { + // Set fee on the home chain + let tx = await homeMetacolony.setNetworkFeeInverse(100); + await tx.wait(); + const tokenFactory = new ethers.ContractFactory(MetaTxToken.abi, MetaTxToken.bytecode, ethersForeignSigner); const foreignToken = await tokenFactory.deploy("Test Token", "TT", 18); await (await foreignToken.unlock()).wait(); @@ -1367,7 +1366,7 @@ contract("Cross-chain", (accounts) => { let p = guardianSpy.getPromiseForNextBridgedTransaction(); - let tx = await proxyColony.claimColonyFunds(foreignToken.address); + tx = await proxyColony.claimColonyFunds(foreignToken.address); await tx.wait(); await p; @@ -1383,9 +1382,10 @@ contract("Cross-chain", (accounts) => { await homeColony.setUserRoles(1, UINT256_MAX_ETHERS, oneTxPayment.address, 1, ROLES); const balanceBefore = await foreignToken.balanceOf(accounts[0]); + const networkBalanceBefore = await foreignToken.balanceOf(remoteColonyNetwork.address); p = guardianSpy.getPromiseForNextBridgedTransaction(); - console.log("amkepayment"); + tx = await oneTxPayment["makePayment(uint256,uint256,uint256,uint256,address[],uint256[],address[],uint256[],uint256,uint256)"]( 1, UINT256_MAX_ETHERS, @@ -1402,7 +1402,13 @@ contract("Cross-chain", (accounts) => { await p; const balanceAfter = await foreignToken.balanceOf(accounts[0]); - expect(balanceAfter.sub(balanceBefore).toHexString()).to.equal(paymentAmount.toHexString()); + expect(balanceAfter.sub(balanceBefore).toHexString()).to.equal(paymentAmount.sub(ethers.utils.parseEther("0.3").add(1)).toHexString()); + + const networkBalanceAfter = await foreignToken.balanceOf(remoteColonyNetwork.address); + expect(networkBalanceAfter.sub(networkBalanceBefore).toHexString()).to.equal(ethers.utils.parseEther("0.3").add(1).toHexString()); + + tx = await homeMetacolony.setNetworkFeeInverse(UINT256_MAX_ETHERS); + await tx.wait(); }); }); From 370067c5a1058543feece0ad5df66bb6a9b3a5bf Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 3 Feb 2025 10:27:21 +0000 Subject: [PATCH 71/72] PayoutClaimed should have chainId --- contracts/colony/ColonyDataTypes.sol | 2 +- contracts/colony/ColonyFunding.sol | 2 +- test/contracts-network/colony-expenditure.js | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/contracts/colony/ColonyDataTypes.sol b/contracts/colony/ColonyDataTypes.sol index 9ec25e1242..33392db439 100755 --- a/contracts/colony/ColonyDataTypes.sol +++ b/contracts/colony/ColonyDataTypes.sol @@ -271,7 +271,7 @@ interface ColonyDataTypes is CommonDataTypes { /// @param slot Expenditure slot of the payout claimed /// @param token Token of the payout claim /// @param tokenPayout Amount of the payout claimed, after network fee was deducted - event PayoutClaimed(address agent, uint256 id, uint256 slot, address token, uint256 tokenPayout); + event PayoutClaimed(address agent, uint256 id, uint256 slot, uint256 chainId, address token, uint256 tokenPayout); /// @notice Event logged when a colony requests a proxy colony deployment /// @param agent The address that is responsible for triggering this event diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index a65ba1b8ea..9da7df4477 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -524,7 +524,7 @@ contract ColonyFunding is slot.recipient ); - emit PayoutClaimed(msgSender(), _id, _slot, _token, payoutMinusFee); + emit PayoutClaimed(msgSender(), _id, _slot, _chainId, _token, payoutMinusFee); } // View diff --git a/test/contracts-network/colony-expenditure.js b/test/contracts-network/colony-expenditure.js index 5a56c0f645..f76c125c65 100644 --- a/test/contracts-network/colony-expenditure.js +++ b/test/contracts-network/colony-expenditure.js @@ -14,6 +14,7 @@ const { getBlockTime, bn2bytes32, upgradeColonyOnceThenToLatest, + getChainId, } = require("../../helpers/test-helper"); const { fundColonyWithTokens, setupRandomColony } = require("../../helpers/test-data-generator"); const { setupEtherRouter } = require("../../helpers/upgradable-contracts"); @@ -767,7 +768,10 @@ contract("Colony Expenditure", (accounts) => { ); await colony.finalizeExpenditure(expenditureId, { from: ADMIN }); const tx = await colony.claimExpenditurePayout(expenditureId, SLOT0, token.address); - await expectEvent(tx, "PayoutClaimed", [accounts[0], expenditureId, SLOT0, token.address, WAD.divn(100).muln(99).subn(1)]); + + const chainId = await getChainId(); + + await expectEvent(tx, "PayoutClaimed", [accounts[0], expenditureId, SLOT0, chainId, token.address, WAD.divn(100).muln(99).subn(1)]); await expectEvent(tx, "PayoutClaimed", [accounts[0], expenditure.fundingPotId, token.address, WAD.divn(100).muln(99).subn(1)]); }); From 1653b8bbd9d769aeb0560dd36cbfb4f489ca91ec Mon Sep 17 00:00:00 2001 From: Alex Rea Date: Mon, 3 Feb 2025 11:00:50 +0000 Subject: [PATCH 72/72] ExpenditurePayoutsSet should have chainId --- contracts/colony/ColonyDataTypes.sol | 6 ++++-- contracts/colony/ColonyFunding.sol | 11 +++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/contracts/colony/ColonyDataTypes.sol b/contracts/colony/ColonyDataTypes.sol index 33392db439..764f9c74eb 100755 --- a/contracts/colony/ColonyDataTypes.sol +++ b/contracts/colony/ColonyDataTypes.sol @@ -55,8 +55,9 @@ interface ColonyDataTypes is CommonDataTypes { /// @param fromPot The source funding pot /// @param toPot The targer funding pot /// @param amount The amount that was transferred + /// @param chainId The chain id of the token being transferred /// @param token The token address being transferred - event ColonyFundsMovedBetweenFundingPots(address agent, uint256 indexed fromPot, uint256 indexed toPot, uint256 amount, address token); + event ColonyFundsMovedBetweenFundingPots(address agent, uint256 indexed fromPot, uint256 indexed toPot, uint256 amount, uint256 chainId, address token); /// @notice Event logged when colony funds are moved to the top-level domain pot /// @param agent The address that is responsible for triggering this event @@ -150,9 +151,10 @@ interface ColonyDataTypes is CommonDataTypes { /// @param agent The address that is responsible for triggering this event /// @param expenditureId Id of the expenditure /// @param slot Expenditure slot of the payout being changed + /// @param chainId Chain id of the token being paid out /// @param token Token of the payout funding /// @param amount Amount of the payout funding - event ExpenditurePayoutSet(address agent, uint256 indexed expenditureId, uint256 indexed slot, address indexed token, uint256 amount); + event ExpenditurePayoutSet(address agent, uint256 indexed expenditureId, uint256 indexed slot, uint256 chainId, address indexed token, uint256 amount); /// @notice Event logged when an expenditure slot claim delay changes /// @param agent The address that is responsible for triggering this event diff --git a/contracts/colony/ColonyFunding.sol b/contracts/colony/ColonyFunding.sol index 9da7df4477..f23ad54dcc 100755 --- a/contracts/colony/ColonyFunding.sol +++ b/contracts/colony/ColonyFunding.sol @@ -652,7 +652,14 @@ contract ColonyFunding is decrementNonRewardPotsTotal(_chainId, _token, _amount); } - emit ColonyFundsMovedBetweenFundingPots(msgSender(), _fromPot, _toPot, _amount, _token); + emit ColonyFundsMovedBetweenFundingPots( + msgSender(), + _fromPot, + _toPot, + _amount, + _chainId, + _token + ); } function updatePayoutsWeCannotMakeAfterPotChange( @@ -734,7 +741,7 @@ contract ColonyFunding is setExpenditureSlotPayout(_id, _slots[i], _chainId, _token, _amounts[i]); runningTotal = (runningTotal - currentPayout) + _amounts[i]; - emit ExpenditurePayoutSet(msgSender(), _id, _slots[i], _token, _amounts[i]); + emit ExpenditurePayoutSet(msgSender(), _id, _slots[i], _chainId, _token, _amounts[i]); } // fundingPot.payouts[_token] = runningTotal;