diff --git a/packages/tasit-identity-contract/src/GnosisSafe.js b/packages/tasit-identity-contract/src/GnosisSafe.js index 4b7f1fbb..c83fbb53 100644 --- a/packages/tasit-identity-contract/src/GnosisSafe.js +++ b/packages/tasit-identity-contract/src/GnosisSafe.js @@ -3,6 +3,7 @@ const { Contract, ERC20, ERC721 } = Action; const { ERC20Detailed } = ERC20; const { ERC721Full } = ERC721; import GnosisSafeUtils from "./GnosisSafeUtils"; +import ActionUtils from "tasit-action/dist/contract/Utils.js"; import gnosisSafeABI from "../../tasit-contracts/abi/GnosisSafe.json"; import erc20ABI from "../../tasit-contracts/abi/MyERC20Full.json"; @@ -25,9 +26,23 @@ const operations = { const { CALL } = operations; +const areValidSigners = signers => { + const { isEthersJsSigner: isSigner } = ActionUtils; + const { isArray } = Array; + + if (!isArray(signers)) return false; + + const allAreValid = !signers.map(isSigner).includes(false); + + if (!allAreValid) return false; + + return true; +}; + // Extended Gnosis Safe wallet contract with higher-level functions export default class GnosisSafe extends Contract { #utils; + #signers; constructor(address, wallet) { const abi = gnosisSafeABI; @@ -35,14 +50,22 @@ export default class GnosisSafe extends Contract { this.#utils = new GnosisSafeUtils(this); } - transferERC20 = async (signers, tokenAddress, toAddress, value) => { + setSigners = signers => { + if (!areValidSigners(signers)) + throw new Error( + `Cannot set invalid signers for the Gnosis Safe contract.` + ); + + this.#signers = signers; + }; + + transferERC20 = async (tokenAddress, toAddress, value) => { const data = this.#utils.encodeFunctionCall(erc20ABI, "transfer", [ toAddress, value, ]); const etherValue = "0"; const action = await this.#executeTransaction( - signers, data, tokenAddress, etherValue @@ -50,7 +73,7 @@ export default class GnosisSafe extends Contract { return action; }; - transferNFT = async (signers, tokenAddress, toAddress, tokenId) => { + transferNFT = async (tokenAddress, toAddress, tokenId) => { const fromAddress = this.getAddress(); const data = this.#utils.encodeFunctionCall(erc721ABI, "safeTransferFrom", [ fromAddress, @@ -59,7 +82,6 @@ export default class GnosisSafe extends Contract { ]); const etherValue = "0"; const action = await this.#executeTransaction( - signers, data, tokenAddress, etherValue @@ -67,19 +89,14 @@ export default class GnosisSafe extends Contract { return action; }; - transferEther = async (signers, toAddress, value) => { + transferEther = async (toAddress, value) => { const data = "0x"; const etherValue = value; - const action = await this.#executeTransaction( - signers, - data, - toAddress, - etherValue - ); + const action = await this.#executeTransaction(data, toAddress, etherValue); return action; }; - addSignerWithThreshold = async (signers, newSignerAddress, newThreshold) => { + addSignerWithThreshold = async (newSignerAddress, newThreshold) => { const data = this.#utils.encodeFunctionCall( this.getABI(), "addOwnerWithThreshold", @@ -87,19 +104,21 @@ export default class GnosisSafe extends Contract { ); const to = this.getAddress(); const etherValue = "0"; - const action = await this.#executeTransaction( - signers, - data, - to, - etherValue - ); + const action = await this.#executeTransaction(data, to, etherValue); return action; }; // Note: Should we move this function to sync to keep same behavior as // contract's write functions that returns an Action object? // See more: https://github.com/tasitlabs/TasitSDK/issues/234 - #executeTransaction = async (signers, data, toAddress, etherValue) => { + #executeTransaction = async (data, toAddress, etherValue) => { + const signers = this.#signers; + + if (!signers) + throw new Error( + `Cannot send an action to Gnosis Safe contract without signers.` + ); + const to = toAddress; const operation = CALL; diff --git a/packages/tasit-identity-contract/src/GnosisSafe.test.js b/packages/tasit-identity-contract/src/GnosisSafe.test.js index c9383a98..0f6fa752 100644 --- a/packages/tasit-identity-contract/src/GnosisSafe.test.js +++ b/packages/tasit-identity-contract/src/GnosisSafe.test.js @@ -31,9 +31,12 @@ describe("GnosisSafe", () => { ephemeralWallet = Account.create(); + const signers = [johnWallet]; + // Contract deployment setup with john (accounts[9]) as the only owner // To change that, edit the file "tasit-contract/3rd-parties/gnosis/scripts/2_deploy_contracts.js" gnosisSafe = new GnosisSafe(GNOSIS_SAFE_ADDRESS); + gnosisSafe.setSigners(signers); erc20 = new ERC20Full(ERC20_ADDRESS); @@ -65,16 +68,11 @@ describe("GnosisSafe", () => { }); it("contract account should send ethers back to owner", async () => { - const signers = [johnWallet]; const { address: toAddress } = johnWallet; const value = ONE; gnosisSafe.setWallet(johnWallet); - const execTxAction = await gnosisSafe.transferEther( - signers, - toAddress, - value - ); + const execTxAction = await gnosisSafe.transferEther(toAddress, value); await execTxAction.waitForNonceToUpdate(); const balance = await provider.getBalance(GNOSIS_SAFE_ADDRESS); @@ -90,14 +88,12 @@ describe("GnosisSafe", () => { }); it("contract account should send ERC20 tokens back to owner", async () => { - const signers = [johnWallet]; const tokenAddress = ERC20_ADDRESS; const { address: toAddress } = johnWallet; const value = ONE; gnosisSafe.setWallet(johnWallet); const action = await gnosisSafe.transferERC20( - signers, tokenAddress, toAddress, value @@ -120,13 +116,11 @@ describe("GnosisSafe", () => { }); it("contract account should send NFT tokens back to owner", async () => { - const signers = [johnWallet]; const tokenAddress = NFT_ADDRESS; const { address: toAddress } = johnWallet; gnosisSafe.setWallet(johnWallet); const execTxAction = await gnosisSafe.transferNFT( - signers, tokenAddress, toAddress, tokenId @@ -147,13 +141,11 @@ describe("GnosisSafe", () => { const thresholdBefore = await gnosisSafe.getThreshold(); expect(`${thresholdBefore}`).to.equal(`1`); - const signers = [johnWallet]; const { address: newSignerAddress } = ephemeralWallet; const newThreshold = `2`; gnosisSafe.setWallet(johnWallet); const action = await gnosisSafe.addSignerWithThreshold( - signers, newSignerAddress, newThreshold );