From 6144b691a533679a52e1bd7f3fbe1f3067c4f3be Mon Sep 17 00:00:00 2001 From: Juan Enrique Alcaraz Date: Fri, 4 Aug 2023 12:44:58 +0200 Subject: [PATCH] Improve module readability and add all docs --- src/safe.js | 769 ++++++++++++++++++++++++---------------------- src/utils.js | 9 +- test/safe.test.js | 4 +- 3 files changed, 406 insertions(+), 376 deletions(-) diff --git a/src/safe.js b/src/safe.js index 6de0e3dc..4f69b9a3 100644 --- a/src/safe.js +++ b/src/safe.js @@ -13,15 +13,12 @@ import loop from '~/common/loop'; import safeContractAbis from '~/common/safeContractAbis'; /** - * Safe submodule to deploy and interact with the Gnosis Safe. - * + * Module to manage Gnosis Safes * @access private - * * @param {Web3} web3 - Web3 instance * @param {Object} contracts - common contract instances * @param {Object} utils - utils module instance * @param {Object} globalOptions - global core options - * * @return {Object} - safe module instance */ export default function createSafeModule( @@ -39,7 +36,12 @@ export default function createSafeModule( } = globalOptions; const { safeMaster, proxyFactory } = contracts; - const customContracts = { + /** + * Custom contracts configuration + * @access private + * @type {ContractNetworkConfig} + */ + const _customContracts = { safeMasterCopyAddress: safeMasterAddress, safeProxyFactoryAddress: proxyFactoryAddress, fallbackHandlerAddress: fallbackHandlerAddress, @@ -48,31 +50,57 @@ export default function createSafeModule( ...safeContractAbis, }; - const getContractNetworks = () => + /** + * Prepare the contract networks data for the safe-core-sdk + * @access private + * @return {ContractNetworksConfig} - contract networks data + */ + const _getContractNetworks = () => web3.eth.getChainId().then((chainId) => ({ - [chainId]: customContracts, + [chainId]: _customContracts, })); - const createEthAdapter = (signerAddress) => + /** + * Create the ethAdapter for the safe-core-sdk + * @access private + * @param {string} signerAddress - Address of the transactions signer + * @return {Web3Adapter} - ethAdapter + */ + const _createEthAdapter = (signerAddress) => new Web3Adapter({ web3, signerAddress }); - const createSafeFactory = (signerAddress) => - getContractNetworks().then((contractNetworks) => + /** + * Create a Safe factory + * @access private + * @param {string} signerAddress - Address of the transactions signer + * @return {SafeFactory} - Safe factory + */ + const _createSafeFactory = (signerAddress) => + _getContractNetworks().then((contractNetworks) => SafeFactory.create({ - ethAdapter: createEthAdapter(signerAddress), + ethAdapter: _createEthAdapter(signerAddress), contractNetworks, }), ); - const getSafeSdk = ({ + /** + * Create a Safe instance + * @access private + * @param {PredictedSafeProps} config.predictedSafe - Config for a predicted Safe + * @param {string} config.safeAddress - Address of the Safe if it is an existing one + * @param {string} config.signerAddress - Address of the transactions signer for the adapter + * @param {Object} config.params - Params to overwrite the Safe.create method + * @return {Safe} - Instance of a Safe + */ + const _getSafeSdk = ({ predictedSafe, safeAddress, signerAddress, params = {}, }) => - getContractNetworks().then((contractNetworks) => + _getContractNetworks().then((contractNetworks) => Safe.create({ - ethAdapter: createEthAdapter(signerAddress), + ethAdapter: _createEthAdapter(signerAddress), contractNetworks, ...(predictedSafe && { predictedSafe }), ...(safeAddress && { safeAddress }), @@ -80,12 +108,20 @@ export default function createSafeModule( }), ); - const prepareSafeTransaction = async ({ safeSdk, safeTx, signerAddress }) => + /** + * Prepare a Safe transaction to be funded. The transaction is signed and then encoded + * @access private + * @param {Safe} config.safeSdk - Safe instance + * @param {SafeTransaction} config.SafeTx - Params to overwrite the Safe.create method + * @param {string} config.signerAddress - Address of the transactions signer for the adapter + * @return {string} - Encoded signed transaction + */ + const _prepareSafeTransaction = async ({ safeSdk, safeTx, signerAddress }) => Promise.all([ _getSafeContract({ - ethAdapter: createEthAdapter(signerAddress), + ethAdapter: _createEthAdapter(signerAddress), safeVersion: await safeSdk.getContractVersion(), - customContracts, + customContracts: _customContracts, }), safeSdk.signTransaction(safeTx), ]).then(([safeSingletonContract, signedSafeTx]) => @@ -104,17 +140,14 @@ export default function createSafeModule( ); /** - * Predict Safe address - * + * Predict a Safe address * @access private - * * @param {string} ownerAddress - Safe owner address - * @param {number} nonce - Safe creation salt nonce - * + * @param {number} nonce - Safe saltNonce for deterministic address generation * @return {string} - predicted Safe address */ - const predictAddress = (ownerAddress, nonce) => - createSafeFactory(ownerAddress).then((safeFactory) => + const _predictAddress = (ownerAddress, nonce) => + _createSafeFactory(ownerAddress).then((safeFactory) => safeFactory.predictSafeAddress( { owners: [ownerAddress], @@ -124,12 +157,89 @@ export default function createSafeModule( ), ); - const isDeployed = (safeAddress) => - createEthAdapter().isContractDeployed(safeAddress); + /** + * Check if a Safe address is deployed + * @access private + * @param {string} safeAddress - Safe address + * @return {boolean} - if Safe is deployed + */ + const _isDeployed = (safeAddress) => + _createEthAdapter().isContractDeployed(safeAddress); + + /** + * Get Safe version + * @access private + * @param {string} safeAddress + * @return {string} - version of the Safe + */ + const _getVersion = (safeAddress) => + _getSafeSdk({ safeAddress }).then((safeSdk) => + safeSdk.getContractVersion(), + ); + + /** + * Add an owner to a Safe + * @namespace core.safe.addOwner + * @param {Object} account - web3 account instance + * @param {Object} userOptions - options + * @param {string} userOptions.safeAddress - Safe address + * @param {string} userOptions.ownerAddress - owner address to be added + * @return {RelayResponse} - transaction response + */ + const addOwner = async (account, userOptions) => { + checkAccount(web3, account); + + const { safeAddress, ownerAddress } = checkOptions(userOptions, { + safeAddress: { + type: web3.utils.checkAddressChecksum, + }, + ownerAddress: { + type: web3.utils.checkAddressChecksum, + }, + }); + + const safeSdk = await _getSafeSdk({ + safeAddress, + signerAddress: account.address, + }); + + return safeSdk + .createAddOwnerTx({ ownerAddress }) + .then((safeTx) => + _prepareSafeTransaction({ + safeTx, + safeSdk, + signerAddress: account.address, + }), + ) + .then((data) => + utils.sendTransaction({ + target: safeAddress, + data, + }), + ); + }; + + /** + * Deploy a new Safe + * @namespace core.safe.deploySafe + * @param {Object} account - web3 account instance + * @param {Object} userOptions - options + * @param {number} userOptions.nonce - nonce to predict address + * @throws {SafeDeployedError} - Safe must not exist + * @return {string} - Safe address + */ + const deploySafe = async (account, userOptions) => { + checkAccount(web3, account); - const deploySafe = async (ownerAddress, nonce) => { - const safeAddress = await predictAddress(ownerAddress, nonce); - const isSafeDeployed = await isDeployed(safeAddress); + const { nonce } = checkOptions(userOptions, { + nonce: { + type: 'number', + }, + }); + + const safeAddress = await _predictAddress(account.address, nonce); + const isSafeDeployed = await _isDeployed(safeAddress); if (isSafeDeployed) { throw new SafeDeployedError( @@ -139,7 +249,7 @@ export default function createSafeModule( const initializer = safeMaster.methods .setup( - [ownerAddress], + [account.address], 1, ZERO_ADDRESS, '0x', @@ -162,375 +272,288 @@ export default function createSafeModule( }; /** - * Helper method to get the Safe version. - * - * @access private - * - * @param {string} safeAddress - * - * @return {string} - version of the Safe + * List all Safe addresses of an owner + * @namespace core.safe.getAddresses + * @param {Object} account - web3 account instance + * @param {Object} userOptions - options + * @param {string} userOptions.ownerAddress - Safe owner address + * @return {string[]} - List of Safe addresses */ - const getVersion = (safeAddress) => - getSafeSdk({ safeAddress }).then((safeSdk) => safeSdk.getContractVersion()); + const getAddresses = (account, userOptions) => { + checkAccount(web3, account); + + const options = checkOptions(userOptions, { + ownerAddress: { + type: web3.utils.checkAddressChecksum, + }, + }); + + return utils + .requestIndexedDB('safe_addresses', options) + .then((response) => + response && response.user + ? response.user.safeAddresses.map((address) => + web3.utils.toChecksumAddress(address), + ) + : [], + ); + }; /** - * Helper method to receive a list of all Gnosis Safe owners. - * - * @access private - * - * @param {string} safeAddress - * - * @return {string[]} - array of owner addresses + * Get all Safe owners + * @namespace core.safe.getOwners + * @param {Object} account - web3 account instance + * @param {Object} userOptions - options + * @param {string} userOptions.safeAddress - Safe address + * @return {string[]} - list of owner addresses */ - const getOwners = (safeAddress) => - getSafeSdk({ safeAddress }).then((safeSdk) => safeSdk.getOwners()); + const getOwners = (account, userOptions) => { + checkAccount(web3, account); - return { - /** - * Predict Safe address. - * - * @namespace core.safe.predictAddress - * - * @param {Object} account - web3 account instance - * @param {Object} userOptions - options - * @param {number} userOptions.nonce - nonce to predict address - * - * @return {string} - Predicted Gnosis Safe address - */ - predictAddress: async (account, userOptions) => { - checkAccount(web3, account); - - const { nonce } = checkOptions(userOptions, { - nonce: { - type: 'number', - }, - }); + const { safeAddress } = checkOptions(userOptions, { + safeAddress: { + type: web3.utils.checkAddressChecksum, + }, + }); - return predictAddress(account.address, nonce); - }, - - /** - * Deploy a new Safe with the Relayer. - * - * @namespace core.safe.deploySafe - * - * @param {Object} account - web3 account instance - * @param {Object} userOptions - options - * @param {number} userOptions.nonce - nonce to predict address - * - * @return {string} - Gnosis Safe address - */ - deploySafe: (account, userOptions) => { - checkAccount(web3, account); - - const { nonce } = checkOptions(userOptions, { - nonce: { - type: 'number', - }, - }); + return _getSafeSdk({ safeAddress }).then((safeSdk) => safeSdk.getOwners()); + }; - return deploySafe(account.address, nonce); - }, + /** + * Get Safe version + * @namespace core.safe.getVersion + * @param {Object} account - web3 account instance + * @param {Object} userOptions - options + * @param {string} userOptions.safeAddress - Safe address + * @return {string} - Safe version + */ + const getVersion = (account, userOptions) => { + checkAccount(web3, account); - isDeployed: (account, userOptions) => { - checkAccount(web3, account); + const { safeAddress } = checkOptions(userOptions, { + safeAddress: { + type: web3.utils.checkAddressChecksum, + }, + }); - const { safeAddress } = checkOptions(userOptions, { - safeAddress: { - type: web3.utils.checkAddressChecksum, - }, - }); + return _getVersion(safeAddress); + }; - return isDeployed(safeAddress); - }, - - /** - * Returns a list of all owners of the given Gnosis Safe. - * - * @namespace core.safe.getOwners - * - * @param {Object} account - web3 account instance - * @param {Object} userOptions - options - * @param {number} userOptions.safeAddress - address of the Gnosis Safe - * - * @return {string[]} - array of owner addresses - */ - getOwners: (account, userOptions) => { - checkAccount(web3, account); - - const { safeAddress } = checkOptions(userOptions, { - safeAddress: { - type: web3.utils.checkAddressChecksum, - }, - }); + /** + * Check if a Safe address is deployed + * @namespace core.safe.isDeployed + * @param {Object} account - web3 account instance + * @param {Object} userOptions - options + * @param {string} userOptions.safeAddress - Safe address + * @return {boolean} - if Safe is deployed + */ + const isDeployed = (account, userOptions) => { + checkAccount(web3, account); - return getOwners(safeAddress); - }, - - /** - * Add an address as an owner of a given Gnosis Safe. - * - * @namespace core.safe.addOwner - * - * @param {Object} account - web3 account instance - * @param {Object} userOptions - options - * @param {number} userOptions.safeAddress - address of the Gnosis Safe - * @param {number} userOptions.ownerAddress - owner address to be added - * - * @return {string} - transaction hash - */ - addOwner: async (account, userOptions) => { - checkAccount(web3, account); - - const { safeAddress, ownerAddress } = checkOptions(userOptions, { - safeAddress: { - type: web3.utils.checkAddressChecksum, - }, - ownerAddress: { - type: web3.utils.checkAddressChecksum, - }, - }); + const { safeAddress } = checkOptions(userOptions, { + safeAddress: { + type: web3.utils.checkAddressChecksum, + }, + }); - const safeSdk = await getSafeSdk({ - safeAddress, - signerAddress: account.address, - }); + return _isDeployed(safeAddress); + }; - return safeSdk - .createAddOwnerTx({ ownerAddress }) - .then((safeTx) => - prepareSafeTransaction({ - safeTx, - safeSdk, - signerAddress: account.address, - }), - ) - .then((data) => - utils.sendTransaction({ - target: safeAddress, - data, - }), - ); - }, - - /** - * Remove owner of a given Gnosis Safe. - * - * @namespace core.safe.removeOwner - * - * @param {Object} account - web3 account instance - * @param {Object} userOptions - options - * @param {number} userOptions.safeAddress - address of the Gnosis Safe - * @param {number} userOptions.ownerAddress - owner address to be removed - * - * @return {string} - transaction hash - */ - removeOwner: async (account, userOptions) => { - checkAccount(web3, account); - - const { safeAddress, ownerAddress } = checkOptions(userOptions, { - safeAddress: { - type: web3.utils.checkAddressChecksum, - }, - ownerAddress: { - type: web3.utils.checkAddressChecksum, - }, - }); + /** + * Predict a Safe address + * @namespace core.safe.predictAddress + * @param {Object} account - web3 account instance + * @param {Object} userOptions - options + * @param {number} userOptions.nonce - nonce to predict address + * @return {string} - predicted Safe address + */ + const predictAddress = async (account, userOptions) => { + checkAccount(web3, account); + + const { nonce } = checkOptions(userOptions, { + nonce: { + type: 'number', + }, + }); + + return _predictAddress(account.address, nonce); + }; + + /** + * Remove an owner from a Safe + * @namespace core.safe.removeOwner + * @param {Object} account - web3 account instance + * @param {Object} userOptions - options + * @param {string} userOptions.safeAddress - Safe address + * @param {string} userOptions.ownerAddress - owner address to be removed + * @return {RelayResponse} - transaction response + */ + const removeOwner = async (account, userOptions) => { + checkAccount(web3, account); + + const { safeAddress, ownerAddress } = checkOptions(userOptions, { + safeAddress: { + type: web3.utils.checkAddressChecksum, + }, + ownerAddress: { + type: web3.utils.checkAddressChecksum, + }, + }); + + const safeSdk = await _getSafeSdk({ + safeAddress, + signerAddress: account.address, + }); - const safeSdk = await getSafeSdk({ + return safeSdk + .createRemoveOwnerTx({ + ownerAddress, + threshold: await safeSdk.getThreshold(), + }) + .then((safeTx) => + _prepareSafeTransaction({ + safeTx, + safeSdk, + signerAddress: account.address, + }), + ) + .then((data) => + utils.sendTransaction({ + target: safeAddress, + data, + }), + ); + }; + + /** + * Update Safe version to the last version (v1.3.0) by + * changing the Master Copy and setting the Fallback Handler + * @namespace core.safe.updateToLastVersion + * @param {Object} account - web3 account instance + * @param {Object} userOptions - options + * @param {string} userOptions.safeAddress - Safe address + * @return {string} - Safe version + */ + const updateToLastVersion = async (account, userOptions) => { + checkAccount(web3, account); + + const { safeAddress } = checkOptions(userOptions, { + safeAddress: { + type: web3.utils.checkAddressChecksum, + }, + }); + let safeVersion = await _getVersion(safeAddress); + + if (safeVersion !== SAFE_LAST_VERSION) { + // References: + // https://github.com/safe-global/web-core/blob/main/src/services/tx/safeUpdateParams.ts + // https://github.com/safe-global/safe-react/blob/main/src/logic/safe/utils/upgradeSafe.ts + + // Get the Safe contract with version v1.1.1+Circles + const safeInstance = getSafeCRCVersionContract(web3, safeAddress); + const safeSdk = await _getSafeSdk({ safeAddress, signerAddress: account.address, }); - return safeSdk - .createRemoveOwnerTx({ - ownerAddress, - threshold: await safeSdk.getThreshold(), - }) - .then((safeTx) => - prepareSafeTransaction({ - safeTx, - safeSdk, - signerAddress: account.address, - }), - ) - .then((data) => - utils.sendTransaction({ - target: safeAddress, - data, - }), - ); - }, - - /** - * Requests the relayer to deploy a Safe for an organization. The relayer - * funds the deployment of this Safe when the account is already known and - * verified / already has a deployed Safe from before. - * - * @namespace core.safe.deployForOrganization - * - * @param {Object} account - web3 account instance - * @param {Object} userOptions - options - * @param {number} userOptions.safeAddress - to-be-deployed Safe address - * - * @return {boolean} - returns true when successful - */ - // TODO: this method is missing to be implemented because it will be moved and replaced into organization.js - deployForOrganization: async (account, userOptions) => { - checkAccount(web3, account); - - const options = checkOptions(userOptions, { - safeAddress: { - type: web3.utils.checkAddressChecksum, - }, + // First we change the Master Copy to v1.3.0 + // @ts-expect-error this was removed in 1.3.0 but we need to support it for older safe versions + await utils.sendTransaction({ + target: safeAddress, + data: await safeSdk + .createTransaction({ + safeTransactionData: { + to: safeAddress, + value: 0, + data: safeInstance.methods + .changeMasterCopy(safeMaster.options.address) + .encodeABI(), + }, + }) + .then((safeTx) => + _prepareSafeTransaction({ + safeTx, + safeSdk, + signerAddress: account.address, + }), + ), }); - await utils.requestRelayer({ - path: ['safes', options.safeAddress, 'organization'], - version: 2, - method: 'PUT', + await utils.sendTransaction({ + target: safeAddress, + data: await safeSdk + .createTransaction({ + safeTransactionData: { + to: safeAddress, + value: 0, + data: safeInstance.methods + .setFallbackHandler(fallbackHandlerAddress) + .encodeABI(), + }, + }) + .then((safeTx) => + _prepareSafeTransaction({ + safeTx, + safeSdk, + signerAddress: account.address, + }), + ), }); - return true; - }, - - /** - * Finds the Safe addresses of an owner. - * - * @namespace core.safe.getAddresses - * - * @param {Object} account - web3 account instance - * @param {Object} userOptions - options - * @param {number} userOptions.ownerAddress - address of the Safe owner - * - * @return {string} - Safe address - */ - getAddresses: (account, userOptions) => { - checkAccount(web3, account); - - const options = checkOptions(userOptions, { - ownerAddress: { - type: web3.utils.checkAddressChecksum, - }, - }); + // Wait to check that the version is updated + safeVersion = await loop( + () => _getVersion(safeAddress), + (version) => version === SAFE_LAST_VERSION, + { label: 'Waiting for CRC Safe to upgrade version' }, + ); + } - return utils - .requestIndexedDB('safe_addresses', options) - .then((response) => - response && response.user - ? response.user.safeAddresses.map((address) => - web3.utils.toChecksumAddress(address), - ) - : [], - ); - }, - - /** - * Get Safe version. - * - * @namespace core.safe.getVersion - * - * @param {Object} userOptions - options - * @param {number} userOptions.safeAddress - address of the Gnosis Safe - * - * @return {string} - transaction hash - */ - getVersion: (userOptions) => { - const { safeAddress } = checkOptions(userOptions, { - safeAddress: { - type: web3.utils.checkAddressChecksum, - }, - }); + return safeVersion; + }; - return getVersion(safeAddress); - }, - - /** - * Update Safe version to the last version (v1.3.0) by changing the the Master Copy. - * - * @namespace core.safe.updateSafeVersion - * - * @param {Object} account - web3 account instance - * @param {Object} userOptions - options - * @param {number} userOptions.safeAddress - address of the Gnosis Safe - * - * @return {string} - transaction hash - */ - updateToLastVersion: async (account, userOptions) => { - checkAccount(web3, account); - - const { safeAddress } = checkOptions(userOptions, { - safeAddress: { - type: web3.utils.checkAddressChecksum, - }, - }); - let safeVersion = await getVersion(safeAddress); + // TODO: this method is missing to be implemented because it will be moved and replaced into organization.js. + // Makes more sense to have it there. + /** + * Requests the relayer to deploy a Safe for an organization. The relayer + * funds the deployment of this Safe when the account is already known and + * verified / already has a deployed Safe from before. + * + * @namespace core.safe.deployForOrganization + * + * @param {Object} account - web3 account instance + * @param {Object} userOptions - options + * @param {number} userOptions.safeAddress - to-be-deployed Safe address + * + * @return {boolean} - returns true when successful + */ + const deployForOrganization = async (account, userOptions) => { + checkAccount(web3, account); - if (safeVersion !== SAFE_LAST_VERSION) { - // References: - // https://github.com/safe-global/web-core/blob/main/src/services/tx/safeUpdateParams.ts - // https://github.com/safe-global/safe-react/blob/main/src/logic/safe/utils/upgradeSafe.ts + const options = checkOptions(userOptions, { + safeAddress: { + type: web3.utils.checkAddressChecksum, + }, + }); - // Get the Safe contract with version v1.1.1+Circles - const safeInstance = getSafeCRCVersionContract(web3, safeAddress); - const safeSdk = await getSafeSdk({ - safeAddress, - signerAddress: account.address, - }); + await utils.requestRelayer({ + path: ['safes', options.safeAddress, 'organization'], + version: 2, + method: 'PUT', + }); - // First we change the Master Copy to v1.3.0 - // @ts-expect-error this was removed in 1.3.0 but we need to support it for older safe versions - await utils.sendTransaction({ - target: safeAddress, - data: await safeSdk - .createTransaction({ - safeTransactionData: { - to: safeAddress, - value: 0, - data: safeInstance.methods - .changeMasterCopy(safeMaster.options.address) - .encodeABI(), - }, - }) - .then((safeTx) => - prepareSafeTransaction({ - safeTx, - safeSdk, - signerAddress: account.address, - }), - ), - }); - - await utils.sendTransaction({ - target: safeAddress, - data: await safeSdk - .createTransaction({ - safeTransactionData: { - to: safeAddress, - value: 0, - data: safeInstance.methods - .setFallbackHandler(fallbackHandlerAddress) - .encodeABI(), - }, - }) - .then((safeTx) => - prepareSafeTransaction({ - safeTx, - safeSdk, - signerAddress: account.address, - }), - ), - }); - - // Wait to check that the version is updated - safeVersion = await loop( - () => getVersion(safeAddress), - (version) => version === SAFE_LAST_VERSION, - { label: 'Waiting for CRC Safe to upgrade version' }, - ); - } - - return safeVersion; - }, + return true; + }; + + return { + addOwner, + deployForOrganization, + deploySafe, + getAddresses, + getOwners, + getVersion, + isDeployed, + predictAddress, + removeOwner, + updateToLastVersion, }; } diff --git a/src/utils.js b/src/utils.js index 51546c7d..5a6231d7 100644 --- a/src/utils.js +++ b/src/utils.js @@ -670,7 +670,14 @@ export default function createUtilsModule(web3, contracts, globalOptions) { return parseInt(web3.utils.fromWei(`${value}`, 'ether'), 10); }, - // TODO: docs + /** + * Send a transaction though the relayer to be funded + * @namespace core.utils.sendTransaction + * @param {SponsoredCallRequest} data - gelato request payload data + * @param {string} data.target - address of the target smart contract + * @param {Object} data.data - encoded payload data (usually a function selector plus the required arguments) used to call the required target address + * @return {RelayResponse} - gelato response + */ sendTransaction: (data) => request(relayServiceEndpoint, { path: ['transactions'], diff --git a/test/safe.test.js b/test/safe.test.js index e22e83f8..2906d796 100644 --- a/test/safe.test.js +++ b/test/safe.test.js @@ -26,7 +26,7 @@ describe('Safe', () => { it('should get the last version of Safe Contract by default', () => core.safe - .getVersion({ safeAddress }) + .getVersion(accounts[0], { safeAddress }) .then((version) => expect(version).toBe(SAFE_LAST_VERSION))); it('should get the safe address of the owner', () => @@ -105,7 +105,7 @@ describe('Safe', () => { it('should get the CRC version when deploying with CRC contract', () => core.safe - .getVersion({ + .getVersion(CRCSafeOwner, { safeAddress: CRCSafeAddress, }) .then((version) => expect(version).toBe(SAFE_CRC_VERSION)));