From 16cb5def4277a41115f5108ba9e98525fb3c1149 Mon Sep 17 00:00:00 2001 From: Daniel Kronovet Date: Thu, 31 Oct 2024 13:18:34 -0700 Subject: [PATCH] Add _strict flag to makeArbitraryTransaction --- .../colony/ColonyArbitraryTransaction.sol | 5 +- contracts/colony/ColonyAuthority.sol | 5 +- contracts/colony/IColony.sol | 4 +- docs/interfaces/icolony.md | 3 +- docs/interfaces/imetacolony.md | 3 +- .../MetatransactionBroadcaster.js | 2 + .../colony-arbitrary-transactions.js | 56 +++++++++---------- test/contracts-network/colony-permissions.js | 2 +- test/contracts-network/colony-recovery.js | 2 +- test/cross-chain/cross-chain.js | 2 +- test/packages/metaTransactionBroadcaster.js | 12 +++- 11 files changed, 58 insertions(+), 38 deletions(-) diff --git a/contracts/colony/ColonyArbitraryTransaction.sol b/contracts/colony/ColonyArbitraryTransaction.sol index e111d2380c..60ffb9bc6a 100644 --- a/contracts/colony/ColonyArbitraryTransaction.sol +++ b/contracts/colony/ColonyArbitraryTransaction.sol @@ -36,7 +36,8 @@ contract ColonyArbitraryTransaction is ColonyStorage { function makeArbitraryTransaction( address _to, - bytes memory _action + bytes memory _action, + bool _strict ) public stoppable auth returns (bool) { // Prevent transactions to network contracts require(_to != address(this), "colony-cannot-target-self"); @@ -78,6 +79,8 @@ contract ColonyArbitraryTransaction is ColonyStorage { emit ArbitraryTransaction(_to, _action, res); + require(res || !_strict, "colony-arbitrary-transaction-failed"); + return res; } diff --git a/contracts/colony/ColonyAuthority.sol b/contracts/colony/ColonyAuthority.sol index 2323e50363..c7f97b3f99 100644 --- a/contracts/colony/ColonyAuthority.sol +++ b/contracts/colony/ColonyAuthority.sol @@ -98,7 +98,7 @@ contract ColonyAuthority is CommonAuthority { addRoleCapability(ROOT_ROLE, "upgradeExtension(bytes32,uint256)"); addRoleCapability(ROOT_ROLE, "deprecateExtension(bytes32,bool)"); addRoleCapability(ROOT_ROLE, "uninstallExtension(bytes32)"); - addRoleCapability(ROOT_ROLE, "makeArbitraryTransaction(address,bytes)"); + addRoleCapability(ROOT_ROLE, "makeArbitraryTransaction(address,bytes)"); // Deprecated addRoleCapability(ROOT_ROLE, "emitDomainReputationReward(uint256,address,int256)"); addRoleCapability(ROOT_ROLE, "emitSkillReputationReward(uint256,address,int256)"); addRoleCapability(ARBITRATION_ROLE, "transferStake(uint256,uint256,address,address,uint256,uint256,address)"); @@ -134,6 +134,9 @@ contract ColonyAuthority is CommonAuthority { addRoleCapability(ARBITRATION_ROLE, "finalizeExpenditureViaArbitration(uint256,uint256,uint256)"); addRoleCapability(ROOT_ROLE, "setColonyBridgeAddress(address)"); addRoleCapability(ROOT_ROLE, "initialiseReputationMining(uint256,bytes32,uint256)"); + + // Added in colony v17 (jade-lwss) + addRoleCapability(ROOT_ROLE, "makeArbitraryTransaction(address,bytes,bool)"); } function addRoleCapability(uint8 role, bytes memory sig) private { diff --git a/contracts/colony/IColony.sol b/contracts/colony/IColony.sol index 5bb51fd3ea..bd15c09177 100644 --- a/contracts/colony/IColony.sol +++ b/contracts/colony/IColony.sol @@ -54,10 +54,12 @@ interface IColony is IDSAuth, ColonyDataTypes, IRecovery, IBasicMetaTransaction, /// @notice Executes an arbitrary transaction /// @param _target Contract to receive the function call /// @param _action Bytes array encoding the function call and arguments + /// @param _strict Boolean indicating whether the transaction must succeed /// @return success Boolean indicating whether the transactions succeeded function makeArbitraryTransaction( address _target, - bytes memory _action + bytes memory _action, + bool _strict ) external returns (bool success); /// @notice Emit a metadata string for a transaction diff --git a/docs/interfaces/icolony.md b/docs/interfaces/icolony.md index 692efa6b09..160bf0096f 100644 --- a/docs/interfaces/icolony.md +++ b/docs/interfaces/icolony.md @@ -1049,7 +1049,7 @@ Lock the colony's token. Can only be called by a network-managed extension. |---|---|---| |timesLocked|uint256|The amount of times the token was locked -### ▸ `makeArbitraryTransaction(address _target, bytes memory _action):bool success` +### ▸ `makeArbitraryTransaction(address _target, bytes memory _action, bool _strict):bool success` Executes an arbitrary transaction @@ -1060,6 +1060,7 @@ Executes an arbitrary transaction |---|---|---| |_target|address|Contract to receive the function call |_action|bytes|Bytes array encoding the function call and arguments +|_strict|bool|Boolean indicating whether the transaction must succeed **Return Parameters** diff --git a/docs/interfaces/imetacolony.md b/docs/interfaces/imetacolony.md index 1fe35efde5..6ffb4f39bb 100644 --- a/docs/interfaces/imetacolony.md +++ b/docs/interfaces/imetacolony.md @@ -1087,7 +1087,7 @@ Lock the colony's token. Can only be called by a network-managed extension. |---|---|---| |timesLocked|uint256|The amount of times the token was locked -### ▸ `makeArbitraryTransaction(address _target, bytes memory _action):bool success` +### ▸ `makeArbitraryTransaction(address _target, bytes memory _action, bool _strict):bool success` Executes an arbitrary transaction @@ -1098,6 +1098,7 @@ Executes an arbitrary transaction |---|---|---| |_target|address|Contract to receive the function call |_action|bytes|Bytes array encoding the function call and arguments +|_strict|bool|Boolean indicating whether the transaction must succeed **Return Parameters** diff --git a/packages/metatransaction-broadcaster/MetatransactionBroadcaster.js b/packages/metatransaction-broadcaster/MetatransactionBroadcaster.js index 01ce22f313..a58ed99dcd 100644 --- a/packages/metatransaction-broadcaster/MetatransactionBroadcaster.js +++ b/packages/metatransaction-broadcaster/MetatransactionBroadcaster.js @@ -208,6 +208,7 @@ class MetatransactionBroadcaster { // Add the old makeSingleArbitraryTransaction and makeArbitraryTransactions to the abi const iface = new ethers.utils.Interface([ + "function makeArbitraryTransaction(address,bytes)", "function makeSingleArbitraryTransaction(address,bytes)", "function makeArbitraryTransactions(address[],bytes[],bool)", ]); @@ -223,6 +224,7 @@ class MetatransactionBroadcaster { // If it's an arbitrary transaction... if ( tx.signature === "makeArbitraryTransaction(address,bytes)" || + tx.signature === "makeArbitraryTransaction(address,bytes,bool)" || tx.signature === "makeSingleArbitraryTransaction(address,bytes)" || tx.signature === "makeArbitraryTransactions(address[],bytes[],bool)" ) { diff --git a/test/contracts-network/colony-arbitrary-transactions.js b/test/contracts-network/colony-arbitrary-transactions.js index 7b98cd9329..f1806dadf4 100644 --- a/test/contracts-network/colony-arbitrary-transactions.js +++ b/test/contracts-network/colony-arbitrary-transactions.js @@ -40,7 +40,7 @@ contract("Colony Arbitrary Transactions", (accounts) => { const action = await encodeTxData(token, "mint", [WAD]); const balancePre = await token.balanceOf(colony.address); - const tx = await colony.makeArbitraryTransaction(token.address, action); + const tx = await colony.makeArbitraryTransaction(token.address, action, false); await expectEvent(tx, "ArbitraryTransaction(address,bytes,bool)", [token.address, action, true]); @@ -53,8 +53,8 @@ contract("Colony Arbitrary Transactions", (accounts) => { const action2 = await encodeTxData(token, "mint", [WAD.muln(2)]); const balancePre = await token.balanceOf(colony.address); - const arbitraryAction = await encodeTxData(colony, "makeArbitraryTransaction", [token.address, action]); - const arbitraryAction2 = await encodeTxData(colony, "makeArbitraryTransaction", [token.address, action2]); + const arbitraryAction = await encodeTxData(colony, "makeArbitraryTransaction", [token.address, action, false]); + const arbitraryAction2 = await encodeTxData(colony, "makeArbitraryTransaction", [token.address, action2, false]); const tx = await colony.multicall([arbitraryAction, arbitraryAction2]); @@ -68,15 +68,15 @@ contract("Colony Arbitrary Transactions", (accounts) => { it("should not be able to make arbitrary transactions if not root", async () => { const action = await encodeTxData(token, "mint", [WAD]); - await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action, { from: USER1 }), "ds-auth-unauthorized"); + await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action, false, { from: USER1 }), "ds-auth-unauthorized"); }); it("should not be able to make arbitrary transactions to a colony itself", async () => { - await checkErrorRevert(colony.makeArbitraryTransaction(colony.address, "0x0"), "colony-cannot-target-self"); + await checkErrorRevert(colony.makeArbitraryTransaction(colony.address, 0x0, false), "colony-cannot-target-self"); }); it("should not be able to make arbitrary transactions to a user address", async () => { - await checkErrorRevert(colony.makeArbitraryTransaction(accounts[0], "0x0"), "colony-to-must-be-contract"); + await checkErrorRevert(colony.makeArbitraryTransaction(accounts[0], 0x0, false), "colony-to-must-be-contract"); }); it("should not be able to make arbitrary transactions to network or token locking", async () => { @@ -86,14 +86,14 @@ contract("Colony Arbitrary Transactions", (accounts) => { const action1 = await encodeTxData(colonyNetwork, "addSkill", [0]); const action2 = await encodeTxData(tokenLocking, "lockToken", [token.address]); - await checkErrorRevert(colony.makeArbitraryTransaction(colonyNetwork.address, action1), "colony-cannot-target-network"); - await checkErrorRevert(colony.makeArbitraryTransaction(tokenLocking.address, action2), "colony-cannot-target-token-locking"); + await checkErrorRevert(colony.makeArbitraryTransaction(colonyNetwork.address, action1, false), "colony-cannot-target-network"); + await checkErrorRevert(colony.makeArbitraryTransaction(tokenLocking.address, action2, false), "colony-cannot-target-token-locking"); }); it("if an arbitrary transaction is made to approve tokens, then tokens needed for approval cannot be moved out of the main pot", async () => { await fundColonyWithTokens(colony, token, 100); const action1 = await encodeTxData(token, "approve", [USER0, 50]); - await colony.makeArbitraryTransaction(token.address, action1); + await colony.makeArbitraryTransaction(token.address, action1, false); await colony.moveFundsBetweenPots(1, UINT256_MAX, 1, UINT256_MAX, UINT256_MAX, 1, 0, 50, token.address); await checkErrorRevert( colony.moveFundsBetweenPots(1, UINT256_MAX, 1, UINT256_MAX, UINT256_MAX, 1, 0, 50, token.address), @@ -109,7 +109,7 @@ contract("Colony Arbitrary Transactions", (accounts) => { tokens can only be moved from main pot that weren't part of the allowance`, async () => { await fundColonyWithTokens(colony, token, 100); const action1 = await encodeTxData(token, "approve", [USER0, 20]); - await colony.makeArbitraryTransaction(token.address, action1); + await colony.makeArbitraryTransaction(token.address, action1, false); // Use allowance await token.transferFrom(colony.address, USER0, 20, { from: USER0 }); // Approval tracking still thinks it has to reserve 20 @@ -141,9 +141,9 @@ contract("Colony Arbitrary Transactions", (accounts) => { await fundColonyWithTokens(colony, token, 100); const action1 = await encodeTxData(token, "approve", [USER0, 300]); // Not enough tokens at all - await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action1), "colony-approval-exceeds-balance"); + await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action1, false), "colony-approval-exceeds-balance"); await fundColonyWithTokens(colony, token, 1000); - await colony.makeArbitraryTransaction(token.address, action1); + await colony.makeArbitraryTransaction(token.address, action1, false); // They are now approved for 300. let approval = await colony.getTokenApproval(token.address, USER0); expect(approval).to.be.eq.BN(300); @@ -152,14 +152,14 @@ contract("Colony Arbitrary Transactions", (accounts) => { const action2 = await encodeTxData(token, "approve", [USER0, 900]); // User was approved for 300, we now approve them for 900. There are enough tokens to cover this, even though 900 + 300 > 1100, the balance of the pot - await colony.makeArbitraryTransaction(token.address, action2); + await colony.makeArbitraryTransaction(token.address, action2, false); approval = await colony.getTokenApproval(token.address, USER0); expect(approval).to.be.eq.BN(900); allApprovals = await colony.getTotalTokenApproval(token.address); expect(allApprovals).to.be.eq.BN(900); // Set them back to 300 - await colony.makeArbitraryTransaction(token.address, action1); + await colony.makeArbitraryTransaction(token.address, action1, false); approval = await colony.getTokenApproval(token.address, USER0); expect(approval).to.be.eq.BN(300); allApprovals = await colony.getTotalTokenApproval(token.address); @@ -167,10 +167,10 @@ contract("Colony Arbitrary Transactions", (accounts) => { // Cannot approve someone else for 900 const action3 = await encodeTxData(token, "approve", [USER1, 900]); - await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action3), "colony-approval-exceeds-balance"); + await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action3, false), "colony-approval-exceeds-balance"); // But can for 800 const action4 = await encodeTxData(token, "approve", [USER1, 800]); - await colony.makeArbitraryTransaction(token.address, action4); + await colony.makeArbitraryTransaction(token.address, action4, false); approval = await colony.getTokenApproval(token.address, USER1); expect(approval).to.be.eq.BN(800); allApprovals = await colony.getTotalTokenApproval(token.address); @@ -212,29 +212,29 @@ contract("Colony Arbitrary Transactions", (accounts) => { 0, ]); - await checkErrorRevert(colony.makeArbitraryTransaction(oneTxPayment.address, action), "colony-cannot-target-extensions"); + await checkErrorRevert(colony.makeArbitraryTransaction(oneTxPayment.address, action, false), "colony-cannot-target-extensions"); // But other colonies can const { colony: otherColony } = await setupRandomColony(colonyNetwork); await colony.setUserRoles(1, UINT256_MAX, otherColony.address, 1, ROLES); - await otherColony.makeArbitraryTransaction(oneTxPayment.address, action); + await otherColony.makeArbitraryTransaction(oneTxPayment.address, action, false); }); it("when burning tokens, can burn own tokens with burn(amount) up to the amount unspoken for in root pot", async () => { await fundColonyWithTokens(colony, token, 100); const action1 = await encodeTxData(token, "approve", [USER0, 60]); - await colony.makeArbitraryTransaction(token.address, action1); + await colony.makeArbitraryTransaction(token.address, action1, false); let potBalance = await colony.getFundingPotBalance(1, token.address); expect(potBalance).to.be.eq.BN(100); const action2 = await encodeTxData(token, "burn", [100]); // Can't burn 100 as 60 are reserved - await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action2), "colony-not-enough-tokens"); + await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action2, false), "colony-not-enough-tokens"); // Can burn 40 const action3 = await encodeTxData(token, "burn", [40]); - await colony.makeArbitraryTransaction(token.address, action3); + await colony.makeArbitraryTransaction(token.address, action3, false); potBalance = await colony.getFundingPotBalance(1, token.address); expect(potBalance).to.be.eq.BN(60); }); @@ -242,17 +242,17 @@ contract("Colony Arbitrary Transactions", (accounts) => { it("when transferring tokens, can transfer own tokens with transfer(dst, amount) up to the amount unspoken for in root pot", async () => { await fundColonyWithTokens(colony, token, 100); const action1 = await encodeTxData(token, "approve", [USER0, 60]); - await colony.makeArbitraryTransaction(token.address, action1); + await colony.makeArbitraryTransaction(token.address, action1, false); let potBalance = await colony.getFundingPotBalance(1, token.address); expect(potBalance).to.be.eq.BN(100); const action2 = await encodeTxData(token, "transfer", [USER0, 100]); // Can't transfer 100 as 60 are reserved - await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action2), "colony-not-enough-tokens"); + await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action2, false), "colony-not-enough-tokens"); // Can transfer 40 const action3 = await encodeTxData(token, "transfer", [USER0, 40]); - await colony.makeArbitraryTransaction(token.address, action3); + await colony.makeArbitraryTransaction(token.address, action3, false); potBalance = await colony.getFundingPotBalance(1, token.address); expect(potBalance).to.be.eq.BN(60); const userBalance = await token.balanceOf(USER0); @@ -263,7 +263,7 @@ contract("Colony Arbitrary Transactions", (accounts) => { await token.mint(100); await token.approve(colony.address, 100); const action1 = await encodeTxData(token, "burn", [USER0, 60]); - await colony.makeArbitraryTransaction(token.address, action1); + await colony.makeArbitraryTransaction(token.address, action1, false); const userBalance = await token.balanceOf(USER0); expect(userBalance).to.be.eq.BN(40); @@ -273,7 +273,7 @@ contract("Colony Arbitrary Transactions", (accounts) => { await token.mint(100); await token.approve(colony.address, 100); const action1 = await encodeTxData(token, "transferFrom", [USER0, colony.address, 60]); - await colony.makeArbitraryTransaction(token.address, action1); + await colony.makeArbitraryTransaction(token.address, action1, false); const userBalance = await token.balanceOf(USER0); expect(userBalance).to.be.eq.BN(40); @@ -283,11 +283,11 @@ contract("Colony Arbitrary Transactions", (accounts) => { it("cannot burn own tokens with burn(guy, amount)", async () => { const action = await encodeTxData(token, "burn", [colony.address, 60]); - await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action), "colony-cannot-spend-own-allowance"); + await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action, false), "colony-cannot-spend-own-allowance"); }); it("cannot transfer own tokens with transferFrom(from, to, amount)", async () => { const action = await encodeTxData(token, "transferFrom", [colony.address, USER0, 60]); - await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action), "colony-cannot-spend-own-allowance"); + await checkErrorRevert(colony.makeArbitraryTransaction(token.address, action, false), "colony-cannot-spend-own-allowance"); }); }); diff --git a/test/contracts-network/colony-permissions.js b/test/contracts-network/colony-permissions.js index 165cb7991a..a094fd0d8e 100644 --- a/test/contracts-network/colony-permissions.js +++ b/test/contracts-network/colony-permissions.js @@ -287,7 +287,7 @@ contract("ColonyPermissions", (accounts) => { await checkErrorRevert(colony.installExtension(HASHZERO, ADDRESS_ZERO, { from: USER1 }), "ds-auth-unauthorized"); await checkErrorRevert(colony.addLocalSkill({ from: USER1 }), "ds-auth-unauthorized"); await checkErrorRevert(colony.deprecateLocalSkill(0, true, { from: USER2 }), "ds-auth-unauthorized"); - await checkErrorRevert(colony.makeArbitraryTransaction(ADDRESS_ZERO, HASHZERO, { from: USER1 }), "ds-auth-unauthorized"); + await checkErrorRevert(colony.makeArbitraryTransaction(ADDRESS_ZERO, HASHZERO, false, { from: USER1 }), "ds-auth-unauthorized"); await checkErrorRevert(colony.startNextRewardPayout(ADDRESS_ZERO, HASHZERO, HASHZERO, 0, [HASHZERO], { from: USER1 }), "ds-auth-unauthorized"); }); diff --git a/test/contracts-network/colony-recovery.js b/test/contracts-network/colony-recovery.js index 810ad0b888..9b045ae4a6 100644 --- a/test/contracts-network/colony-recovery.js +++ b/test/contracts-network/colony-recovery.js @@ -203,7 +203,7 @@ contract("Colony Recovery", (accounts) => { await checkErrorRevert(metaColony.burnTokens(ADDRESS_ZERO, 0), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.registerColonyLabel("", ""), "colony-in-recovery-mode"); await checkErrorRevert(metaColony.updateColonyOrbitDB(""), "colony-in-recovery-mode"); - await checkErrorRevert(metaColony.makeArbitraryTransaction(ADDRESS_ZERO, HASHZERO), "colony-in-recovery-mode"); + await checkErrorRevert(metaColony.makeArbitraryTransaction(ADDRESS_ZERO, HASHZERO, false), "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 7331573a88..2db9f0e69a 100644 --- a/test/cross-chain/cross-chain.js +++ b/test/cross-chain/cross-chain.js @@ -440,7 +440,7 @@ contract("Cross-chain", (accounts) => { console.log("tx to home bridge address:", homeBridge.address); - const tx = await homeColony.makeArbitraryTransaction(homeBridge.address, txDataToBeSentToAMB); + const tx = await homeColony.makeArbitraryTransaction(homeBridge.address, txDataToBeSentToAMB, true); await tx.wait(); await p; // Check balances diff --git a/test/packages/metaTransactionBroadcaster.js b/test/packages/metaTransactionBroadcaster.js index 080c4500d4..36049a76c1 100644 --- a/test/packages/metaTransactionBroadcaster.js +++ b/test/packages/metaTransactionBroadcaster.js @@ -124,13 +124,17 @@ contract("Metatransaction broadcaster", (accounts) => { let valid = await broadcaster.isColonyFamilyTransactionAllowed(colony.address, txData); expect(valid).to.be.equal(false); - txData = await colony.contract.methods.makeArbitraryTransaction(colony.address, "0x00000000").encodeABI(); + txData = await encodeTxData(colony, "makeArbitraryTransaction(address,bytes)", [colony.address, "0x00000000"]); valid = await broadcaster.isColonyFamilyTransactionAllowed(colony.address, txData); expect(valid).to.be.equal(false); txData = await encodeTxData(colony, "makeSingleArbitraryTransaction(address,bytes)", [colony.address, "0x00000000"]); valid = await broadcaster.isColonyFamilyTransactionAllowed(colony.address, txData); expect(valid).to.be.equal(false); + + txData = await colony.contract.methods.makeArbitraryTransaction(colony.address, "0x00000000", false).encodeABI(); + valid = await broadcaster.isColonyFamilyTransactionAllowed(colony.address, txData); + expect(valid).to.be.equal(false); }); it.skip(`transactions to the forbidden arbitrary transaction methods are allowed only if @@ -147,7 +151,7 @@ contract("Metatransaction broadcaster", (accounts) => { let valid = await broadcaster.isColonyFamilyTransactionAllowed(colony.address, txData); expect(valid).to.be.equal(true); - txData = await colony.contract.methods.makeArbitraryTransaction(BINANCE_BRIDGE_ADDRESS, ambCall).encodeABI(); + txData = await encodeTxData(colony, "makeArbitraryTransaction(address,bytes)", [ETHEREUM_BRIDGE_ADDRESS, ambCall]); valid = await broadcaster.isColonyFamilyTransactionAllowed(colony.address, txData); expect(valid).to.be.equal(true); @@ -155,6 +159,10 @@ contract("Metatransaction broadcaster", (accounts) => { valid = await broadcaster.isColonyFamilyTransactionAllowed(colony.address, txData); expect(valid).to.be.equal(true); + txData = await colony.contract.methods.makeArbitraryTransaction(BINANCE_BRIDGE_ADDRESS, ambCall, false).encodeABI(); + valid = await broadcaster.isColonyFamilyTransactionAllowed(colony.address, txData); + expect(valid).to.be.equal(true); + // Going to a bridge, but not the right function call txData = await encodeTxData(colony, "makeSingleArbitraryTransaction(address,bytes)", [BINANCE_BRIDGE_ADDRESS, "0x00000000"]); valid = await broadcaster.isColonyFamilyTransactionAllowed(colony.address, txData);