diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 2f3d4b0b0..2387e4a15 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,4 +1,4 @@
-name: Hardhat Test
+name: Monorepo Test
on: [push, pull_request]
env:
INFURA_KEY: ${{ secrets.INFURA_KEY }}
diff --git a/.github/workflows/test_V1_2_0.yml b/.github/workflows/test_V1_2_0.yml
new file mode 100644
index 000000000..8005161d2
--- /dev/null
+++ b/.github/workflows/test_V1_2_0.yml
@@ -0,0 +1,21 @@
+name: Safe Core SDK Test (Safe v1.2.0)
+on: [push, pull_request]
+env:
+ INFURA_KEY: ${{ secrets.INFURA_KEY }}
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ node-version: [14.x]
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+ - run: |
+ yarn install
+ yarn build
+ cd packages/safe-core-sdk
+ yarn test:hardhat:ethers:v1.2.0
diff --git a/.github/workflows/test_V1_3_0.yml b/.github/workflows/test_V1_3_0.yml
new file mode 100644
index 000000000..9a2056b79
--- /dev/null
+++ b/.github/workflows/test_V1_3_0.yml
@@ -0,0 +1,21 @@
+name: Safe Core SDK Test (Safe v1.3.0)
+on: [push, pull_request]
+env:
+ INFURA_KEY: ${{ secrets.INFURA_KEY }}
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ node-version: [14.x]
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+ - run: |
+ yarn install
+ yarn build
+ cd packages/safe-core-sdk
+ yarn test:hardhat:ethers:v1.3.0
diff --git a/packages/safe-core-sdk/README.md b/packages/safe-core-sdk/README.md
index 8c04ddae5..4b82bc7d3 100644
--- a/packages/safe-core-sdk/README.md
+++ b/packages/safe-core-sdk/README.md
@@ -68,13 +68,36 @@ const safeFactory = await SafeFactory.create({ ethAdapter })
const owners = ['0x
', '0x', '0x']
const threshold = 3
-const safeAccountConfig: SafeAccountConfig = { owners, threshold }
+const safeAccountConfig: SafeAccountConfig = {
+ owners,
+ threshold,
+ // ...
+}
const safeSdk: Safe = await safeFactory.deploySafe(safeAccountConfig)
```
The method `deploySafe` executes a transaction from `owner1` account, deploys a new Safe and returns an instance of the Safe Core SDK connected to the new Safe.
+The `SafeFactory` will deploy the last version of the Safe contracts available by default (currently `v1.3.0`). To deploy an older version of the Safe contracts instantiate the `SafeFactory` adding the property `safeVersion` with the desired version number.
+
+```js
+const safeFactoryV1_1_1 = await SafeFactory.create({ ethAdapter, safeVersion: '1.1.1' })
+```
+
+The property `contractNetworks` can also be used to provide the Safe contract addresses in case the SDK is used in a network where the Safe contracts are not deployed.
+
+```js
+const contractNetworks: ContractNetworksConfig = {
+ [chainId]: {
+ multiSendAddress: '0x',
+ safeMasterCopyAddress: '0x',
+ safeProxyFactoryAddress: '0x'
+ }
+}
+const safeFactory = await SafeFactory.create({ ethAdapter, contractNetworks })
+```
+
Call the method `getAddress`, for example, to check the address of the newly deployed Safe.
```js
@@ -153,7 +176,9 @@ The property `contractNetworks` can be added to provide the Safe contract addres
```js
const contractNetworks: ContractNetworksConfig = {
[chainId]: {
- multiSendAddress: '0x'
+ multiSendAddress: '0x',
+ safeMasterCopyAddress: '0x',
+ safeProxyFactoryAddress: '0x'
}
}
const safeSdk = await Safe.create({ ethAdapter, safeAddress, contractNetworks })
@@ -172,7 +197,9 @@ The property `contractNetworks` can be added to provide the Safe contract addres
```js
const contractNetworks: ContractNetworksConfig = {
[chainId]: {
- multiSendAddress: '0x'
+ multiSendAddress: '0x',
+ safeMasterCopyAddress: '0x',
+ safeProxyFactoryAddress: '0x'
}
}
const safeSdk = await Safe.create({ ethAdapter, safeAddress, contractNetworks })
diff --git a/packages/safe-core-sdk/contracts/Deps.sol b/packages/safe-core-sdk/contracts/Deps.sol
deleted file mode 100644
index 29f702a84..000000000
--- a/packages/safe-core-sdk/contracts/Deps.sol
+++ /dev/null
@@ -1,8 +0,0 @@
-pragma solidity >=0.5.0 <0.7.0;
-
-import { GnosisSafeProxyFactory } from "@gnosis.pm/safe-contracts/contracts/proxies/GnosisSafeProxyFactory.sol";
-import { GnosisSafe } from "@gnosis.pm/safe-contracts/contracts/GnosisSafe.sol";
-import { MultiSend } from "@gnosis.pm/safe-contracts/contracts/libraries/MultiSend.sol";
-import { DailyLimitModule } from "@gnosis.pm/safe-contracts/contracts/modules/DailyLimitModule.sol";
-import { SocialRecoveryModule } from "@gnosis.pm/safe-contracts/contracts/modules/SocialRecoveryModule.sol";
-import { ERC20Mintable } from "openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol";
diff --git a/packages/safe-core-sdk/contracts/Deps_V1_2_0.sol b/packages/safe-core-sdk/contracts/Deps_V1_2_0.sol
new file mode 100644
index 000000000..a365e9702
--- /dev/null
+++ b/packages/safe-core-sdk/contracts/Deps_V1_2_0.sol
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity >=0.5.0 <0.9.0;
+
+import { GnosisSafeProxyFactory } from "@gnosis.pm/safe-contracts-v1.2.0/contracts/proxies/GnosisSafeProxyFactory.sol";
+import { GnosisSafe } from "@gnosis.pm/safe-contracts-v1.2.0/contracts/GnosisSafe.sol";
+import { MultiSend } from "@gnosis.pm/safe-contracts-v1.2.0/contracts/libraries/MultiSend.sol";
+import { DailyLimitModule } from "@gnosis.pm/safe-contracts-v1.2.0/contracts/modules/DailyLimitModule.sol";
+import { SocialRecoveryModule } from "@gnosis.pm/safe-contracts-v1.2.0/contracts/modules/SocialRecoveryModule.sol";
+import { ERC20Mintable } from "openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol";
+
+contract ProxyFactory_SV1_2_0 is GnosisSafeProxyFactory {}
+contract GnosisSafe_SV1_2_0 is GnosisSafe {}
+contract MultiSend_SV1_2_0 is MultiSend {}
diff --git a/packages/safe-core-sdk/contracts/Deps_V1_3_0.sol b/packages/safe-core-sdk/contracts/Deps_V1_3_0.sol
new file mode 100644
index 000000000..122460b1e
--- /dev/null
+++ b/packages/safe-core-sdk/contracts/Deps_V1_3_0.sol
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity >=0.7.0 <0.9.0;
+
+import { GnosisSafeProxyFactory } from "@gnosis.pm/safe-contracts-v1.3.0/contracts/proxies/GnosisSafeProxyFactory.sol";
+import { GnosisSafe } from "@gnosis.pm/safe-contracts-v1.3.0/contracts/GnosisSafe.sol";
+import { MultiSend } from "@gnosis.pm/safe-contracts-v1.3.0/contracts/libraries/MultiSend.sol";
+
+contract ProxyFactory_SV1_3_0 is GnosisSafeProxyFactory {}
+contract GnosisSafe_SV1_3_0 is GnosisSafe {}
+contract MultiSend_SV1_3_0 is MultiSend {}
diff --git a/packages/safe-core-sdk/hardhat.config.ts b/packages/safe-core-sdk/hardhat.config.ts
index b721b0b31..f3071bcb2 100644
--- a/packages/safe-core-sdk/hardhat.config.ts
+++ b/packages/safe-core-sdk/hardhat.config.ts
@@ -37,7 +37,9 @@ const config: HardhatUserConfig = {
defaultNetwork: "hardhat",
solidity: {
compilers: [
- { version: '0.5.17' }
+ { version: '0.5.17' },
+ { version: '0.5.3' },
+ { version: '0.8.0' },
]
},
paths: {
diff --git a/packages/safe-core-sdk/hardhat/deploy/deploy-contracts.ts b/packages/safe-core-sdk/hardhat/deploy/deploy-contracts.ts
index f016a79f4..8916a56d7 100644
--- a/packages/safe-core-sdk/hardhat/deploy/deploy-contracts.ts
+++ b/packages/safe-core-sdk/hardhat/deploy/deploy-contracts.ts
@@ -1,26 +1,52 @@
import { DeployFunction } from 'hardhat-deploy/types'
import { HardhatRuntimeEnvironment } from 'hardhat/types'
-const deploy: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
+type SafeVersion = '1.3.0' | '1.2.0' | '1.1.1'
+
+export const safeVersionDeployed = process.env.SAFE_VERSION as SafeVersion
+
+const gnosisSafeContracts = {
+ '1.3.0': { name: 'GnosisSafe_SV1_3_0' },
+ '1.2.0': { name: 'GnosisSafe_SV1_2_0' },
+ '1.1.1': { name: 'GnosisSafe_SV1_1_1' }
+}
+
+const proxyFactoryContracts = {
+ '1.3.0': { name: 'ProxyFactory_SV1_3_0' },
+ '1.2.0': { name: 'ProxyFactory_SV1_2_0' },
+ '1.1.1': { name: 'ProxyFactory_SV1_1_1' }
+}
+
+const multiSendContracts = {
+ '1.3.0': { name: 'MultiSend_SV1_3_0' },
+ '1.2.0': { name: 'MultiSend_SV1_2_0' },
+ '1.1.1': { name: 'MultiSend_SV1_1_1' }
+}
+
+export const gnosisSafeDeployed = gnosisSafeContracts[safeVersionDeployed]
+export const proxyFactoryDeployed = proxyFactoryContracts[safeVersionDeployed]
+export const multiSendDeployed = multiSendContracts[safeVersionDeployed]
+
+const deploy: DeployFunction = async (hre: HardhatRuntimeEnvironment): Promise => {
const { deployments, getNamedAccounts } = hre
const { deployer } = await getNamedAccounts()
const { deploy } = deployments
- await deploy('GnosisSafe', {
+ await deploy(gnosisSafeDeployed.name, {
from: deployer,
args: [],
log: true,
deterministicDeployment: true
})
- await deploy('GnosisSafeProxyFactory', {
+ await deploy(proxyFactoryDeployed.name, {
from: deployer,
args: [],
log: true,
deterministicDeployment: true
})
- await deploy('MultiSend', {
+ await deploy(multiSendDeployed.name, {
from: deployer,
args: [],
log: true,
diff --git a/packages/safe-core-sdk/package.json b/packages/safe-core-sdk/package.json
index 817e09050..a567d2761 100644
--- a/packages/safe-core-sdk/package.json
+++ b/packages/safe-core-sdk/package.json
@@ -11,16 +11,19 @@
"SDK"
],
"scripts": {
- "typechain": "node scripts/generateTypechainFiles.ts",
+ "typechain": "ts-node scripts/generateTypechainFiles.ts",
"unbuild": "rimraf dist artifacts cache .nyc_output typechain",
"build": "hardhat compile && yarn typechain && tsc",
- "test:ganache:web3": "export TEST_NETWORK=ganache && export ETH_LIB=web3 && hardhat --network localhost deploy && nyc hardhat --network localhost test",
- "test:ganache:ethers": "export TEST_NETWORK=ganache && export ETH_LIB=ethers && hardhat --network localhost deploy && nyc hardhat --network localhost test",
- "test:hardhat:web3": "export TEST_NETWORK=hardhat && export ETH_LIB=web3 && hardhat deploy && nyc hardhat test",
- "test:hardhat:ethers": "export TEST_NETWORK=hardhat && export ETH_LIB=ethers && hardhat deploy && nyc hardhat test",
- "test": "yarn test:hardhat:web3 && yarn test:hardhat:ethers",
+ "test:ganache:web3:v1.2.0": "export TEST_NETWORK=ganache && export ETH_LIB=web3 && export SAFE_VERSION=1.2.0 && hardhat --network localhost deploy && nyc hardhat --network localhost test",
+ "test:ganache:web3:v1.3.0": "export TEST_NETWORK=ganache && export ETH_LIB=web3 && export SAFE_VERSION=1.3.0 && hardhat --network localhost deploy && nyc hardhat --network localhost test",
+ "test:ganache:ethers:v1.2.0": "export TEST_NETWORK=ganache && export ETH_LIB=ethers && export SAFE_VERSION=1.2.0 && hardhat --network localhost deploy && nyc hardhat --network localhost test",
+ "test:ganache:ethers:v1.3.0": "export TEST_NETWORK=ganache && export ETH_LIB=ethers && export SAFE_VERSION=1.3.0 && hardhat --network localhost deploy && nyc hardhat --network localhost test",
+ "test:hardhat:web3:v1.2.0": "export TEST_NETWORK=hardhat && export ETH_LIB=web3 && export SAFE_VERSION=1.2.0 && hardhat deploy && nyc hardhat test",
+ "test:hardhat:web3:v1.3.0": "export TEST_NETWORK=hardhat && export ETH_LIB=web3 && export SAFE_VERSION=1.3.0 && hardhat deploy && nyc hardhat test",
+ "test:hardhat:ethers:v1.2.0": "export TEST_NETWORK=hardhat && export ETH_LIB=ethers && export SAFE_VERSION=1.2.0 && hardhat deploy && nyc hardhat test",
+ "test:hardhat:ethers:v1.3.0": "export TEST_NETWORK=hardhat && export ETH_LIB=ethers && export SAFE_VERSION=1.3.0 && hardhat deploy && nyc hardhat test",
"coverage": "nyc report --reporter=text-lcov | coveralls",
- "format": "prettier --write \"{src,tests,hardhat}/**/*.ts\"",
+ "format": "prettier --write \"{src,tests,hardhat,scripts}/**/*.ts\"",
"lint": "tslint -p tsconfig.json"
},
"repository": {
@@ -38,7 +41,8 @@
],
"homepage": "https://github.com/gnosis/safe-core-sdk#readme",
"devDependencies": {
- "@gnosis.pm/safe-contracts": "1.2.0",
+ "@gnosis.pm/safe-contracts-v1.2.0": "npm:@gnosis.pm/safe-contracts@1.2.0",
+ "@gnosis.pm/safe-contracts-v1.3.0": "npm:@gnosis.pm/safe-contracts@1.3.0",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@nomiclabs/hardhat-web3": "^2.0.0",
@@ -87,6 +91,7 @@
},
"dependencies": {
"@gnosis.pm/safe-core-sdk-types": "^0.1.1",
+ "@gnosis.pm/safe-deployments": "^1.4.0",
"ethereumjs-util": "^7.0.10"
}
}
diff --git a/packages/safe-core-sdk/scripts/generateTypechainFiles.ts b/packages/safe-core-sdk/scripts/generateTypechainFiles.ts
index e083ef31e..ac35bcb67 100644
--- a/packages/safe-core-sdk/scripts/generateTypechainFiles.ts
+++ b/packages/safe-core-sdk/scripts/generateTypechainFiles.ts
@@ -1,66 +1,67 @@
-const { execSync } = require('child_process')
-const { readdir, mkdirSync, existsSync } = require('fs')
-const path = require('path')
+import { execSync } from 'child_process'
+import { existsSync, mkdirSync, readdir } from 'fs'
+import path from 'path'
// Directories where the Typechain files will be generated
const outDirSrc = 'typechain/src/'
const typeChainDirectorySrcPath = path.join(__dirname, `../${outDirSrc}`)
-const outDirTests = 'typechain/tests/'
-const typeChainDirectoryTestsPath = path.join(__dirname, `../${outDirTests}`)
-
const outDirBuild = 'dist/typechain/src/'
const typeChainDirectoryBuildPath = path.join(__dirname, `../${outDirBuild}`)
+const outDirTests = 'typechain/tests/'
+
// Contract list for which the Typechain files will be generated
// Will be included in dist/ folder
-const safeContractsPath = '../../node_modules/@gnosis.pm/safe-contracts/build/contracts'
-const openZeppelinContractsPath = '../../node_modules/openzeppelin-solidity/build/contracts'
-const safeContracts = [
- `${safeContractsPath}/GnosisSafe.json`,
- `${safeContractsPath}/GnosisSafeProxyFactory.json`,
- `${safeContractsPath}/MultiSend.json`,
+const safeContractsPath = '../../node_modules/@gnosis.pm/safe-deployments/dist/assets'
+
+const safeContracts_V1_3_0 = [
+ `${safeContractsPath}/v1.3.0/gnosis_safe.json`,
+ `${safeContractsPath}/v1.3.0/proxy_factory.json`,
+ `${safeContractsPath}/v1.3.0/multi_send.json`,
+].join(' ')
+const safeContracts_V1_2_0 = [
+ `${safeContractsPath}/v1.2.0/gnosis_safe.json`,
].join(' ')
+const safeContracts_V1_1_1 = [
+ `${safeContractsPath}/v1.1.1/gnosis_safe.json`,
+ `${safeContractsPath}/v1.1.1/proxy_factory.json`,
+ `${safeContractsPath}/v1.1.1/multi_send.json`,
+].join(' ')
+
// Won't be included in dist/ folder
+const safeContractsTestPath = '../../node_modules/@gnosis.pm/safe-contracts-v1.2.0/build/contracts'
+const openZeppelinContractsPath = '../../node_modules/openzeppelin-solidity/build/contracts'
const testContracts = [
- `${safeContractsPath}/DailyLimitModule.json`,
- `${safeContractsPath}/SocialRecoveryModule.json`,
+ `${safeContractsTestPath}/DailyLimitModule.json`,
+ `${safeContractsTestPath}/SocialRecoveryModule.json`,
`${openZeppelinContractsPath}/ERC20Mintable.json`
].join(' ')
// Remove existing Typechain files
-execSync(`rimraf ${outDirSrc} ${outDirTests}`, (error) => {
- if (error) {
- console.log(error.message)
- return
- }
-})
+execSync(`rimraf ${outDirSrc} ${outDirTests}`)
// Generate Typechain files
-function generateTypechainFiles(typechainVersion, outDir, contractList) {
- execSync(`typechain --target ${typechainVersion} --out-dir ${outDir}${typechainVersion} ${contractList}`, (error) => {
- if (error) {
- console.log(error.message)
- }
- })
+function generateTypechainFiles(typechainVersion: string, outDir: string, contractList: string): void {
+ execSync(`typechain --target ${typechainVersion} --out-dir ${outDir} ${contractList}`)
console.log(`Generated typechain ${typechainVersion} at ${outDir}`)
}
// Copy Typechain files with the right extension (.d.ts -> .ts) allows them to be included in the build folder
-function moveTypechainFiles(typechainVersion, inDir, outDir) {
- readdir(`${inDir}${typechainVersion}`, (error, files) => {
+function moveTypechainFiles(inDir: string, outDir: string): void {
+ readdir(`${inDir}`, (error, files) => {
if (error) {
console.log(error)
}
- if (!existsSync(`${outDir}${typechainVersion}`)) {
- mkdirSync(`${outDir}${typechainVersion}`, { recursive: true })
+ if (!existsSync(`${outDir}`)) {
+ mkdirSync(`${outDir}`, { recursive: true })
}
files.forEach(file => {
const pattern = /.d.ts/
if (!file.match(pattern)) {
return
}
- execSync(`cp ${inDir}${typechainVersion}/${file} ${outDir}${typechainVersion}/${file}`)
+ execSync(`cp ${inDir}/${file} ${outDir}/${file}`)
})
})
}
@@ -69,12 +70,20 @@ const web3V1 = 'web3-v1'
const ethersV5 = 'ethers-v5'
// Src: Web3 V1 types
-generateTypechainFiles(web3V1, outDirSrc, safeContracts)
-moveTypechainFiles(web3V1, typeChainDirectorySrcPath, typeChainDirectoryBuildPath)
+generateTypechainFiles(web3V1, `${outDirSrc}${web3V1}/v1.3.0`, safeContracts_V1_3_0)
+generateTypechainFiles(web3V1, `${outDirSrc}${web3V1}/v1.2.0`, safeContracts_V1_2_0)
+generateTypechainFiles(web3V1, `${outDirSrc}${web3V1}/v1.1.1`, safeContracts_V1_1_1)
+moveTypechainFiles(`${typeChainDirectorySrcPath}${web3V1}/v1.3.0`, `${typeChainDirectoryBuildPath}${web3V1}/v1.3.0`)
+moveTypechainFiles(`${typeChainDirectorySrcPath}${web3V1}/v1.2.0`, `${typeChainDirectoryBuildPath}${web3V1}/v1.2.0`)
+moveTypechainFiles(`${typeChainDirectorySrcPath}${web3V1}/v1.1.1`, `${typeChainDirectoryBuildPath}${web3V1}/v1.1.1`)
// Src: Ethers V5 types
-generateTypechainFiles(ethersV5, outDirSrc, safeContracts)
-moveTypechainFiles(ethersV5, typeChainDirectorySrcPath, typeChainDirectoryBuildPath)
+generateTypechainFiles(ethersV5, `${outDirSrc}${ethersV5}/v1.3.0`, safeContracts_V1_3_0)
+generateTypechainFiles(ethersV5, `${outDirSrc}${ethersV5}/v1.2.0`, safeContracts_V1_2_0)
+generateTypechainFiles(ethersV5, `${outDirSrc}${ethersV5}/v1.1.1`, safeContracts_V1_1_1)
+moveTypechainFiles(`${typeChainDirectorySrcPath}${ethersV5}/v1.3.0`, `${typeChainDirectoryBuildPath}${ethersV5}/v1.3.0`)
+moveTypechainFiles(`${typeChainDirectorySrcPath}${ethersV5}/v1.2.0`, `${typeChainDirectoryBuildPath}${ethersV5}/v1.2.0`)
+moveTypechainFiles(`${typeChainDirectorySrcPath}${ethersV5}/v1.1.1`, `${typeChainDirectoryBuildPath}${ethersV5}/v1.1.1`)
// Tests: Ethers V5 types
-generateTypechainFiles(ethersV5, outDirTests, testContracts)
+generateTypechainFiles(ethersV5, `${outDirTests}${ethersV5}`, testContracts)
diff --git a/packages/safe-core-sdk/src/Safe.ts b/packages/safe-core-sdk/src/Safe.ts
index 99276beae..cf927f7c7 100644
--- a/packages/safe-core-sdk/src/Safe.ts
+++ b/packages/safe-core-sdk/src/Safe.ts
@@ -6,11 +6,11 @@ import {
SafeTransaction,
SafeTransactionDataPartial
} from '@gnosis.pm/safe-core-sdk-types'
-import { ContractNetworksConfig } from './configuration/contracts'
import EthAdapter from './ethereumLibs/EthAdapter'
import ContractManager from './managers/contractManager'
import ModuleManager from './managers/moduleManager'
import OwnerManager from './managers/ownerManager'
+import { ContractNetworksConfig } from './types'
import { sameString } from './utils'
import { generatePreValidatedSignature, generateSignature } from './utils/signatures'
import { estimateGasForTransactionExecution } from './utils/transactions/gas'
@@ -31,6 +31,8 @@ export interface SafeConfig {
ethAdapter: EthAdapter
/** safeAddress - The address of the Safe account to use */
safeAddress: string
+ /** isL1SafeMasterCopy - Forces to use the Gnosis Safe L1 version of the contract instead of the L2 version */
+ isL1SafeMasterCopy?: boolean
/** contractNetworks - Contract network configuration */
contractNetworks?: ContractNetworksConfig
}
@@ -40,6 +42,8 @@ export interface ConnectSafeConfig {
ethAdapter?: EthAdapter
/** safeAddress - The address of the Safe account to use */
safeAddress?: string
+ /** isL1SafeMasterCopy - Forces to use the Gnosis Safe L1 version of the contract instead of the L2 version */
+ isL1SafeMasterCopy?: boolean
/** contractNetworks - Contract network configuration */
contractNetworks?: ContractNetworksConfig
}
@@ -75,13 +79,17 @@ class Safe {
* Creates an instance of the Safe Core SDK.
* @param config - Ethers Safe configuration
* @returns The Safe Core SDK instance
- * @throws "Safe contracts not found in the current network"
* @throws "Safe Proxy contract is not deployed in the current network"
* @throws "MultiSend contract is not deployed in the current network"
*/
- static async create({ ethAdapter, safeAddress, contractNetworks }: SafeConfig): Promise {
+ static async create({
+ ethAdapter,
+ safeAddress,
+ isL1SafeMasterCopy,
+ contractNetworks
+ }: SafeConfig): Promise {
const safeSdk = new Safe()
- await safeSdk.init({ ethAdapter, safeAddress, contractNetworks })
+ await safeSdk.init({ ethAdapter, safeAddress, isL1SafeMasterCopy, contractNetworks })
return safeSdk
}
@@ -89,17 +97,22 @@ class Safe {
* Initializes the Safe Core SDK instance.
* @param config - Safe configuration
* @throws "Signer must be connected to a provider"
- * @throws "Safe contracts not found in the current network"
* @throws "Safe Proxy contract is not deployed in the current network"
* @throws "MultiSend contract is not deployed in the current network"
*/
- private async init({ ethAdapter, safeAddress, contractNetworks }: SafeConfig): Promise {
+ private async init({
+ ethAdapter,
+ safeAddress,
+ isL1SafeMasterCopy,
+ contractNetworks
+ }: SafeConfig): Promise {
this.#ethAdapter = ethAdapter
- this.#contractManager = await ContractManager.create(
- this.#ethAdapter,
+ this.#contractManager = await ContractManager.create({
+ ethAdapter: this.#ethAdapter,
safeAddress,
+ isL1SafeMasterCopy,
contractNetworks
- )
+ })
this.#ownerManager = new OwnerManager(this.#ethAdapter, this.#contractManager.safeContract)
this.#moduleManager = new ModuleManager(this.#ethAdapter, this.#contractManager.safeContract)
}
@@ -107,14 +120,19 @@ class Safe {
/**
* Returns a new instance of the Safe Core SDK.
* @param config - Connect Safe configuration
- * @throws "Safe contracts not found in the current network"
* @throws "Safe Proxy contract is not deployed in the current network"
* @throws "MultiSend contract is not deployed in the current network"
*/
- async connect({ ethAdapter, safeAddress, contractNetworks }: ConnectSafeConfig): Promise {
+ async connect({
+ ethAdapter,
+ safeAddress,
+ isL1SafeMasterCopy,
+ contractNetworks
+ }: ConnectSafeConfig): Promise {
return await Safe.create({
ethAdapter: ethAdapter || this.#ethAdapter,
safeAddress: safeAddress || this.getAddress(),
+ isL1SafeMasterCopy: isL1SafeMasterCopy || this.#contractManager.isL1SafeMasterCopy,
contractNetworks: contractNetworks || this.#contractManager.contractNetworks
})
}
diff --git a/packages/safe-core-sdk/src/configuration/contracts.ts b/packages/safe-core-sdk/src/configuration/contracts.ts
deleted file mode 100644
index d5e3fbc29..000000000
--- a/packages/safe-core-sdk/src/configuration/contracts.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-export interface ContractNetworkConfig {
- /** multiSendAddress - Address of the MultiSend contract deployed on a specific network */
- multiSendAddress: string
- /** safeMasterCopyAddress - Address of the Gnosis Safe Master Copy contract deployed on a specific network */
- safeMasterCopyAddress: string
- /** safeProxyFactoryAddress - Address of the Gnosis Safe Proxy Factory contract deployed on a specific network */
- safeProxyFactoryAddress: string
-}
-
-export interface ContractNetworksConfig {
- /** id - Network id */
- [id: string]: ContractNetworkConfig
-}
-
-export const defaultContractNetworks: ContractNetworksConfig = {
- // mainnet
- 1: {
- multiSendAddress: '0x8D29bE29923b68abfDD21e541b9374737B49cdAD',
- safeMasterCopyAddress: '0x6851D6fDFAfD08c0295C392436245E5bc78B0185',
- safeProxyFactoryAddress: '0x76E2cFc1F5Fa8F6a5b3fC4c8F4788F0116861F9B'
- },
- // rinkeby
- 4: {
- multiSendAddress: '0x8D29bE29923b68abfDD21e541b9374737B49cdAD',
- safeMasterCopyAddress: '0x6851D6fDFAfD08c0295C392436245E5bc78B0185',
- safeProxyFactoryAddress: '0x76E2cFc1F5Fa8F6a5b3fC4c8F4788F0116861F9B'
- },
- // goerli
- 5: {
- multiSendAddress: '0x8D29bE29923b68abfDD21e541b9374737B49cdAD',
- safeMasterCopyAddress: '0x6851D6fDFAfD08c0295C392436245E5bc78B0185',
- safeProxyFactoryAddress: '0x76E2cFc1F5Fa8F6a5b3fC4c8F4788F0116861F9B'
- },
- // kovan
- 42: {
- multiSendAddress: '0x8D29bE29923b68abfDD21e541b9374737B49cdAD',
- safeMasterCopyAddress: '0x6851D6fDFAfD08c0295C392436245E5bc78B0185',
- safeProxyFactoryAddress: '0x76E2cFc1F5Fa8F6a5b3fC4c8F4788F0116861F9B'
- },
- // xdai
- 100: {
- multiSendAddress: '0x8D29bE29923b68abfDD21e541b9374737B49cdAD',
- safeMasterCopyAddress: '0x6851D6fDFAfD08c0295C392436245E5bc78B0185',
- safeProxyFactoryAddress: '0x76E2cFc1F5Fa8F6a5b3fC4c8F4788F0116861F9B'
- },
- // energy web chain
- 246: {
- multiSendAddress: '0x8D29bE29923b68abfDD21e541b9374737B49cdAD',
- safeMasterCopyAddress: '0x6851D6fDFAfD08c0295C392436245E5bc78B0185',
- safeProxyFactoryAddress: '0x76E2cFc1F5Fa8F6a5b3fC4c8F4788F0116861F9B'
- },
- // energy web volta
- 73799: {
- multiSendAddress: '0x8D29bE29923b68abfDD21e541b9374737B49cdAD',
- safeMasterCopyAddress: '0x6851D6fDFAfD08c0295C392436245E5bc78B0185',
- safeProxyFactoryAddress: '0x76E2cFc1F5Fa8F6a5b3fC4c8F4788F0116861F9B'
- }
-}
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafe/GnosisSafeEthersV5Contract.ts b/packages/safe-core-sdk/src/contracts/GnosisSafe/GnosisSafeContractEthers.ts
similarity index 82%
rename from packages/safe-core-sdk/src/contracts/GnosisSafe/GnosisSafeEthersV5Contract.ts
rename to packages/safe-core-sdk/src/contracts/GnosisSafe/GnosisSafeContractEthers.ts
index a1420f13c..b19cc629a 100644
--- a/packages/safe-core-sdk/src/contracts/GnosisSafe/GnosisSafeEthersV5Contract.ts
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafe/GnosisSafeContractEthers.ts
@@ -1,7 +1,12 @@
import { BigNumber } from '@ethersproject/bignumber'
import { ContractTransaction } from '@ethersproject/contracts'
import { SafeTransaction, SafeTransactionData } from '@gnosis.pm/safe-core-sdk-types'
-import { GnosisSafe, GnosisSafeInterface } from '../../../typechain/src/ethers-v5/GnosisSafe'
+import { GnosisSafe as GnosisSafe_V1_1_1 } from '../../../typechain/src/ethers-v5/v1.1.1/GnosisSafe'
+import { GnosisSafe as GnosisSafe_V1_2_0 } from '../../../typechain/src/ethers-v5/v1.2.0/GnosisSafe'
+import {
+ GnosisSafe as GnosisSafe_V1_3_0,
+ GnosisSafeInterface
+} from '../../../typechain/src/ethers-v5/v1.3.0/GnosisSafe'
import { EthersTransactionResult, TransactionOptions } from '../../utils/transactions/types'
import GnosisSafeContract from './GnosisSafeContract'
@@ -16,8 +21,8 @@ function toTxResult(
}
}
-class GnosisSafeEthersV5Contract implements GnosisSafeContract {
- constructor(public contract: GnosisSafe) {}
+abstract class GnosisSafeContractEthers implements GnosisSafeContract {
+ constructor(public contract: GnosisSafe_V1_1_1 | GnosisSafe_V1_2_0 | GnosisSafe_V1_3_0) {}
async getVersion(): Promise {
return this.contract.VERSION()
@@ -67,13 +72,9 @@ class GnosisSafeEthersV5Contract implements GnosisSafeContract {
return toTxResult(txResponse, options)
}
- async getModules(): Promise {
- return this.contract.getModules()
- }
+ abstract getModules(): Promise
- async isModuleEnabled(moduleAddress: string): Promise {
- return this.contract.isModuleEnabled(moduleAddress)
- }
+ abstract isModuleEnabled(moduleAddress: string): Promise
async execTransaction(
safeTransaction: SafeTransaction,
@@ -108,4 +109,4 @@ class GnosisSafeEthersV5Contract implements GnosisSafeContract {
}
}
-export default GnosisSafeEthersV5Contract
+export default GnosisSafeContractEthers
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafe/GnosisSafeWeb3Contract.ts b/packages/safe-core-sdk/src/contracts/GnosisSafe/GnosisSafeContractWeb3.ts
similarity index 84%
rename from packages/safe-core-sdk/src/contracts/GnosisSafe/GnosisSafeWeb3Contract.ts
rename to packages/safe-core-sdk/src/contracts/GnosisSafe/GnosisSafeContractWeb3.ts
index 508f56bcb..b08e1a104 100644
--- a/packages/safe-core-sdk/src/contracts/GnosisSafe/GnosisSafeWeb3Contract.ts
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafe/GnosisSafeContractWeb3.ts
@@ -1,7 +1,9 @@
import { BigNumber } from '@ethersproject/bignumber'
import { SafeTransaction, SafeTransactionData } from '@gnosis.pm/safe-core-sdk-types'
import { PromiEvent, TransactionReceipt } from 'web3-core/types'
-import { GnosisSafe } from '../../../typechain/src/web3-v1/GnosisSafe'
+import { GnosisSafe as GnosisSafe_V1_1_1 } from '../../../typechain/src/web3-v1/v1.1.1/gnosis_safe'
+import { GnosisSafe as GnosisSafe_V1_2_0 } from '../../../typechain/src/web3-v1/v1.2.0/gnosis_safe'
+import { GnosisSafe as GnosisSafe_V1_3_0 } from '../../../typechain/src/web3-v1/v1.3.0/gnosis_safe'
import { TransactionOptions, Web3TransactionResult } from '../../utils/transactions/types'
import GnosisSafeContract from './GnosisSafeContract'
@@ -16,8 +18,8 @@ function toTxResult(
)
}
-class GnosisSafeWeb3Contract implements GnosisSafeContract {
- constructor(public contract: GnosisSafe) {}
+abstract class GnosisSafeContractWeb3 implements GnosisSafeContract {
+ constructor(public contract: GnosisSafe_V1_1_1 | GnosisSafe_V1_2_0 | GnosisSafe_V1_3_0) {}
async getVersion(): Promise {
return this.contract.methods.VERSION().call()
@@ -69,13 +71,9 @@ class GnosisSafeWeb3Contract implements GnosisSafeContract {
return toTxResult(txResponse, options)
}
- async getModules(): Promise {
- return this.contract.methods.getModules().call()
- }
+ abstract getModules(): Promise
- async isModuleEnabled(moduleAddress: string): Promise {
- return this.contract.methods.isModuleEnabled(moduleAddress).call()
- }
+ abstract isModuleEnabled(moduleAddress: string): Promise
async execTransaction(
safeTransaction: SafeTransaction,
@@ -111,4 +109,4 @@ class GnosisSafeWeb3Contract implements GnosisSafeContract {
}
}
-export default GnosisSafeWeb3Contract
+export default GnosisSafeContractWeb3
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafe/SafeAbiV1-2-0.json b/packages/safe-core-sdk/src/contracts/GnosisSafe/SafeAbiV1-2-0.json
deleted file mode 100644
index 245c5fd3d..000000000
--- a/packages/safe-core-sdk/src/contracts/GnosisSafe/SafeAbiV1-2-0.json
+++ /dev/null
@@ -1,474 +0,0 @@
-[
- {
- "constant": true,
- "inputs": [],
- "name": "nonce",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "VERSION",
- "outputs": [
- {
- "internalType": "string",
- "name": "",
- "type": "string"
- }
- ],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "getOwners",
- "outputs": [
- {
- "internalType": "address[]",
- "name": "",
- "type": "address[]"
- }
- ],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "getThreshold",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "getModules",
- "outputs": [
- {
- "internalType": "address[]",
- "name": "",
- "type": "address[]"
- }
- ],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "internalType": "contract Module",
- "name": "module",
- "type": "address"
- }
- ],
- "name": "isModuleEnabled",
- "outputs": [
- {
- "internalType": "bool",
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "_owners",
- "type": "address[]"
- },
- {
- "name": "_threshold",
- "type": "uint256"
- },
- {
- "name": "to",
- "type": "address"
- },
- {
- "name": "data",
- "type": "bytes"
- },
- {
- "name": "fallbackHandler",
- "type": "address"
- },
- {
- "name": "paymentToken",
- "type": "address"
- },
- {
- "name": "payment",
- "type": "uint256"
- },
- {
- "name": "paymentReceiver",
- "type": "address"
- }
- ],
- "name": "setup",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "to",
- "type": "address"
- },
- {
- "name": "value",
- "type": "uint256"
- },
- {
- "name": "data",
- "type": "bytes"
- },
- {
- "name": "operation",
- "type": "uint8"
- },
- {
- "name": "safeTxGas",
- "type": "uint256"
- },
- {
- "name": "baseGas",
- "type": "uint256"
- },
- {
- "name": "gasPrice",
- "type": "uint256"
- },
- {
- "name": "gasToken",
- "type": "address"
- },
- {
- "name": "refundReceiver",
- "type": "address"
- },
- {
- "name": "signatures",
- "type": "bytes"
- }
- ],
- "name": "execTransaction",
- "outputs": [
- {
- "name": "success",
- "type": "bool"
- }
- ],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "internalType": "address",
- "name": "to",
- "type": "address"
- },
- {
- "internalType": "uint256",
- "name": "value",
- "type": "uint256"
- },
- {
- "internalType": "bytes",
- "name": "data",
- "type": "bytes"
- },
- {
- "internalType": "enum Enum.Operation",
- "name": "operation",
- "type": "uint8"
- },
- {
- "internalType": "uint256",
- "name": "safeTxGas",
- "type": "uint256"
- },
- {
- "internalType": "uint256",
- "name": "baseGas",
- "type": "uint256"
- },
- {
- "internalType": "uint256",
- "name": "gasPrice",
- "type": "uint256"
- },
- {
- "internalType": "address",
- "name": "gasToken",
- "type": "address"
- },
- {
- "internalType": "address",
- "name": "refundReceiver",
- "type": "address"
- },
- {
- "internalType": "uint256",
- "name": "_nonce",
- "type": "uint256"
- }
- ],
- "name": "getTransactionHash",
- "outputs": [
- {
- "internalType": "bytes32",
- "name": "",
- "type": "bytes32"
- }
- ],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "internalType": "bytes32",
- "name": "hashToApprove",
- "type": "bytes32"
- }
- ],
- "name": "approveHash",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "internalType": "address",
- "name": "",
- "type": "address"
- },
- {
- "internalType": "bytes32",
- "name": "",
- "type": "bytes32"
- }
- ],
- "name": "approvedHashes",
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "name": "to",
- "type": "address"
- },
- {
- "name": "value",
- "type": "uint256"
- },
- {
- "name": "data",
- "type": "bytes"
- },
- {
- "name": "operation",
- "type": "uint8"
- }
- ],
- "name": "requiredTxGas",
- "outputs": [
- {
- "name": "",
- "type": "uint256"
- }
- ],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "internalType": "contract Module",
- "name": "module",
- "type": "address"
- }
- ],
- "name": "enableModule",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "internalType": "contract Module",
- "name": "prevModule",
- "type": "address"
- },
- {
- "internalType": "contract Module",
- "name": "module",
- "type": "address"
- }
- ],
- "name": "disableModule",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [
- {
- "internalType": "address",
- "name": "owner",
- "type": "address"
- }
- ],
- "name": "isOwner",
- "outputs": [
- {
- "internalType": "bool",
- "name": "",
- "type": "bool"
- }
- ],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "internalType": "uint256",
- "name": "_threshold",
- "type": "uint256"
- }
- ],
- "name": "addOwnerWithThreshold",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "internalType": "address",
- "name": "prevOwner",
- "type": "address"
- },
- {
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "internalType": "uint256",
- "name": "_threshold",
- "type": "uint256"
- }
- ],
- "name": "removeOwner",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "internalType": "address",
- "name": "prevOwner",
- "type": "address"
- },
- {
- "internalType": "address",
- "name": "oldOwner",
- "type": "address"
- },
- {
- "internalType": "address",
- "name": "newOwner",
- "type": "address"
- }
- ],
- "name": "swapOwner",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "internalType": "uint256",
- "name": "_threshold",
- "type": "uint256"
- }
- ],
- "name": "changeThreshold",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- }
-]
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.1.1/GnosisSafeContract_V1_1_1_Ethers.ts b/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.1.1/GnosisSafeContract_V1_1_1_Ethers.ts
new file mode 100644
index 000000000..213cf18f4
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.1.1/GnosisSafeContract_V1_1_1_Ethers.ts
@@ -0,0 +1,23 @@
+import { GnosisSafe } from '../../../../typechain/src/ethers-v5/v1.1.1/GnosisSafe'
+import { sameString } from '../../../utils'
+import GnosisSafeContractEthers from '../GnosisSafeContractEthers'
+
+class GnosisSafeContract_V1_1_1_Ethers extends GnosisSafeContractEthers {
+ constructor(public contract: GnosisSafe) {
+ super(contract)
+ }
+
+ async getModules(): Promise {
+ return (super.contract as GnosisSafe).getModules()
+ }
+
+ async isModuleEnabled(moduleAddress: string): Promise {
+ const modules = await this.getModules()
+ const isModuleEnabled = modules.some((enabledModuleAddress: string) =>
+ sameString(enabledModuleAddress, moduleAddress)
+ )
+ return isModuleEnabled
+ }
+}
+
+export default GnosisSafeContract_V1_1_1_Ethers
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.1.1/GnosisSafeContract_V1_1_1_Web3.ts b/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.1.1/GnosisSafeContract_V1_1_1_Web3.ts
new file mode 100644
index 000000000..34191adaf
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.1.1/GnosisSafeContract_V1_1_1_Web3.ts
@@ -0,0 +1,23 @@
+import { GnosisSafe } from '../../../../typechain/src/web3-v1/v1.1.1/gnosis_safe'
+import { sameString } from '../../../utils'
+import GnosisSafeContractWeb3 from '../GnosisSafeContractWeb3'
+
+class GnosisSafeContract_V1_1_1_Web3 extends GnosisSafeContractWeb3 {
+ constructor(public contract: GnosisSafe) {
+ super(contract)
+ }
+
+ async getModules(): Promise {
+ return (super.contract as GnosisSafe).methods.getModules().call()
+ }
+
+ async isModuleEnabled(moduleAddress: string): Promise {
+ const modules = await this.getModules()
+ const isModuleEnabled = modules.some((enabledModuleAddress: string) =>
+ sameString(enabledModuleAddress, moduleAddress)
+ )
+ return isModuleEnabled
+ }
+}
+
+export default GnosisSafeContract_V1_1_1_Web3
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.2.0/GnosisSafeContract_V1_2_0_Ethers.ts b/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.2.0/GnosisSafeContract_V1_2_0_Ethers.ts
new file mode 100644
index 000000000..10131c451
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.2.0/GnosisSafeContract_V1_2_0_Ethers.ts
@@ -0,0 +1,18 @@
+import { GnosisSafe } from '../../../../typechain/src/ethers-v5/v1.2.0/GnosisSafe'
+import GnosisSafeContractEthers from '../GnosisSafeContractEthers'
+
+class GnosisSafeContract_V1_2_0_Ethers extends GnosisSafeContractEthers {
+ constructor(public contract: GnosisSafe) {
+ super(contract)
+ }
+
+ async getModules(): Promise {
+ return this.contract.getModules()
+ }
+
+ async isModuleEnabled(moduleAddress: string): Promise {
+ return this.contract.isModuleEnabled(moduleAddress)
+ }
+}
+
+export default GnosisSafeContract_V1_2_0_Ethers
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.2.0/GnosisSafeContract_V1_2_0_Web3.ts b/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.2.0/GnosisSafeContract_V1_2_0_Web3.ts
new file mode 100644
index 000000000..075ff93fd
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.2.0/GnosisSafeContract_V1_2_0_Web3.ts
@@ -0,0 +1,18 @@
+import { GnosisSafe } from '../../../../typechain/src/web3-v1/v1.2.0/gnosis_safe'
+import GnosisSafeContractWeb3 from '../GnosisSafeContractWeb3'
+
+class GnosisSafeContract_V1_2_0_Web3 extends GnosisSafeContractWeb3 {
+ constructor(public contract: GnosisSafe) {
+ super(contract)
+ }
+
+ async getModules(): Promise {
+ return this.contract.methods.getModules().call()
+ }
+
+ async isModuleEnabled(moduleAddress: string): Promise {
+ return this.contract.methods.isModuleEnabled(moduleAddress).call()
+ }
+}
+
+export default GnosisSafeContract_V1_2_0_Web3
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.3.0/GnosisSafeContract_V1_3_0_Ethers.ts b/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.3.0/GnosisSafeContract_V1_3_0_Ethers.ts
new file mode 100644
index 000000000..56db6ccfe
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.3.0/GnosisSafeContract_V1_3_0_Ethers.ts
@@ -0,0 +1,20 @@
+import { GnosisSafe } from '../../../../typechain/src/ethers-v5/v1.3.0/GnosisSafe'
+import { SENTINEL_ADDRESS } from '../../../utils/constants'
+import GnosisSafeContractEthers from '../GnosisSafeContractEthers'
+
+class GnosisSafeContract_V1_3_0_Ethers extends GnosisSafeContractEthers {
+ constructor(public contract: GnosisSafe) {
+ super(contract)
+ }
+
+ async getModules(): Promise {
+ const { array } = await this.contract.getModulesPaginated(SENTINEL_ADDRESS, 10)
+ return array
+ }
+
+ async isModuleEnabled(moduleAddress: string): Promise {
+ return this.contract.isModuleEnabled(moduleAddress)
+ }
+}
+
+export default GnosisSafeContract_V1_3_0_Ethers
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.3.0/GnosisSafeContract_V1_3_0_Web3.ts b/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.3.0/GnosisSafeContract_V1_3_0_Web3.ts
new file mode 100644
index 000000000..5ecd81220
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafe/v1.3.0/GnosisSafeContract_V1_3_0_Web3.ts
@@ -0,0 +1,20 @@
+import { GnosisSafe } from '../../../../typechain/src/web3-v1/v1.3.0/gnosis_safe'
+import { SENTINEL_ADDRESS } from '../../../utils/constants'
+import GnosisSafeContractWeb3 from '../GnosisSafeContractWeb3'
+
+class GnosisSafeContract_V1_3_0_Web3 extends GnosisSafeContractWeb3 {
+ constructor(public contract: GnosisSafe) {
+ super(contract)
+ }
+
+ async getModules(): Promise {
+ const { array } = await this.contract.methods.getModulesPaginated(SENTINEL_ADDRESS, 10).call()
+ return array
+ }
+
+ async isModuleEnabled(moduleAddress: string): Promise {
+ return this.contract.methods.isModuleEnabled(moduleAddress).call()
+ }
+}
+
+export default GnosisSafeContract_V1_3_0_Web3
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryAbiV1-2-0.json b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryAbiV1-2-0.json
deleted file mode 100644
index 17201aafd..000000000
--- a/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryAbiV1-2-0.json
+++ /dev/null
@@ -1,156 +0,0 @@
-[
- {
- "constant": false,
- "inputs": [
- {
- "internalType": "address",
- "name": "masterCopy",
- "type": "address"
- },
- {
- "internalType": "bytes",
- "name": "data",
- "type": "bytes"
- }
- ],
- "name": "createProxy",
- "outputs": [
- {
- "internalType": "contract GnosisSafeProxy",
- "name": "proxy",
- "type": "address"
- }
- ],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "proxyRuntimeCode",
- "outputs": [
- {
- "internalType": "bytes",
- "name": "",
- "type": "bytes"
- }
- ],
- "payable": false,
- "stateMutability": "pure",
- "type": "function"
- },
- {
- "constant": true,
- "inputs": [],
- "name": "proxyCreationCode",
- "outputs": [
- {
- "internalType": "bytes",
- "name": "",
- "type": "bytes"
- }
- ],
- "payable": false,
- "stateMutability": "pure",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "internalType": "address",
- "name": "_mastercopy",
- "type": "address"
- },
- {
- "internalType": "bytes",
- "name": "initializer",
- "type": "bytes"
- },
- {
- "internalType": "uint256",
- "name": "saltNonce",
- "type": "uint256"
- }
- ],
- "name": "createProxyWithNonce",
- "outputs": [
- {
- "internalType": "contract GnosisSafeProxy",
- "name": "proxy",
- "type": "address"
- }
- ],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "internalType": "address",
- "name": "_mastercopy",
- "type": "address"
- },
- {
- "internalType": "bytes",
- "name": "initializer",
- "type": "bytes"
- },
- {
- "internalType": "uint256",
- "name": "saltNonce",
- "type": "uint256"
- },
- {
- "internalType": "contract IProxyCreationCallback",
- "name": "callback",
- "type": "address"
- }
- ],
- "name": "createProxyWithCallback",
- "outputs": [
- {
- "internalType": "contract GnosisSafeProxy",
- "name": "proxy",
- "type": "address"
- }
- ],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "constant": false,
- "inputs": [
- {
- "internalType": "address",
- "name": "_mastercopy",
- "type": "address"
- },
- {
- "internalType": "bytes",
- "name": "initializer",
- "type": "bytes"
- },
- {
- "internalType": "uint256",
- "name": "saltNonce",
- "type": "uint256"
- }
- ],
- "name": "calculateCreateProxyWithNonceAddress",
- "outputs": [
- {
- "internalType": "contract GnosisSafeProxy",
- "name": "proxy",
- "type": "address"
- }
- ],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- }
-]
\ No newline at end of file
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryEthersV5Contract.ts b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryEthersContract.ts
similarity index 74%
rename from packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryEthersV5Contract.ts
rename to packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryEthersContract.ts
index 1d059640f..0f14e16ed 100644
--- a/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryEthersV5Contract.ts
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryEthersContract.ts
@@ -1,9 +1,10 @@
import { ContractTransaction, Event } from '@ethersproject/contracts'
-import { GnosisSafeProxyFactory } from '../../../typechain/src/ethers-v5/GnosisSafeProxyFactory'
+import { ProxyFactory as ProxyFactory_V1_1_1 } from '../../../typechain/src/ethers-v5/v1.1.1/ProxyFactory'
+import { ProxyFactory as ProxyFactory_V1_3_0 } from '../../../typechain/src/ethers-v5/v1.3.0/ProxyFactory'
import GnosisSafeProxyFactoryContract, { CreateProxyProps } from './GnosisSafeProxyFactoryContract'
-class GnosisSafeProxyFactoryEthersV5Contract implements GnosisSafeProxyFactoryContract {
- constructor(public contract: GnosisSafeProxyFactory) {}
+class GnosisSafeProxyFactoryEthersContract implements GnosisSafeProxyFactoryContract {
+ constructor(public contract: ProxyFactory_V1_3_0 | ProxyFactory_V1_1_1) {}
getAddress(): string {
return this.contract.address
@@ -42,4 +43,4 @@ class GnosisSafeProxyFactoryEthersV5Contract implements GnosisSafeProxyFactoryCo
}
}
-export default GnosisSafeProxyFactoryEthersV5Contract
+export default GnosisSafeProxyFactoryEthersContract
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryWeb3Contract.ts b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryWeb3Contract.ts
index 0cc0a2584..2de5b629e 100644
--- a/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryWeb3Contract.ts
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryWeb3Contract.ts
@@ -1,9 +1,10 @@
import { PromiEvent, TransactionReceipt } from 'web3-core/types'
-import { GnosisSafeProxyFactory } from '../../../typechain/src/web3-v1/GnosisSafeProxyFactory'
+import { ProxyFactory as ProxyFactory_V1_1_1 } from '../../../typechain/src/web3-v1/v1.1.1/proxy_factory'
+import { ProxyFactory as ProxyFactory_V1_3_0 } from '../../../typechain/src/web3-v1/v1.3.0/proxy_factory'
import GnosisSafeProxyFactoryContract, { CreateProxyProps } from './GnosisSafeProxyFactoryContract'
class GnosisSafeProxyFactoryWeb3Contract implements GnosisSafeProxyFactoryContract {
- constructor(public contract: GnosisSafeProxyFactory) {}
+ constructor(public contract: ProxyFactory_V1_3_0 | ProxyFactory_V1_1_1) {}
getAddress(): string {
return this.contract.options.address
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/v1.1.1/GnosisSafeProxyFactoryContract_V1_1_1_Ethers.ts b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/v1.1.1/GnosisSafeProxyFactoryContract_V1_1_1_Ethers.ts
new file mode 100644
index 000000000..6a822ce09
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/v1.1.1/GnosisSafeProxyFactoryContract_V1_1_1_Ethers.ts
@@ -0,0 +1,10 @@
+import { ProxyFactory } from '../../../../typechain/src/ethers-v5/v1.1.1/ProxyFactory'
+import GnosisSafeProxyFactoryEthersContract from '../GnosisSafeProxyFactoryEthersContract'
+
+class GnosisSafeProxyFactoryContract_V1_1_1_Ethers extends GnosisSafeProxyFactoryEthersContract {
+ constructor(public contract: ProxyFactory) {
+ super(contract)
+ }
+}
+
+export default GnosisSafeProxyFactoryContract_V1_1_1_Ethers
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/v1.1.1/GnosisSafeProxyFactoryContract_V1_1_1_Web3.ts b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/v1.1.1/GnosisSafeProxyFactoryContract_V1_1_1_Web3.ts
new file mode 100644
index 000000000..0bbcf5b03
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/v1.1.1/GnosisSafeProxyFactoryContract_V1_1_1_Web3.ts
@@ -0,0 +1,10 @@
+import { ProxyFactory } from '../../../../typechain/src/web3-v1/v1.1.1/proxy_factory'
+import GnosisSafeProxyFactoryWeb3Contract from '../GnosisSafeProxyFactoryWeb3Contract'
+
+class GnosisSafeProxyFactoryContract_V1_1_1_Web3 extends GnosisSafeProxyFactoryWeb3Contract {
+ constructor(public contract: ProxyFactory) {
+ super(contract)
+ }
+}
+
+export default GnosisSafeProxyFactoryContract_V1_1_1_Web3
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/v1.3.0/GnosisSafeProxyFactoryContract_V1_3_0_Ethers.ts b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/v1.3.0/GnosisSafeProxyFactoryContract_V1_3_0_Ethers.ts
new file mode 100644
index 000000000..d66447ad3
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/v1.3.0/GnosisSafeProxyFactoryContract_V1_3_0_Ethers.ts
@@ -0,0 +1,10 @@
+import { ProxyFactory } from '../../../../typechain/src/ethers-v5/v1.3.0/ProxyFactory'
+import GnosisSafeProxyFactoryEthersContract from '../GnosisSafeProxyFactoryEthersContract'
+
+class GnosisSafeProxyFactoryContract_V1_3_0_Ethers extends GnosisSafeProxyFactoryEthersContract {
+ constructor(public contract: ProxyFactory) {
+ super(contract)
+ }
+}
+
+export default GnosisSafeProxyFactoryContract_V1_3_0_Ethers
diff --git a/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/v1.3.0/GnosisSafeProxyFactoryContract_V1_3_0_Web3.ts b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/v1.3.0/GnosisSafeProxyFactoryContract_V1_3_0_Web3.ts
new file mode 100644
index 000000000..31c559cce
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/GnosisSafeProxyFactory/v1.3.0/GnosisSafeProxyFactoryContract_V1_3_0_Web3.ts
@@ -0,0 +1,10 @@
+import { ProxyFactory } from '../../../../typechain/src/web3-v1/v1.3.0/proxy_factory'
+import GnosisSafeProxyFactoryWeb3Contract from '../GnosisSafeProxyFactoryWeb3Contract'
+
+class GnosisSafeProxyFactoryContract_V1_3_0_Web3 extends GnosisSafeProxyFactoryWeb3Contract {
+ constructor(public contract: ProxyFactory) {
+ super(contract)
+ }
+}
+
+export default GnosisSafeProxyFactoryContract_V1_3_0_Web3
diff --git a/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendAbi.json b/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendAbi.json
deleted file mode 100644
index d47e79d3f..000000000
--- a/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendAbi.json
+++ /dev/null
@@ -1,17 +0,0 @@
-[
- {
- "constant": false,
- "inputs": [
- {
- "internalType": "bytes",
- "name": "transactions",
- "type": "bytes"
- }
- ],
- "name": "multiSend",
- "outputs": [],
- "payable": false,
- "stateMutability": "nonpayable",
- "type": "function"
- }
-]
\ No newline at end of file
diff --git a/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendEthersContract.ts b/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendEthersContract.ts
new file mode 100644
index 000000000..ac5542919
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendEthersContract.ts
@@ -0,0 +1,20 @@
+import { MultiSend as MultiSend_V1_1_1 } from '../../../typechain/src/ethers-v5/v1.1.1/MultiSend'
+import {
+ MultiSend as MultiSend_V1_3_0,
+ MultiSendInterface
+} from '../../../typechain/src/ethers-v5/v1.3.0/MultiSend'
+import MultiSendContract from './MultiSendContract'
+
+abstract class MultiSendEthersContract implements MultiSendContract {
+ constructor(public contract: MultiSend_V1_1_1 | MultiSend_V1_3_0) {}
+
+ getAddress(): string {
+ return this.contract.address
+ }
+
+ encode: MultiSendInterface['encodeFunctionData'] = (methodName: any, params: any): string => {
+ return this.contract.interface.encodeFunctionData(methodName, params)
+ }
+}
+
+export default MultiSendEthersContract
diff --git a/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendEthersV5Contract.ts b/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendEthersV5Contract.ts
deleted file mode 100644
index 8ccf3aec9..000000000
--- a/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendEthersV5Contract.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { MultiSend, MultiSendInterface } from '../../../typechain/src/ethers-v5/MultiSend'
-import MultiSendContract from './MultiSendContract'
-
-class MultiSendEthersV5Contract implements MultiSendContract {
- constructor(public contract: MultiSend) {}
-
- getAddress(): string {
- return this.contract.address
- }
-
- encode: MultiSendInterface['encodeFunctionData'] = (methodName: any, params: any): string => {
- return this.contract.interface.encodeFunctionData(methodName, params)
- }
-}
-
-export default MultiSendEthersV5Contract
diff --git a/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendWeb3Contract.ts b/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendWeb3Contract.ts
index 0f3a10410..db1f5201b 100644
--- a/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendWeb3Contract.ts
+++ b/packages/safe-core-sdk/src/contracts/MultiSend/MultiSendWeb3Contract.ts
@@ -1,8 +1,9 @@
-import { MultiSend } from '../../../typechain/src/web3-v1/MultiSend'
+import { MultiSend as MultiSend_V1_1_1 } from '../../../typechain/src/web3-v1/v1.1.1/multi_send'
+import { MultiSend as MultiSend_V1_3_0 } from '../../../typechain/src/web3-v1/v1.3.0/multi_send'
import MultiSendContract from './MultiSendContract'
-class MultiSendWeb3Contract implements MultiSendContract {
- constructor(public contract: MultiSend) {}
+abstract class MultiSendWeb3Contract implements MultiSendContract {
+ constructor(public contract: MultiSend_V1_3_0 | MultiSend_V1_1_1) {}
getAddress(): string {
return this.contract.options.address
diff --git a/packages/safe-core-sdk/src/contracts/MultiSend/v1.1.1/MultiSendContract_V1_1_1_Ethers.ts b/packages/safe-core-sdk/src/contracts/MultiSend/v1.1.1/MultiSendContract_V1_1_1_Ethers.ts
new file mode 100644
index 000000000..c280443ef
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/MultiSend/v1.1.1/MultiSendContract_V1_1_1_Ethers.ts
@@ -0,0 +1,10 @@
+import { MultiSend } from '../../../../typechain/src/ethers-v5/v1.1.1/MultiSend'
+import MultiSendEthersContract from '../MultiSendEthersContract'
+
+class MultiSendContract_V1_1_1_Ethers extends MultiSendEthersContract {
+ constructor(public contract: MultiSend) {
+ super(contract)
+ }
+}
+
+export default MultiSendContract_V1_1_1_Ethers
diff --git a/packages/safe-core-sdk/src/contracts/MultiSend/v1.1.1/MultiSendContract_V1_1_1_Web3.ts b/packages/safe-core-sdk/src/contracts/MultiSend/v1.1.1/MultiSendContract_V1_1_1_Web3.ts
new file mode 100644
index 000000000..6bfb026d6
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/MultiSend/v1.1.1/MultiSendContract_V1_1_1_Web3.ts
@@ -0,0 +1,10 @@
+import { MultiSend } from '../../../../typechain/src/web3-v1/v1.1.1/multi_send'
+import MultiSendWeb3Contract from '../MultiSendWeb3Contract'
+
+class MultiSendContract_V1_1_1_Web3 extends MultiSendWeb3Contract {
+ constructor(public contract: MultiSend) {
+ super(contract)
+ }
+}
+
+export default MultiSendContract_V1_1_1_Web3
diff --git a/packages/safe-core-sdk/src/contracts/MultiSend/v1.3.0/MultiSendContract_V1_3_0_Ethers.ts b/packages/safe-core-sdk/src/contracts/MultiSend/v1.3.0/MultiSendContract_V1_3_0_Ethers.ts
new file mode 100644
index 000000000..685542fbe
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/MultiSend/v1.3.0/MultiSendContract_V1_3_0_Ethers.ts
@@ -0,0 +1,10 @@
+import { MultiSend } from '../../../../typechain/src/ethers-v5/v1.3.0/MultiSend'
+import MultiSendEthersContract from '../MultiSendEthersContract'
+
+class MultiSendContract_V1_3_0_Ethers extends MultiSendEthersContract {
+ constructor(public contract: MultiSend) {
+ super(contract)
+ }
+}
+
+export default MultiSendContract_V1_3_0_Ethers
diff --git a/packages/safe-core-sdk/src/contracts/MultiSend/v1.3.0/MultiSendContract_V1_3_0_Web3.ts b/packages/safe-core-sdk/src/contracts/MultiSend/v1.3.0/MultiSendContract_V1_3_0_Web3.ts
new file mode 100644
index 000000000..abdcd4a70
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/MultiSend/v1.3.0/MultiSendContract_V1_3_0_Web3.ts
@@ -0,0 +1,10 @@
+import { MultiSend } from '../../../../typechain/src/web3-v1/v1.3.0/multi_send'
+import MultiSendWeb3Contract from '../MultiSendWeb3Contract'
+
+class MultiSendContract_V1_3_0_Web3 extends MultiSendWeb3Contract {
+ constructor(public contract: MultiSend) {
+ super(contract)
+ }
+}
+
+export default MultiSendContract_V1_3_0_Web3
diff --git a/packages/safe-core-sdk/src/contracts/config.ts b/packages/safe-core-sdk/src/contracts/config.ts
new file mode 100644
index 000000000..6470cbbfd
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/config.ts
@@ -0,0 +1,38 @@
+export type SafeVersion = '1.3.0' | '1.2.0' | '1.1.1'
+
+export const SAFE_LAST_VERSION: SafeVersion = '1.3.0'
+export const SAFE_BASE_VERSION: SafeVersion = '1.1.1'
+
+type SafeDeploymentsVersions = {
+ [version: string]: {
+ safeMasterCopyVersion: string
+ safeMasterCopyL2Version: string | undefined
+ safeProxyFactoryVersion: string
+ multiSendVersion: string
+ }
+}
+
+export const safeDeploymentsVersions: SafeDeploymentsVersions = {
+ '1.3.0': {
+ safeMasterCopyVersion: '1.3.0',
+ safeMasterCopyL2Version: '1.3.0',
+ safeProxyFactoryVersion: '1.3.0',
+ multiSendVersion: '1.3.0'
+ },
+ '1.2.0': {
+ safeMasterCopyVersion: '1.2.0',
+ safeMasterCopyL2Version: undefined,
+ safeProxyFactoryVersion: '1.1.1',
+ multiSendVersion: '1.1.1'
+ },
+ '1.1.1': {
+ safeMasterCopyVersion: '1.1.1',
+ safeMasterCopyL2Version: undefined,
+ safeProxyFactoryVersion: '1.1.1',
+ multiSendVersion: '1.1.1'
+ }
+}
+
+export const safeDeploymentsL1ChainIds = [
+ 1 // Ethereum Mainnet
+]
diff --git a/packages/safe-core-sdk/src/contracts/contractInstancesEthers.ts b/packages/safe-core-sdk/src/contracts/contractInstancesEthers.ts
new file mode 100644
index 000000000..305616468
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/contractInstancesEthers.ts
@@ -0,0 +1,78 @@
+import { Signer } from '@ethersproject/abstract-signer'
+import { GnosisSafe__factory as SafeMasterCopy_V1_1_1 } from '../../typechain/src/ethers-v5/v1.1.1/factories/GnosisSafe__factory'
+import { MultiSend__factory as MultiSend_V1_1_1 } from '../../typechain/src/ethers-v5/v1.1.1/factories/MultiSend__factory'
+import { ProxyFactory__factory as SafeProxyFactory_V1_1_1 } from '../../typechain/src/ethers-v5/v1.1.1/factories/ProxyFactory__factory'
+import { GnosisSafe__factory as SafeMasterCopy_V1_2_0 } from '../../typechain/src/ethers-v5/v1.2.0/factories/GnosisSafe__factory'
+import { GnosisSafe__factory as SafeMasterCopy_V1_3_0 } from '../../typechain/src/ethers-v5/v1.3.0/factories/GnosisSafe__factory'
+import { MultiSend__factory as MultiSend_V1_3_0 } from '../../typechain/src/ethers-v5/v1.3.0/factories/MultiSend__factory'
+import { ProxyFactory__factory as SafeProxyFactory_V1_3_0 } from '../../typechain/src/ethers-v5/v1.3.0/factories/ProxyFactory__factory'
+import { SafeVersion } from './config'
+import GnosisSafeContract_V1_1_1_Ethers from './GnosisSafe/v1.1.1/GnosisSafeContract_V1_1_1_Ethers'
+import GnosisSafeContract_V1_2_0_Ethers from './GnosisSafe/v1.2.0/GnosisSafeContract_V1_2_0_Ethers'
+import GnosisSafeContract_V1_3_0_Ethers from './GnosisSafe/v1.3.0/GnosisSafeContract_V1_3_0_Ethers'
+import GnosisSafeProxyFactoryContract_V1_1_1_Ethers from './GnosisSafeProxyFactory/v1.1.1/GnosisSafeProxyFactoryContract_V1_1_1_Ethers'
+import GnosisSafeProxyFactoryContract_V1_3_0_Ethers from './GnosisSafeProxyFactory/v1.3.0/GnosisSafeProxyFactoryContract_V1_3_0_Ethers'
+import MultiSendContract_V1_1_1_Ethers from './MultiSend/v1.1.1/MultiSendContract_V1_1_1_Ethers'
+import MultiSendContract_V1_3_0_Ethers from './MultiSend/v1.3.0/MultiSendContract_V1_3_0_Ethers'
+
+export function getSafeContractInstance(
+ safeVersion: SafeVersion,
+ contractAddress: string,
+ signer: Signer
+):
+ | GnosisSafeContract_V1_3_0_Ethers
+ | GnosisSafeContract_V1_2_0_Ethers
+ | GnosisSafeContract_V1_1_1_Ethers {
+ let safeContract
+ switch (safeVersion) {
+ case '1.3.0':
+ safeContract = SafeMasterCopy_V1_3_0.connect(contractAddress, signer)
+ return new GnosisSafeContract_V1_3_0_Ethers(safeContract)
+ case '1.2.0':
+ safeContract = SafeMasterCopy_V1_2_0.connect(contractAddress, signer)
+ return new GnosisSafeContract_V1_2_0_Ethers(safeContract)
+ case '1.1.1':
+ safeContract = SafeMasterCopy_V1_1_1.connect(contractAddress, signer)
+ return new GnosisSafeContract_V1_1_1_Ethers(safeContract)
+ default:
+ throw new Error('Invalid Safe version')
+ }
+}
+
+export function getMultiSendContractInstance(
+ safeVersion: SafeVersion,
+ contractAddress: string,
+ signer: Signer
+): MultiSendContract_V1_3_0_Ethers | MultiSendContract_V1_1_1_Ethers {
+ let multiSendContract
+ switch (safeVersion) {
+ case '1.3.0':
+ multiSendContract = MultiSend_V1_3_0.connect(contractAddress, signer)
+ return new MultiSendContract_V1_3_0_Ethers(multiSendContract)
+ case '1.2.0':
+ case '1.1.1':
+ multiSendContract = MultiSend_V1_1_1.connect(contractAddress, signer)
+ return new MultiSendContract_V1_1_1_Ethers(multiSendContract)
+ default:
+ throw new Error('Invalid Safe version')
+ }
+}
+
+export function getSafeProxyFactoryContractInstance(
+ safeVersion: SafeVersion,
+ contractAddress: string,
+ signer: Signer
+): GnosisSafeProxyFactoryContract_V1_3_0_Ethers | GnosisSafeProxyFactoryContract_V1_1_1_Ethers {
+ let gnosisSafeProxyFactoryContract
+ switch (safeVersion) {
+ case '1.3.0':
+ gnosisSafeProxyFactoryContract = SafeProxyFactory_V1_3_0.connect(contractAddress, signer)
+ return new GnosisSafeProxyFactoryContract_V1_3_0_Ethers(gnosisSafeProxyFactoryContract)
+ case '1.2.0':
+ case '1.1.1':
+ gnosisSafeProxyFactoryContract = SafeProxyFactory_V1_1_1.connect(contractAddress, signer)
+ return new GnosisSafeProxyFactoryContract_V1_1_1_Ethers(gnosisSafeProxyFactoryContract)
+ default:
+ throw new Error('Invalid Safe version')
+ }
+}
diff --git a/packages/safe-core-sdk/src/contracts/contractInstancesWeb3.ts b/packages/safe-core-sdk/src/contracts/contractInstancesWeb3.ts
new file mode 100644
index 000000000..a5fd4009d
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/contractInstancesWeb3.ts
@@ -0,0 +1,68 @@
+import { GnosisSafe as SafeMasterCopy_V1_1_1 } from '../../typechain/src/web3-v1/v1.1.1/gnosis_safe'
+import { MultiSend as MultiSend_V1_1_1 } from '../../typechain/src/web3-v1/v1.1.1/multi_send'
+import { ProxyFactory as GnosisSafeProxyFactory_V1_1_1 } from '../../typechain/src/web3-v1/v1.1.1/proxy_factory'
+import { GnosisSafe as SafeMasterCopy_V1_2_0 } from '../../typechain/src/web3-v1/v1.2.0/gnosis_safe'
+import { GnosisSafe as SafeMasterCopy_V1_3_0 } from '../../typechain/src/web3-v1/v1.3.0/gnosis_safe'
+import { MultiSend as MultiSend_V1_3_0 } from '../../typechain/src/web3-v1/v1.3.0/multi_send'
+import { ProxyFactory as GnosisSafeProxyFactory_V1_3_0 } from '../../typechain/src/web3-v1/v1.3.0/proxy_factory'
+import { SafeVersion } from './config'
+import GnosisSafeContract_V1_1_1_Web3 from './GnosisSafe/v1.1.1/GnosisSafeContract_V1_1_1_Web3'
+import GnosisSafeContract_V1_2_0_Web3 from './GnosisSafe/v1.2.0/GnosisSafeContract_V1_2_0_Web3'
+import GnosisSafeContract_V1_3_0_Web3 from './GnosisSafe/v1.3.0/GnosisSafeContract_V1_3_0_Web3'
+import GnosisSafeProxyFactoryContract_V1_1_1_Web3 from './GnosisSafeProxyFactory/v1.1.1/GnosisSafeProxyFactoryContract_V1_1_1_Web3'
+import GnosisSafeProxyFactoryContract_V1_3_0_Web3 from './GnosisSafeProxyFactory/v1.3.0/GnosisSafeProxyFactoryContract_V1_3_0_Web3'
+import MultiSendContract_V1_1_1_Web3 from './MultiSend/v1.1.1/MultiSendContract_V1_1_1_Web3'
+import MultiSendContract_V1_3_0_Web3 from './MultiSend/v1.3.0/MultiSendContract_V1_3_0_Web3'
+
+export function getSafeContractInstance(
+ safeVersion: SafeVersion,
+ safeContract: SafeMasterCopy_V1_3_0 | SafeMasterCopy_V1_2_0 | SafeMasterCopy_V1_1_1
+):
+ | GnosisSafeContract_V1_3_0_Web3
+ | GnosisSafeContract_V1_2_0_Web3
+ | GnosisSafeContract_V1_1_1_Web3 {
+ switch (safeVersion) {
+ case '1.3.0':
+ return new GnosisSafeContract_V1_3_0_Web3(safeContract as SafeMasterCopy_V1_3_0)
+ case '1.2.0':
+ return new GnosisSafeContract_V1_2_0_Web3(safeContract as SafeMasterCopy_V1_2_0)
+ case '1.1.1':
+ return new GnosisSafeContract_V1_1_1_Web3(safeContract as SafeMasterCopy_V1_1_1)
+ default:
+ throw new Error('Invalid Safe version')
+ }
+}
+
+export function getMultiSendContractInstance(
+ safeVersion: SafeVersion,
+ multiSendContract: MultiSend_V1_3_0 | MultiSend_V1_1_1
+): MultiSendContract_V1_3_0_Web3 | MultiSendContract_V1_1_1_Web3 {
+ switch (safeVersion) {
+ case '1.3.0':
+ return new MultiSendContract_V1_3_0_Web3(multiSendContract as MultiSend_V1_3_0)
+ case '1.2.0':
+ case '1.1.1':
+ return new MultiSendContract_V1_1_1_Web3(multiSendContract as MultiSend_V1_1_1)
+ default:
+ throw new Error('Invalid Safe version')
+ }
+}
+
+export function getGnosisSafeProxyFactoryContractInstance(
+ safeVersion: SafeVersion,
+ gnosisSafeProxyFactoryContract: GnosisSafeProxyFactory_V1_3_0 | GnosisSafeProxyFactory_V1_1_1
+): GnosisSafeProxyFactoryContract_V1_3_0_Web3 | GnosisSafeProxyFactoryContract_V1_1_1_Web3 {
+ switch (safeVersion) {
+ case '1.3.0':
+ return new GnosisSafeProxyFactoryContract_V1_3_0_Web3(
+ gnosisSafeProxyFactoryContract as GnosisSafeProxyFactory_V1_3_0
+ )
+ case '1.2.0':
+ case '1.1.1':
+ return new GnosisSafeProxyFactoryContract_V1_1_1_Web3(
+ gnosisSafeProxyFactoryContract as GnosisSafeProxyFactory_V1_1_1
+ )
+ default:
+ throw new Error('Invalid Safe version')
+ }
+}
diff --git a/packages/safe-core-sdk/src/contracts/safeDeploymentContracts.ts b/packages/safe-core-sdk/src/contracts/safeDeploymentContracts.ts
new file mode 100644
index 000000000..3d93f7417
--- /dev/null
+++ b/packages/safe-core-sdk/src/contracts/safeDeploymentContracts.ts
@@ -0,0 +1,38 @@
+import {
+ DeploymentFilter,
+ getMultiSendDeployment,
+ getProxyFactoryDeployment,
+ getSafeL2SingletonDeployment,
+ getSafeSingletonDeployment,
+ SingletonDeployment
+} from '@gnosis.pm/safe-deployments'
+import { safeDeploymentsL1ChainIds, safeDeploymentsVersions, SafeVersion } from './config'
+
+export function getSafeContractDeployment(
+ safeVersion: SafeVersion,
+ chainId: number,
+ isL1SafeMasterCopy: boolean = false
+): SingletonDeployment | undefined {
+ const version = safeDeploymentsVersions[safeVersion].safeMasterCopyVersion
+ const filters: DeploymentFilter = { version, network: chainId.toString(), released: true }
+ if (safeDeploymentsL1ChainIds.includes(chainId) || isL1SafeMasterCopy) {
+ return getSafeSingletonDeployment(filters)
+ }
+ return getSafeL2SingletonDeployment(filters)
+}
+
+export function getMultiSendContractDeployment(
+ safeVersion: SafeVersion,
+ chainId: number
+): SingletonDeployment | undefined {
+ const version = safeDeploymentsVersions[safeVersion].multiSendVersion
+ return getMultiSendDeployment({ version, network: chainId.toString(), released: true })
+}
+
+export function getSafeProxyFactoryContractDeployment(
+ safeVersion: SafeVersion,
+ chainId: number
+): SingletonDeployment | undefined {
+ const version = safeDeploymentsVersions[safeVersion].safeProxyFactoryVersion
+ return getProxyFactoryDeployment({ version, network: chainId.toString(), released: true })
+}
diff --git a/packages/safe-core-sdk/src/ethereumLibs/EthAdapter.ts b/packages/safe-core-sdk/src/ethereumLibs/EthAdapter.ts
index b671e9a9f..4aaba31cb 100644
--- a/packages/safe-core-sdk/src/ethereumLibs/EthAdapter.ts
+++ b/packages/safe-core-sdk/src/ethereumLibs/EthAdapter.ts
@@ -1,4 +1,5 @@
import { BigNumber } from '@ethersproject/bignumber'
+import { SafeVersion } from '../contracts/config'
import GnosisSafeContract from '../contracts/GnosisSafe/GnosisSafeContract'
import GnosisSafeProxyFactoryContract from '../contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryContract'
import MultiSendContract from '../contracts/MultiSend/MultiSendContract'
@@ -13,9 +14,11 @@ export interface EthAdapterTransaction {
gasLimit?: number
}
-export interface GnosisSafeContracts {
- gnosisSafeContract: GnosisSafeContract
- multiSendContract: MultiSendContract
+export interface GetSafeContractProps {
+ safeVersion: SafeVersion
+ chainId: number
+ isL1SafeMasterCopy?: boolean
+ customContractAddress?: string
}
interface EthAdapter {
@@ -23,11 +26,22 @@ interface EthAdapter {
getBalance(address: string): Promise
getChainId(): Promise
getContract(address: string, abi: AbiItem[]): any
- getSafeContract(safeAddress: string): Promise
- getMultiSendContract(multiSendAddress: string): Promise
- getGnosisSafeProxyFactoryContract(
- proxyFactoryAddress: string
- ): Promise
+ getSafeContract({
+ safeVersion,
+ chainId,
+ isL1SafeMasterCopy,
+ customContractAddress
+ }: GetSafeContractProps): GnosisSafeContract
+ getMultiSendContract(
+ safeVersion: SafeVersion,
+ chainId: number,
+ customContractAddress?: string
+ ): MultiSendContract
+ getSafeProxyFactoryContract(
+ safeVersion: SafeVersion,
+ chainId: number,
+ customContractAddress?: string
+ ): GnosisSafeProxyFactoryContract
getContractCode(address: string): Promise
getTransaction(transactionHash: string): Promise
getSignerAddress(): Promise
diff --git a/packages/safe-core-sdk/src/ethereumLibs/EthersAdapter.ts b/packages/safe-core-sdk/src/ethereumLibs/EthersAdapter.ts
index 71a4345e9..615cb55ba 100644
--- a/packages/safe-core-sdk/src/ethereumLibs/EthersAdapter.ts
+++ b/packages/safe-core-sdk/src/ethereumLibs/EthersAdapter.ts
@@ -3,14 +3,22 @@ import { Signer } from '@ethersproject/abstract-signer'
import { BigNumber } from '@ethersproject/bignumber'
import { Contract } from '@ethersproject/contracts'
import { Provider } from '@ethersproject/providers'
-import { GnosisSafeProxyFactory__factory } from '../../typechain/src/ethers-v5/factories/GnosisSafeProxyFactory__factory'
-import { GnosisSafe__factory } from '../../typechain/src/ethers-v5/factories/GnosisSafe__factory'
-import { MultiSend__factory } from '../../typechain/src/ethers-v5/factories/MultiSend__factory'
-import GnosisSafeEthersV5Contract from '../contracts/GnosisSafe/GnosisSafeEthersV5Contract'
-import GnosisSafeProxyFactoryEthersV5Contract from '../contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryEthersV5Contract'
-import MultiSendEthersV5Contract from '../contracts/MultiSend/MultiSendEthersV5Contract'
+import { SafeVersion } from '../contracts/config'
+import {
+ getMultiSendContractInstance,
+ getSafeContractInstance,
+ getSafeProxyFactoryContractInstance
+} from '../contracts/contractInstancesEthers'
+import GnosisSafeContractEthers from '../contracts/GnosisSafe/GnosisSafeContractEthers'
+import GnosisSafeProxyFactoryEthersContract from '../contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryEthersContract'
+import MultiSendEthersContract from '../contracts/MultiSend/MultiSendEthersContract'
+import {
+ getMultiSendContractDeployment,
+ getSafeContractDeployment,
+ getSafeProxyFactoryContractDeployment
+} from '../contracts/safeDeploymentContracts'
import { AbiItem } from '../types'
-import EthAdapter, { EthAdapterTransaction } from './EthAdapter'
+import EthAdapter, { EthAdapterTransaction, GetSafeContractProps } from './EthAdapter'
export interface EthersAdapterConfig {
/** ethers - Ethers v5 library */
@@ -56,41 +64,63 @@ class EthersAdapter implements EthAdapter {
return (await this.#provider.getNetwork()).chainId
}
- async getSafeContract(safeAddress: string): Promise {
- const safeContractCode = await this.getContractCode(safeAddress)
- if (safeContractCode === '0x') {
- throw new Error('Safe Proxy contract is not deployed in the current network')
+ getSafeContract({
+ safeVersion,
+ chainId,
+ isL1SafeMasterCopy,
+ customContractAddress
+ }: GetSafeContractProps): GnosisSafeContractEthers {
+ let contractAddress: string | undefined
+ if (customContractAddress) {
+ contractAddress = customContractAddress
+ } else {
+ const safeSingletonDeployment = getSafeContractDeployment(
+ safeVersion,
+ chainId,
+ isL1SafeMasterCopy
+ )
+ contractAddress = safeSingletonDeployment?.networkAddresses[chainId]
}
- const safeContract = GnosisSafe__factory.connect(safeAddress, this.#signer)
- const wrapperSafeContract = new GnosisSafeEthersV5Contract(safeContract)
- return wrapperSafeContract
- }
-
- async getMultiSendContract(multiSendAddress: string): Promise {
- const multiSendContractCode = await this.getContractCode(multiSendAddress)
- if (multiSendContractCode === '0x') {
- throw new Error('MultiSend contract is not deployed in the current network')
+ if (!contractAddress) {
+ throw new Error('Invalid Safe Proxy contract address')
}
- const multiSendContract = MultiSend__factory.connect(multiSendAddress, this.#signer)
- const wrappedMultiSendContract = new MultiSendEthersV5Contract(multiSendContract)
- return wrappedMultiSendContract
- }
-
- async getGnosisSafeProxyFactoryContract(
- proxyFactoryAddress: string
- ): Promise {
- const proxyFactoryContractCode = await this.getContractCode(proxyFactoryAddress)
- if (proxyFactoryContractCode === '0x') {
- throw new Error('Safe Proxy Factory contract is not deployed in the current network')
+ return getSafeContractInstance(safeVersion, contractAddress, this.#signer)
+ }
+
+ getMultiSendContract(
+ safeVersion: SafeVersion,
+ chainId: number,
+ customContractAddress?: string
+ ): MultiSendEthersContract {
+ let contractAddress: string | undefined
+ if (customContractAddress) {
+ contractAddress = customContractAddress
+ } else {
+ const multiSendDeployment = getMultiSendContractDeployment(safeVersion, chainId)
+ contractAddress = multiSendDeployment?.networkAddresses[chainId]
+ }
+ if (!contractAddress) {
+ throw new Error('Invalid Multi Send contract address')
+ }
+ return getMultiSendContractInstance(safeVersion, contractAddress, this.#signer)
+ }
+
+ getSafeProxyFactoryContract(
+ safeVersion: SafeVersion,
+ chainId: number,
+ customContractAddress?: string
+ ): GnosisSafeProxyFactoryEthersContract {
+ let contractAddress: string | undefined
+ if (customContractAddress) {
+ contractAddress = customContractAddress
+ } else {
+ const proxyFactoryDeployment = getSafeProxyFactoryContractDeployment(safeVersion, chainId)
+ contractAddress = proxyFactoryDeployment?.networkAddresses[chainId]
+ }
+ if (!contractAddress) {
+ throw new Error('Invalid Safe Proxy Factory contract address')
}
- const proxyFactoryContract = GnosisSafeProxyFactory__factory.connect(
- proxyFactoryAddress,
- this.#signer
- )
- const wrappedProxyFactoryContract = new GnosisSafeProxyFactoryEthersV5Contract(
- proxyFactoryContract
- )
- return wrappedProxyFactoryContract
+ return getSafeProxyFactoryContractInstance(safeVersion, contractAddress, this.#signer)
}
getContract(address: string, abi: AbiItem[]): Contract {
diff --git a/packages/safe-core-sdk/src/ethereumLibs/Web3Adapter.ts b/packages/safe-core-sdk/src/ethereumLibs/Web3Adapter.ts
index 58f6e09f6..8d78986af 100644
--- a/packages/safe-core-sdk/src/ethereumLibs/Web3Adapter.ts
+++ b/packages/safe-core-sdk/src/ethereumLibs/Web3Adapter.ts
@@ -1,15 +1,20 @@
import { BigNumber } from '@ethersproject/bignumber'
-import { GnosisSafe } from '../../typechain/src/web3-v1/GnosisSafe'
-import { GnosisSafeProxyFactory } from '../../typechain/src/web3-v1/GnosisSafeProxyFactory'
-import { MultiSend } from '../../typechain/src/web3-v1/MultiSend'
-import GnosisSafeWeb3Contract from '../contracts/GnosisSafe/GnosisSafeWeb3Contract'
-import SafeAbiV120 from '../contracts/GnosisSafe/SafeAbiV1-2-0.json'
-import GnosisSafeProxyFactoryAbiV120 from '../contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryAbiV1-2-0.json'
+import { SafeVersion } from '../contracts/config'
+import {
+ getGnosisSafeProxyFactoryContractInstance,
+ getMultiSendContractInstance,
+ getSafeContractInstance
+} from '../contracts/contractInstancesWeb3'
+import GnosisSafeContractWeb3 from '../contracts/GnosisSafe/GnosisSafeContractWeb3'
import GnosisSafeProxyFactoryWeb3Contract from '../contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryWeb3Contract'
-import MultiSendAbi from '../contracts/MultiSend/MultiSendAbi.json'
import MultiSendWeb3Contract from '../contracts/MultiSend/MultiSendWeb3Contract'
+import {
+ getMultiSendContractDeployment,
+ getSafeContractDeployment,
+ getSafeProxyFactoryContractDeployment
+} from '../contracts/safeDeploymentContracts'
import { AbiItem } from '../types'
-import EthAdapter, { EthAdapterTransaction } from './EthAdapter'
+import EthAdapter, { EthAdapterTransaction, GetSafeContractProps } from './EthAdapter'
export interface Web3AdapterConfig {
/** web3 - Web3 library */
@@ -42,43 +47,66 @@ class Web3Adapter implements EthAdapter {
return this.#web3.eth.getChainId()
}
- async getContract(address: string, abi: AbiItem[]): Promise {
- return new this.#web3.eth.Contract(abi, address)
+ getSafeContract({
+ safeVersion,
+ chainId,
+ isL1SafeMasterCopy,
+ customContractAddress
+ }: GetSafeContractProps): GnosisSafeContractWeb3 {
+ const safeSingletonDeployment = getSafeContractDeployment(
+ safeVersion,
+ chainId,
+ isL1SafeMasterCopy
+ )
+ const contractAddress =
+ customContractAddress ?? safeSingletonDeployment?.networkAddresses[chainId]
+ if (!contractAddress) {
+ throw new Error('Invalid Safe Proxy contract address')
+ }
+ const safeContract = this.getContract(
+ contractAddress,
+ safeSingletonDeployment?.abi as AbiItem[]
+ )
+ return getSafeContractInstance(safeVersion, safeContract)
}
- async getSafeContract(safeAddress: string): Promise {
- const safeContractCode = await this.getContractCode(safeAddress)
- if (safeContractCode === '0x') {
- throw new Error('Safe Proxy contract is not deployed in the current network')
+ getMultiSendContract(
+ safeVersion: SafeVersion,
+ chainId: number,
+ customContractAddress?: string
+ ): MultiSendWeb3Contract {
+ const multiSendDeployment = getMultiSendContractDeployment(safeVersion, chainId)
+ const contractAddress = customContractAddress ?? multiSendDeployment?.networkAddresses[chainId]
+ if (!contractAddress) {
+ throw new Error('Invalid Multi Send contract addresss')
}
- const safeContract = (await this.getContract(safeAddress, SafeAbiV120)) as GnosisSafe
- const wrapperSafeContract = new GnosisSafeWeb3Contract(safeContract)
- return wrapperSafeContract
+ const multiSendContract = this.getContract(
+ contractAddress,
+ multiSendDeployment?.abi as AbiItem[]
+ )
+ return getMultiSendContractInstance(safeVersion, multiSendContract)
}
- async getMultiSendContract(multiSendAddress: string): Promise {
- const multiSendContractCode = await this.getContractCode(multiSendAddress)
- if (multiSendContractCode === '0x') {
- throw new Error('MultiSend contract is not deployed in the current network')
+ getSafeProxyFactoryContract(
+ safeVersion: SafeVersion,
+ chainId: number,
+ customContractAddress?: string
+ ): GnosisSafeProxyFactoryWeb3Contract {
+ const proxyFactoryDeployment = getSafeProxyFactoryContractDeployment(safeVersion, chainId)
+ const contractAddress =
+ customContractAddress ?? proxyFactoryDeployment?.networkAddresses[chainId]
+ if (!contractAddress) {
+ throw new Error('Invalid Safe Proxy Factory contract address')
}
- const multiSendContract = (await this.getContract(multiSendAddress, MultiSendAbi)) as MultiSend
- const wrappedMultiSendContract = new MultiSendWeb3Contract(multiSendContract)
- return wrappedMultiSendContract
+ const proxyFactoryContract = this.getContract(
+ contractAddress,
+ proxyFactoryDeployment?.abi as AbiItem[]
+ )
+ return getGnosisSafeProxyFactoryContractInstance(safeVersion, proxyFactoryContract)
}
- async getGnosisSafeProxyFactoryContract(
- proxyFactoryAddress: string
- ): Promise {
- const proxyFactoryContractCode = await this.getContractCode(proxyFactoryAddress)
- if (proxyFactoryContractCode === '0x') {
- throw new Error('Safe Proxy Factory contract is not deployed in the current network')
- }
- const proxyFactoryContract = (await this.getContract(
- proxyFactoryAddress,
- GnosisSafeProxyFactoryAbiV120
- )) as GnosisSafeProxyFactory
- const wrappedProxyFactoryContract = new GnosisSafeProxyFactoryWeb3Contract(proxyFactoryContract)
- return wrappedProxyFactoryContract
+ getContract(address: string, abi: AbiItem[]): any {
+ return new this.#web3.eth.Contract(abi, address)
}
async getContractCode(address: string): Promise {
diff --git a/packages/safe-core-sdk/src/index.ts b/packages/safe-core-sdk/src/index.ts
index 4552edb1c..ad1aad542 100644
--- a/packages/safe-core-sdk/src/index.ts
+++ b/packages/safe-core-sdk/src/index.ts
@@ -1,4 +1,4 @@
-import { ContractNetworksConfig } from './configuration/contracts'
+import { SafeVersion } from './contracts/config'
import EthAdapter, { EthAdapterTransaction } from './ethereumLibs/EthAdapter'
import EthersAdapter, { EthersAdapterConfig } from './ethereumLibs/EthersAdapter'
import Web3Adapter, { Web3AdapterConfig } from './ethereumLibs/Web3Adapter'
@@ -14,6 +14,7 @@ import SafeFactory, {
SafeDeploymentConfig,
SafeFactoryConfig
} from './safeFactory'
+import { ContractNetworksConfig } from './types'
import EthSignSignature from './utils/signatures/SafeSignature'
import {
SafeTransactionOptionalProps,
@@ -23,6 +24,7 @@ import {
export default Safe
export {
+ SafeVersion,
SafeFactory,
SafeFactoryConfig,
SafeAccountConfig,
diff --git a/packages/safe-core-sdk/src/managers/contractManager.ts b/packages/safe-core-sdk/src/managers/contractManager.ts
index 20bf4e2e8..e53d2613e 100644
--- a/packages/safe-core-sdk/src/managers/contractManager.ts
+++ b/packages/safe-core-sdk/src/managers/contractManager.ts
@@ -1,43 +1,77 @@
-import { ContractNetworksConfig, defaultContractNetworks } from '../configuration/contracts'
+import { SafeVersion, SAFE_BASE_VERSION } from '../contracts/config'
import GnosisSafeContract from '../contracts/GnosisSafe/GnosisSafeContract'
import MultiSendContract from '../contracts/MultiSend/MultiSendContract'
-import EthAdapter from '../ethereumLibs/EthAdapter'
+import { SafeConfig } from '../Safe'
+import { ContractNetworksConfig } from '../types'
class ContractManager {
- #contractNetworks!: ContractNetworksConfig
+ #contractNetworks?: ContractNetworksConfig
+ #isL1SafeMasterCopy?: boolean
#safeContract!: GnosisSafeContract
#multiSendContract!: MultiSendContract
- static async create(
- ethAdapter: EthAdapter,
- safeAddress: string,
- contractNetworks?: ContractNetworksConfig
- ): Promise {
+ static async create({
+ ethAdapter,
+ safeAddress,
+ isL1SafeMasterCopy,
+ contractNetworks
+ }: SafeConfig): Promise {
const contractManager = new ContractManager()
- await contractManager.init(ethAdapter, safeAddress, contractNetworks)
+ await contractManager.init({ ethAdapter, safeAddress, isL1SafeMasterCopy, contractNetworks })
return contractManager
}
- async init(
- ethAdapter: EthAdapter,
- safeAddress: string,
- contractNetworks?: ContractNetworksConfig
- ): Promise {
+ async init({
+ ethAdapter,
+ safeAddress,
+ isL1SafeMasterCopy,
+ contractNetworks
+ }: SafeConfig): Promise {
const chainId = await ethAdapter.getChainId()
- const contractNetworksConfig = { ...defaultContractNetworks, ...contractNetworks }
- const contracts = contractNetworksConfig[chainId]
- if (!contracts) {
- throw new Error('Safe contracts not found in the current network')
+ const temporarySafeContract = ethAdapter.getSafeContract({
+ safeVersion: SAFE_BASE_VERSION,
+ chainId,
+ isL1SafeMasterCopy,
+ customContractAddress: safeAddress
+ })
+ if ((await ethAdapter.getContractCode(temporarySafeContract.getAddress())) === '0x') {
+ throw new Error('Safe Proxy contract is not deployed in the current network')
}
- this.#contractNetworks = contractNetworksConfig
- this.#safeContract = await ethAdapter.getSafeContract(safeAddress)
- this.#multiSendContract = await ethAdapter.getMultiSendContract(contracts.multiSendAddress)
+ const safeVersion = (await temporarySafeContract.getVersion()) as SafeVersion
+
+ const customContracts = contractNetworks?.[chainId]
+ this.#contractNetworks = contractNetworks
+ this.#isL1SafeMasterCopy = isL1SafeMasterCopy
+ const safeContract = ethAdapter.getSafeContract({
+ safeVersion,
+ chainId,
+ isL1SafeMasterCopy,
+ customContractAddress: safeAddress
+ })
+ if ((await ethAdapter.getContractCode(safeContract.getAddress())) === '0x') {
+ throw new Error('Safe Proxy contract is not deployed in the current network')
+ }
+ this.#safeContract = safeContract
+
+ const multiSendContract = await ethAdapter.getMultiSendContract(
+ safeVersion,
+ chainId,
+ customContracts?.multiSendAddress
+ )
+ if ((await ethAdapter.getContractCode(multiSendContract.getAddress())) === '0x') {
+ throw new Error('Multi Send contract is not deployed in the current network')
+ }
+ this.#multiSendContract = multiSendContract
}
- get contractNetworks(): ContractNetworksConfig {
+ get contractNetworks(): ContractNetworksConfig | undefined {
return this.#contractNetworks
}
+ get isL1SafeMasterCopy(): boolean | undefined {
+ return this.#isL1SafeMasterCopy
+ }
+
get safeContract(): GnosisSafeContract {
return this.#safeContract
}
diff --git a/packages/safe-core-sdk/src/safeFactory/index.ts b/packages/safe-core-sdk/src/safeFactory/index.ts
index f713528e8..390420e59 100644
--- a/packages/safe-core-sdk/src/safeFactory/index.ts
+++ b/packages/safe-core-sdk/src/safeFactory/index.ts
@@ -1,8 +1,9 @@
-import { ContractNetworksConfig, defaultContractNetworks } from '../configuration/contracts'
+import { SafeVersion, SAFE_LAST_VERSION } from '../contracts/config'
import GnosisSafeContract from '../contracts/GnosisSafe/GnosisSafeContract'
import GnosisSafeProxyFactoryContract from '../contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryContract'
import EthAdapter from '../ethereumLibs/EthAdapter'
import Safe from '../Safe'
+import { ContractNetworksConfig } from '../types'
import { EMPTY_DATA, ZERO_ADDRESS } from '../utils/constants'
import { validateSafeAccountConfig } from './utils'
@@ -24,42 +25,90 @@ export interface SafeDeploymentConfig {
export interface SafeFactoryConfig {
/** ethAdapter - Ethereum adapter */
ethAdapter: EthAdapter
+ /** safeVersion - Versions of the Safe deployed by this Factory contract */
+ safeVersion?: SafeVersion
+ /** isL1SafeMasterCopy - Forces to use the Gnosis Safe L1 version of the contract instead of the L2 version */
+ isL1SafeMasterCopy?: boolean
+ /** contractNetworks - Contract network configuration */
+ contractNetworks?: ContractNetworksConfig
+}
+
+interface SafeFactoryInitConfig {
+ /** ethAdapter - Ethereum adapter */
+ ethAdapter: EthAdapter
+ /** safeVersion - Versions of the Safe deployed by this Factory contract */
+ safeVersion: SafeVersion
+ /** isL1SafeMasterCopy - Forces to use the Gnosis Safe L1 version of the contract instead of the L2 version */
+ isL1SafeMasterCopy?: boolean
/** contractNetworks - Contract network configuration */
contractNetworks?: ContractNetworksConfig
}
class SafeFactory {
- #contractNetworks!: ContractNetworksConfig
+ #contractNetworks?: ContractNetworksConfig
+ #isL1SafeMasterCopy?: boolean
+ #safeVersion!: SafeVersion
#ethAdapter!: EthAdapter
#safeProxyFactoryContract!: GnosisSafeProxyFactoryContract
#gnosisSafeContract!: GnosisSafeContract
- static async create({ ethAdapter, contractNetworks }: SafeFactoryConfig): Promise {
+ static async create({
+ ethAdapter,
+ safeVersion = SAFE_LAST_VERSION,
+ isL1SafeMasterCopy = false,
+ contractNetworks
+ }: SafeFactoryConfig): Promise {
const safeFactorySdk = new SafeFactory()
- await safeFactorySdk.init({ ethAdapter, contractNetworks })
+ await safeFactorySdk.init({ ethAdapter, safeVersion, isL1SafeMasterCopy, contractNetworks })
return safeFactorySdk
}
- private async init({ ethAdapter, contractNetworks }: SafeFactoryConfig): Promise {
+ private async init({
+ ethAdapter,
+ safeVersion,
+ isL1SafeMasterCopy,
+ contractNetworks
+ }: SafeFactoryInitConfig): Promise {
this.#ethAdapter = ethAdapter
- this.#contractNetworks = { ...defaultContractNetworks, ...contractNetworks }
+ this.#safeVersion = safeVersion
+ this.#isL1SafeMasterCopy = isL1SafeMasterCopy
+ this.#contractNetworks = contractNetworks
const chainId = await this.#ethAdapter.getChainId()
- const contractNetworksConfig = this.#contractNetworks[chainId]
- if (!contractNetworksConfig) {
- throw new Error('Safe contracts not found in the current network')
- }
- this.#safeProxyFactoryContract = await ethAdapter.getGnosisSafeProxyFactoryContract(
- this.#contractNetworks[chainId].safeProxyFactoryAddress
- )
- this.#gnosisSafeContract = await ethAdapter.getSafeContract(
- contractNetworksConfig.safeMasterCopyAddress
+ const customContracts = contractNetworks?.[chainId]
+ const safeProxyFactoryContract = await ethAdapter.getSafeProxyFactoryContract(
+ this.#safeVersion,
+ chainId,
+ customContracts?.safeProxyFactoryAddress
)
+ if ((await this.#ethAdapter.getContractCode(safeProxyFactoryContract.getAddress())) === '0x') {
+ throw new Error('Safe Proxy Factory contract is not deployed in the current network')
+ }
+ this.#safeProxyFactoryContract = safeProxyFactoryContract
+
+ const gnosisSafeContract = ethAdapter.getSafeContract({
+ safeVersion: this.#safeVersion,
+ chainId,
+ isL1SafeMasterCopy,
+ customContractAddress: customContracts?.safeMasterCopyAddress
+ })
+ if ((await this.#ethAdapter.getContractCode(gnosisSafeContract.getAddress())) === '0x') {
+ throw new Error('Safe Proxy contract is not deployed in the current network')
+ }
+ this.#gnosisSafeContract = gnosisSafeContract
}
getEthAdapter(): EthAdapter {
return this.#ethAdapter
}
+ getSafeVersion(): SafeVersion {
+ return this.#safeVersion
+ }
+
+ getAddress(): string {
+ return this.#safeProxyFactoryContract.getAddress()
+ }
+
async getChainId(): Promise {
return this.#ethAdapter.getChainId()
}
@@ -86,15 +135,12 @@ class SafeFactory {
])
}
- getAddress() {
- return this.#safeProxyFactoryContract.getAddress()
- }
-
async deploySafe(
safeAccountConfig: SafeAccountConfig,
safeDeploymentConfig?: SafeDeploymentConfig
): Promise {
validateSafeAccountConfig(safeAccountConfig)
+ const chainId = await this.#ethAdapter.getChainId()
const signerAddress = await this.#ethAdapter.getSignerAddress()
const initializer = await this.encodeSetupCallData(safeAccountConfig)
const safeAddress = await this.#safeProxyFactoryContract.createProxy({
@@ -103,10 +149,19 @@ class SafeFactory {
saltNonce: safeDeploymentConfig?.saltNonce,
options: { from: signerAddress }
})
- const safeContract = await this.#ethAdapter.getSafeContract(safeAddress)
- const safe = Safe.create({
+ const safeContract = await this.#ethAdapter.getSafeContract({
+ safeVersion: this.#safeVersion,
+ chainId,
+ isL1SafeMasterCopy: this.#isL1SafeMasterCopy,
+ customContractAddress: safeAddress
+ })
+ if ((await this.#ethAdapter.getContractCode(safeContract.getAddress())) === '0x') {
+ throw new Error('Safe Proxy contract is not deployed in the current network')
+ }
+ const safe = await Safe.create({
ethAdapter: this.#ethAdapter,
safeAddress: safeContract.getAddress(),
+ isL1SafeMasterCopy: this.#isL1SafeMasterCopy,
contractNetworks: this.#contractNetworks
})
return safe
diff --git a/packages/safe-core-sdk/src/types/index.ts b/packages/safe-core-sdk/src/types/index.ts
index b2e45b434..d75107122 100644
--- a/packages/safe-core-sdk/src/types/index.ts
+++ b/packages/safe-core-sdk/src/types/index.ts
@@ -24,3 +24,17 @@ export interface AbiItem {
type: string
gas?: number
}
+
+export interface ContractNetworkConfig {
+ /** multiSendAddress - Address of the MultiSend contract deployed on a specific network */
+ multiSendAddress: string
+ /** safeMasterCopyAddress - Address of the Gnosis Safe Master Copy contract deployed on a specific network */
+ safeMasterCopyAddress: string
+ /** safeProxyFactoryAddress - Address of the Gnosis Safe Proxy Factory contract deployed on a specific network */
+ safeProxyFactoryAddress: string
+}
+
+export interface ContractNetworksConfig {
+ /** id - Network id */
+ [id: string]: ContractNetworkConfig
+}
diff --git a/packages/safe-core-sdk/tests/contractManager.test.ts b/packages/safe-core-sdk/tests/contractManager.test.ts
index cf1a7fb02..f46339afb 100644
--- a/packages/safe-core-sdk/tests/contractManager.test.ts
+++ b/packages/safe-core-sdk/tests/contractManager.test.ts
@@ -46,7 +46,7 @@ describe('Safe contracts manager', () => {
safeAddress: safe.address
})
)
- .to.be.rejectedWith('Safe contracts not found in the current network')
+ .to.be.rejectedWith('Invalid Multi Send contract')
})
it('should fail if Safe Proxy contract is not deployed in the current network', async () => {
@@ -65,7 +65,7 @@ describe('Safe contracts manager', () => {
})
it('should fail if MultiSend contract is specified in contractNetworks but not deployed', async () => {
- const { safe, accounts, chainId, contractNetworks } = await setupTests()
+ const { safe, accounts, chainId } = await setupTests()
const customContractNetworks: ContractNetworksConfig = {
[chainId]: {
multiSendAddress: ZERO_ADDRESS,
@@ -83,7 +83,7 @@ describe('Safe contracts manager', () => {
contractNetworks: customContractNetworks
})
)
- .to.be.rejectedWith('MultiSend contract is not deployed in the current network')
+ .to.be.rejectedWith('Multi Send contract is not deployed in the current network')
})
it('should set the MultiSend contract available in the current network', async () => {
diff --git a/packages/safe-core-sdk/tests/core.test.ts b/packages/safe-core-sdk/tests/core.test.ts
index 880329b5e..264ffa63d 100644
--- a/packages/safe-core-sdk/tests/core.test.ts
+++ b/packages/safe-core-sdk/tests/core.test.ts
@@ -3,6 +3,7 @@ import { SafeTransactionDataPartial } from '@gnosis.pm/safe-core-sdk-types'
import chai from 'chai'
import chaiAsPromised from 'chai-as-promised'
import { deployments, waffle } from 'hardhat'
+import { safeVersionDeployed } from '../hardhat/deploy/deploy-contracts'
import Safe, { ContractNetworksConfig } from '../src'
import {
getFactory,
@@ -16,7 +17,7 @@ import { waitSafeTxReceipt } from './utils/transactions'
chai.use(chaiAsPromised)
-describe('Safe Core SDK', () => {
+describe('Safe Info', () => {
const setupTests = deployments.createFixture(async ({ deployments }) => {
await deployments.fixture()
const accounts = await getAccounts()
@@ -78,7 +79,7 @@ describe('Safe Core SDK', () => {
contractNetworks
})
const contractVersion = await safeSdk.getContractVersion()
- chai.expect(contractVersion).to.be.eq('1.2.0')
+ chai.expect(contractVersion).to.be.eq(safeVersionDeployed)
})
})
diff --git a/packages/safe-core-sdk/tests/ethAdapters.test.ts b/packages/safe-core-sdk/tests/ethAdapters.test.ts
new file mode 100644
index 000000000..ed2406bd7
--- /dev/null
+++ b/packages/safe-core-sdk/tests/ethAdapters.test.ts
@@ -0,0 +1,142 @@
+import chai from 'chai'
+import chaiAsPromised from 'chai-as-promised'
+import { deployments, waffle } from 'hardhat'
+import { ContractNetworksConfig, SafeVersion } from '../src'
+import { getFactory, getMultiSend, getSafeSingleton } from './utils/setupContracts'
+import { getEthAdapter } from './utils/setupEthAdapter'
+import { getAccounts } from './utils/setupTestNetwork'
+
+chai.use(chaiAsPromised)
+
+describe('Safe contracts', () => {
+ const setupTests = deployments.createFixture(async ({ deployments }) => {
+ await deployments.fixture()
+ const accounts = await getAccounts()
+ const chainId: number = (await waffle.provider.getNetwork()).chainId
+ const contractNetworks: ContractNetworksConfig = {
+ [chainId]: {
+ multiSendAddress: (await getMultiSend()).address,
+ safeMasterCopyAddress: (await getSafeSingleton()).address,
+ safeProxyFactoryAddress: (await getFactory()).address
+ }
+ }
+ return {
+ accounts,
+ contractNetworks,
+ chainId
+ }
+ })
+
+ describe('getSafeContract', async () => {
+ it('should return an L1 Safe contract from safe-deployments', async () => {
+ const { accounts } = await setupTests()
+ const [account1] = accounts
+ const ethAdapter = await getEthAdapter(account1.signer)
+ const safeVersion: SafeVersion = '1.3.0'
+ const chainId = 1
+ const safeContract = await ethAdapter.getSafeContract({ safeVersion, chainId })
+ chai
+ .expect(await safeContract.getAddress())
+ .to.be.eq('0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552')
+ })
+
+ it('should return an L2 Safe contract from safe-deployments', async () => {
+ const { accounts } = await setupTests()
+ const [account1] = accounts
+ const ethAdapter = await getEthAdapter(account1.signer)
+ const safeVersion: SafeVersion = '1.3.0'
+ const chainId = 100
+ const safeContract = await ethAdapter.getSafeContract({ safeVersion, chainId })
+ chai
+ .expect(await safeContract.getAddress())
+ .to.be.eq('0x3E5c63644E683549055b9Be8653de26E0B4CD36E')
+ })
+
+ it('should return an L1 Safe contract from safe-deployments using the L1 flag', async () => {
+ const { accounts } = await setupTests()
+ const [account1] = accounts
+ const ethAdapter = await getEthAdapter(account1.signer)
+ const safeVersion: SafeVersion = '1.3.0'
+ const chainId = 100
+ const isL1SafeMasterCopy = true
+ const safeContract = await ethAdapter.getSafeContract({
+ safeVersion,
+ chainId,
+ isL1SafeMasterCopy
+ })
+ chai
+ .expect(await safeContract.getAddress())
+ .to.be.eq('0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552')
+ })
+
+ it('should return a Safe contract from the custom addresses', async () => {
+ const { accounts, contractNetworks, chainId } = await setupTests()
+ const [account1] = accounts
+ const ethAdapter = await getEthAdapter(account1.signer)
+ const safeVersion: SafeVersion = '1.3.0'
+ const customContractAddress = contractNetworks[chainId].safeMasterCopyAddress
+ const safeContract = await ethAdapter.getSafeContract({
+ safeVersion,
+ chainId,
+ customContractAddress
+ })
+ chai.expect(await safeContract.getAddress()).to.be.eq((await getSafeSingleton()).address)
+ })
+ })
+
+ describe('getMultiSendContract', async () => {
+ it('should return a Multi Send contract from safe-deployments', async () => {
+ const { accounts } = await setupTests()
+ const [account1] = accounts
+ const ethAdapter = await getEthAdapter(account1.signer)
+ const safeVersion: SafeVersion = '1.3.0'
+ const chainId = 1
+ const multiSendContract = await ethAdapter.getMultiSendContract(safeVersion, chainId)
+ chai
+ .expect(await multiSendContract.getAddress())
+ .to.be.eq('0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761')
+ })
+
+ it('should return a Multi Send contract from the custom addresses', async () => {
+ const { accounts, contractNetworks, chainId } = await setupTests()
+ const [account1] = accounts
+ const ethAdapter = await getEthAdapter(account1.signer)
+ const safeVersion: SafeVersion = '1.3.0'
+ const customContractAddress = contractNetworks[chainId].multiSendAddress
+ const multiSendContract = await ethAdapter.getMultiSendContract(
+ safeVersion,
+ chainId,
+ customContractAddress
+ )
+ chai.expect(await multiSendContract.getAddress()).to.be.eq((await getMultiSend()).address)
+ })
+ })
+
+ describe('getSafeProxyFactoryContract', async () => {
+ it('should return a Safe Proxy Factory contract from safe-deployments', async () => {
+ const { accounts } = await setupTests()
+ const [account1] = accounts
+ const ethAdapter = await getEthAdapter(account1.signer)
+ const safeVersion: SafeVersion = '1.3.0'
+ const chainId = 1
+ const factoryContract = await ethAdapter.getSafeProxyFactoryContract(safeVersion, chainId)
+ chai
+ .expect(await factoryContract.getAddress())
+ .to.be.eq('0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2')
+ })
+
+ it('should return a Safe Proxy Factory contract from the custom addresses', async () => {
+ const { accounts, contractNetworks, chainId } = await setupTests()
+ const [account1] = accounts
+ const ethAdapter = await getEthAdapter(account1.signer)
+ const safeVersion: SafeVersion = '1.3.0'
+ const customContractAddress = contractNetworks[chainId].safeProxyFactoryAddress
+ const factoryContract = await ethAdapter.getSafeProxyFactoryContract(
+ safeVersion,
+ chainId,
+ customContractAddress
+ )
+ chai.expect(await factoryContract.getAddress()).to.be.eq((await getFactory()).address)
+ })
+ })
+})
diff --git a/packages/safe-core-sdk/tests/execution.test.ts b/packages/safe-core-sdk/tests/execution.test.ts
index dc6067980..a48396119 100644
--- a/packages/safe-core-sdk/tests/execution.test.ts
+++ b/packages/safe-core-sdk/tests/execution.test.ts
@@ -3,6 +3,7 @@ import { MetaTransactionData, SafeTransactionDataPartial } from '@gnosis.pm/safe
import chai from 'chai'
import chaiAsPromised from 'chai-as-promised'
import { deployments, waffle } from 'hardhat'
+import { safeVersionDeployed } from '../hardhat/deploy/deploy-contracts'
import Safe, { ContractNetworksConfig, TransactionOptions } from '../src'
import {
getERC20Mintable,
@@ -141,7 +142,7 @@ describe('Transactions execution', () => {
await safeSdk1.signTransaction(tx)
await chai
.expect(safeSdk2.executeTransaction(tx))
- .to.be.rejectedWith('Invalid owner provided')
+ .to.be.rejectedWith(safeVersionDeployed === '1.3.0' ? 'GS026' : 'Invalid owner provided')
})
it('should execute a transaction with threshold 1', async () => {
diff --git a/packages/safe-core-sdk/tests/safeFactory.test.ts b/packages/safe-core-sdk/tests/safeFactory.test.ts
index 091f9b9f6..f7c692c99 100644
--- a/packages/safe-core-sdk/tests/safeFactory.test.ts
+++ b/packages/safe-core-sdk/tests/safeFactory.test.ts
@@ -1,12 +1,16 @@
import chai from 'chai'
import chaiAsPromised from 'chai-as-promised'
import { deployments, waffle } from 'hardhat'
+import { safeVersionDeployed } from '../hardhat/deploy/deploy-contracts'
import {
ContractNetworksConfig,
SafeAccountConfig,
SafeDeploymentConfig,
SafeFactory
} from '../src'
+import { SAFE_LAST_VERSION } from '../src/contracts/config'
+import { ZERO_ADDRESS } from '../src/utils/constants'
+import { itif } from './utils/helpers'
import { getFactory, getMultiSend, getSafeSingleton } from './utils/setupContracts'
import { getEthAdapter } from './utils/setupEthAdapter'
import { getAccounts } from './utils/setupTestNetwork'
@@ -39,7 +43,23 @@ describe('Safe Proxy Factory', () => {
const ethAdapter = await getEthAdapter(account1.signer)
chai
.expect(SafeFactory.create({ ethAdapter }))
- .rejectedWith('Safe contracts not found in the current network')
+ .rejectedWith('Invalid Safe Proxy Factory contract')
+ })
+
+ it('should fail if the contractNetworks provided are not deployed', async () => {
+ const { accounts, chainId } = await setupTests()
+ const [account1] = accounts
+ const ethAdapter = await getEthAdapter(account1.signer)
+ const contractNetworks: ContractNetworksConfig = {
+ [chainId]: {
+ multiSendAddress: ZERO_ADDRESS,
+ safeMasterCopyAddress: ZERO_ADDRESS,
+ safeProxyFactoryAddress: ZERO_ADDRESS
+ }
+ }
+ chai
+ .expect(SafeFactory.create({ ethAdapter, contractNetworks }))
+ .rejectedWith('Safe Proxy Factory contract is not deployed in the current network')
})
it('should instantiate the Safe Proxy Factory', async () => {
@@ -120,13 +140,15 @@ describe('Safe Proxy Factory', () => {
const { accounts, contractNetworks } = await setupTests()
const [account1, account2] = accounts
const ethAdapter = await getEthAdapter(account1.signer)
- const safeFactory = await SafeFactory.create({ ethAdapter, contractNetworks })
+ const safeFactory = await SafeFactory.create({
+ ethAdapter,
+ safeVersion: safeVersionDeployed,
+ contractNetworks
+ })
const owners = [account1.address, account2.address]
const threshold = 2
const safeAccountConfig: SafeAccountConfig = { owners, threshold }
const safe = await safeFactory.deploySafe(safeAccountConfig)
- const contractCodeFinal = ethAdapter.getContractCode(safe.getAddress())
- chai.expect(contractCodeFinal).to.not.be.eq('0x')
const deployedSafeOwners = await safe.getOwners()
chai.expect(deployedSafeOwners.toString()).to.be.eq(owners.toString())
const deployedSafeThreshold = await safe.getThreshold()
@@ -137,18 +159,53 @@ describe('Safe Proxy Factory', () => {
const { accounts, contractNetworks } = await setupTests()
const [account1, account2] = accounts
const ethAdapter = await getEthAdapter(account1.signer)
- const safeFactory = await SafeFactory.create({ ethAdapter, contractNetworks })
+ const safeFactory = await SafeFactory.create({
+ ethAdapter,
+ safeVersion: safeVersionDeployed,
+ contractNetworks
+ })
const owners = [account1.address, account2.address]
const threshold = 2
const safeAccountConfig: SafeAccountConfig = { owners, threshold }
const safeDeploymentConfig: SafeDeploymentConfig = { saltNonce: 1 }
const safe = await safeFactory.deploySafe(safeAccountConfig, safeDeploymentConfig)
- const contractCodeFinal = ethAdapter.getContractCode(safe.getAddress())
- chai.expect(contractCodeFinal).to.not.be.eq('0x')
const deployedSafeOwners = await safe.getOwners()
chai.expect(deployedSafeOwners.toString()).to.be.eq(owners.toString())
const deployedSafeThreshold = await safe.getThreshold()
chai.expect(deployedSafeThreshold).to.be.eq(threshold)
})
+
+ itif(safeVersionDeployed === SAFE_LAST_VERSION)(
+ 'should deploy last Safe version by default',
+ async () => {
+ const { accounts, contractNetworks } = await setupTests()
+ const [account1, account2] = accounts
+ const ethAdapter = await getEthAdapter(account1.signer)
+ const safeFactory = await SafeFactory.create({ ethAdapter, contractNetworks })
+ const owners = [account1.address, account2.address]
+ const threshold = 2
+ const safeAccountConfig: SafeAccountConfig = { owners, threshold }
+ const safe = await safeFactory.deploySafe(safeAccountConfig)
+ const safeInstanceVersion = await safe.getContractVersion()
+ chai.expect(safeInstanceVersion).to.be.eq(safeVersionDeployed)
+ }
+ )
+
+ it('should deploy a specific Safe version', async () => {
+ const { accounts, contractNetworks } = await setupTests()
+ const [account1, account2] = accounts
+ const ethAdapter = await getEthAdapter(account1.signer)
+ const safeFactory = await SafeFactory.create({
+ ethAdapter,
+ safeVersion: safeVersionDeployed,
+ contractNetworks
+ })
+ const owners = [account1.address, account2.address]
+ const threshold = 2
+ const safeAccountConfig: SafeAccountConfig = { owners, threshold }
+ const safe = await safeFactory.deploySafe(safeAccountConfig)
+ const safeInstanceVersion = await safe.getContractVersion()
+ chai.expect(safeInstanceVersion).to.be.eq(safeVersionDeployed)
+ })
})
})
diff --git a/packages/safe-core-sdk/tests/utils/helpers.ts b/packages/safe-core-sdk/tests/utils/helpers.ts
new file mode 100644
index 000000000..c3e7a1264
--- /dev/null
+++ b/packages/safe-core-sdk/tests/utils/helpers.ts
@@ -0,0 +1 @@
+export const itif = (condition: boolean) => (condition ? it : it.skip)
diff --git a/packages/safe-core-sdk/tests/utils/setupContracts.ts b/packages/safe-core-sdk/tests/utils/setupContracts.ts
index 460a7253e..f2d5f3a4e 100644
--- a/packages/safe-core-sdk/tests/utils/setupContracts.ts
+++ b/packages/safe-core-sdk/tests/utils/setupContracts.ts
@@ -1,37 +1,59 @@
import { AddressZero } from '@ethersproject/constants'
import { deployments, ethers } from 'hardhat'
-import { GnosisSafe, MultiSend } from '../../typechain/src/ethers-v5'
+import {
+ gnosisSafeDeployed,
+ multiSendDeployed,
+ proxyFactoryDeployed
+} from '../../hardhat/deploy/deploy-contracts'
+import {
+ GnosisSafe as GnosisSafe_V1_1_1,
+ MultiSend as MultiSend_V1_1_1,
+ ProxyFactory as ProxyFactory_V1_1_1
+} from '../../typechain/src/ethers-v5/v1.1.1'
+import { GnosisSafe as GnosisSafe_V1_2_0 } from '../../typechain/src/ethers-v5/v1.2.0/'
+import {
+ GnosisSafe as GnosisSafe_V1_3_0,
+ MultiSend as MultiSend_V1_3_0,
+ ProxyFactory as ProxyFactory_V1_3_0
+} from '../../typechain/src/ethers-v5/v1.3.0/'
import {
DailyLimitModule,
ERC20Mintable,
SocialRecoveryModule
} from '../../typechain/tests/ethers-v5'
-export const getSafeSingleton = async () => {
- const SafeDeployment = await deployments.get('GnosisSafe')
- const Safe = await ethers.getContractFactory('GnosisSafe')
- return Safe.attach(SafeDeployment.address)
+export const getSafeSingleton = async (): Promise<
+ GnosisSafe_V1_3_0 | GnosisSafe_V1_2_0 | GnosisSafe_V1_1_1
+> => {
+ const SafeDeployment = await deployments.get(gnosisSafeDeployed.name)
+ const Safe = await ethers.getContractFactory(gnosisSafeDeployed.name)
+ return Safe.attach(SafeDeployment.address) as
+ | GnosisSafe_V1_3_0
+ | GnosisSafe_V1_2_0
+ | GnosisSafe_V1_1_1
}
-export const getFactory = async () => {
- const FactoryDeployment = await deployments.get('GnosisSafeProxyFactory')
- const Factory = await ethers.getContractFactory('GnosisSafeProxyFactory')
- return Factory.attach(FactoryDeployment.address)
+export const getFactory = async (): Promise => {
+ const FactoryDeployment = await deployments.get(proxyFactoryDeployed.name)
+ const Factory = await ethers.getContractFactory(proxyFactoryDeployed.name)
+ return Factory.attach(FactoryDeployment.address) as ProxyFactory_V1_3_0 | ProxyFactory_V1_1_1
}
-export const getSafeTemplate = async (): Promise => {
+export const getSafeTemplate = async (): Promise<
+ GnosisSafe_V1_3_0 | GnosisSafe_V1_2_0 | GnosisSafe_V1_1_1
+> => {
const singleton = await getSafeSingleton()
const factory = await getFactory()
const template = await factory.callStatic.createProxy(singleton.address, '0x')
await factory.createProxy(singleton.address, '0x').then((tx: any) => tx.wait())
- const Safe = await ethers.getContractFactory('GnosisSafe')
- return Safe.attach(template) as GnosisSafe
+ const Safe = await ethers.getContractFactory(gnosisSafeDeployed.name)
+ return Safe.attach(template) as GnosisSafe_V1_3_0 | GnosisSafe_V1_2_0 | GnosisSafe_V1_1_1
}
export const getSafeWithOwners = async (
owners: string[],
threshold?: number
-): Promise => {
+): Promise => {
const template = await getSafeTemplate()
await template.setup(
owners,
@@ -43,13 +65,13 @@ export const getSafeWithOwners = async (
0,
AddressZero
)
- return template
+ return template as GnosisSafe_V1_3_0 | GnosisSafe_V1_2_0 | GnosisSafe_V1_1_1
}
-export const getMultiSend = async (): Promise => {
- const MultiSendDeployment = await deployments.get('MultiSend')
- const MultiSend = await ethers.getContractFactory('MultiSend')
- return MultiSend.attach(MultiSendDeployment.address) as MultiSend
+export const getMultiSend = async (): Promise => {
+ const MultiSendDeployment = await deployments.get(multiSendDeployed.name)
+ const MultiSend = await ethers.getContractFactory(multiSendDeployed.name)
+ return MultiSend.attach(MultiSendDeployment.address) as MultiSend_V1_3_0 | MultiSend_V1_1_1
}
export const getDailyLimitModule = async (): Promise => {
diff --git a/yarn.lock b/yarn.lock
index e227bd102..d34ca8d56 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1126,7 +1126,7 @@
"@ethersproject/properties" "^5.4.0"
"@ethersproject/strings" "^5.4.0"
-"@gnosis.pm/safe-contracts@1.2.0":
+"@gnosis.pm/safe-contracts-v1.2.0@npm:@gnosis.pm/safe-contracts@1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-contracts/-/safe-contracts-1.2.0.tgz#33e8332e09c19e8822fccb06c7d3eff806d091f6"
integrity sha512-lcpZodqztDgMICB0kAc8aJdQePwCaLJnbeNjgVTfLAo3fBckVRV06VHXg5IsZ26qLA4JfZ690Cb7TsDVE9ZF3w==
@@ -1138,6 +1138,11 @@
solc "0.5.17"
truffle "^5.1.21"
+"@gnosis.pm/safe-contracts-v1.3.0@npm:@gnosis.pm/safe-contracts@1.3.0":
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-contracts/-/safe-contracts-1.3.0.tgz#316741a7690d8751a1f701538cfc9ec80866eedc"
+ integrity sha512-1p+1HwGvxGUVzVkFjNzglwHrLNA67U/axP0Ct85FzzH8yhGJb4t9jDjPYocVMzLorDoWAfKicGy1akPY9jXRVw==
+
"@gnosis.pm/safe-core-sdk@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-core-sdk/-/safe-core-sdk-0.3.1.tgz#15171657490a90ac3773b6965c2af4b873af136f"
@@ -1151,6 +1156,11 @@
resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-deployments/-/safe-deployments-1.1.0.tgz#63133f4576c5d5f2779f61d322e1e449fb701b1c"
integrity sha512-eIH4gvBQLEksQXFN2vOG+JZpdSCR+q6uoZD7n8Jx/PF2nIaWVYV1CWo6kbWDwfwzgAQkDddM+8WBjnc84fbB+Q==
+"@gnosis.pm/safe-deployments@^1.4.0":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/@gnosis.pm/safe-deployments/-/safe-deployments-1.4.0.tgz#d49d3d36cc014ef62306d09c0f4761895a17d7a6"
+ integrity sha512-q4salJNQ/Gx0DnZJytAFO/U4OwGI6xTGtTJSOZK+C9Fh2NW8sep+YfSunHQvCLcu4b7WgWEBhxnCV6rpyveLHg==
+
"@graphql-tools/batch-delegate@^6.2.4", "@graphql-tools/batch-delegate@^6.2.6":
version "6.2.6"
resolved "https://registry.yarnpkg.com/@graphql-tools/batch-delegate/-/batch-delegate-6.2.6.tgz#fbea98dc825f87ef29ea5f3f371912c2a2aa2f2c"