Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a callback to Safe deployment #188

Merged
merged 5 commits into from
Apr 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface CreateProxyProps {
initializer: string
saltNonce: number
options?: TransactionOptions
callback?: (txHash: string) => void
}

export interface GnosisSafeProxyFactoryContract {
Expand Down
7 changes: 5 additions & 2 deletions packages/safe-core-sdk/src/safeFactory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface DeploySafeProps {
safeAccountConfig: SafeAccountConfig
safeDeploymentConfig?: SafeDeploymentConfig
options?: TransactionOptions
callback?: (txHash: string) => void
}

export interface SafeFactoryConfig {
Expand Down Expand Up @@ -142,7 +143,8 @@ class SafeFactory {
async deploySafe({
safeAccountConfig,
safeDeploymentConfig,
options
options,
callback
}: DeploySafeProps): Promise<Safe> {
validateSafeAccountConfig(safeAccountConfig)
const signerAddress = await this.#ethAdapter.getSignerAddress()
Expand All @@ -160,7 +162,8 @@ class SafeFactory {
options: {
from: signerAddress,
...options
}
},
callback
})
const isContractDeployed = await this.#ethAdapter.isContractDeployed(safeAddress)
if (!isContractDeployed) {
Expand Down
24 changes: 24 additions & 0 deletions packages/safe-core-sdk/tests/safeFactory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,30 @@ describe('Safe Proxy Factory', () => {
chai.expect(deployedSafeThreshold).to.be.eq(threshold)
})

it('should deploy a new Safe with callback', async () => {
const { accounts, contractNetworks } = await setupTests()
const [account1, account2] = accounts
let callbackResult = ''
const callback = (txHash: string) => {
callbackResult = txHash
}
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 deploySafeProps: DeploySafeProps = { safeAccountConfig, callback }
chai.expect(callbackResult).to.be.empty
const safe = await safeFactory.deploySafe(deploySafeProps)
chai.expect(callbackResult).to.be.not.empty
const safeInstanceVersion = await safe.getContractVersion()
chai.expect(safeInstanceVersion).to.be.eq(safeVersionDeployed)
})

itif(safeVersionDeployed === SAFE_LAST_VERSION)(
'should deploy last Safe version by default',
async () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { BigNumber } from '@ethersproject/bignumber'
import { ContractTransaction } from '@ethersproject/contracts'
import {
BaseTransactionResult,
GnosisSafeContract,
SafeTransaction,
SafeTransactionData,
Expand All @@ -13,23 +11,8 @@ import {
GnosisSafe as GnosisSafe_V1_3_0,
GnosisSafeInterface
} from '../../../typechain/src/ethers-v5/v1.3.0/GnosisSafe'
import { EthersTransactionOptions } from '../../types'

export interface EthersTransactionResult extends BaseTransactionResult {
transactionResponse: ContractTransaction
options?: EthersTransactionOptions
}

function toTxResult(
transactionResponse: ContractTransaction,
options?: EthersTransactionOptions
): EthersTransactionResult {
return {
hash: transactionResponse.hash,
options,
transactionResponse
}
}
import { EthersTransactionOptions, EthersTransactionResult } from '../../types'
import { toTxResult } from '../../utils'

abstract class GnosisSafeContractEthers implements GnosisSafeContract {
constructor(public contract: GnosisSafe_V1_1_1 | GnosisSafe_V1_2_0 | GnosisSafe_V1_3_0) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface CreateProxyProps {
initializer: string
saltNonce: number
options?: EthersTransactionOptions
callback?: (txHash: string) => void
}

class GnosisSafeProxyFactoryEthersContract implements GnosisSafeProxyFactoryContract {
Expand All @@ -22,7 +23,8 @@ class GnosisSafeProxyFactoryEthersContract implements GnosisSafeProxyFactoryCont
safeMasterCopyAddress,
initializer,
saltNonce,
options
options,
callback
}: CreateProxyProps): Promise<string> {
if (saltNonce < 0) {
throw new Error('saltNonce must be greater than 0')
Expand All @@ -36,20 +38,22 @@ class GnosisSafeProxyFactoryEthersContract implements GnosisSafeProxyFactoryCont
}
)
}
const txResponse = await this.contract.createProxyWithNonce(
safeMasterCopyAddress,
initializer,
saltNonce,
options
)
const txReceipt = await txResponse.wait()
const proxyCreationEvent = txReceipt.events?.find(
({ event }: Event) => event === 'ProxyCreation'
)
if (!proxyCreationEvent || !proxyCreationEvent.args) {
throw new Error('Safe Proxy was not deployed correctly')
}
const proxyAddress: string = proxyCreationEvent.args[0]
const proxyAddress = this.contract
.createProxyWithNonce(safeMasterCopyAddress, initializer, saltNonce, options)
.then(async (txResponse) => {
if (callback) {
callback(txResponse.hash)
}
const txReceipt = await txResponse.wait()
const proxyCreationEvent = txReceipt?.events?.find(
({ event }: Event) => event === 'ProxyCreation'
)
if (!proxyCreationEvent || !proxyCreationEvent.args) {
throw new Error('Safe Proxy was not deployed correctly')
}
const proxyAddress: string = proxyCreationEvent.args[0]
return proxyAddress
})
return proxyAddress
}

Expand Down
3 changes: 1 addition & 2 deletions packages/safe-ethers-lib/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { EthersTransactionResult } from './contracts/GnosisSafe/GnosisSafeContractEthers'
import { CreateProxyProps } from './contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryEthersContract'
import EthersAdapter, { EthersAdapterConfig } from './EthersAdapter'
import { EthersTransactionOptions } from './types'
import { EthersTransactionOptions, EthersTransactionResult } from './types'

export default EthersAdapter
export { EthersAdapterConfig, EthersTransactionOptions, EthersTransactionResult, CreateProxyProps }
8 changes: 8 additions & 0 deletions packages/safe-ethers-lib/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { ContractTransaction } from '@ethersproject/contracts'
import { BaseTransactionResult } from '@gnosis.pm/safe-core-sdk-types'

export interface EthersTransactionOptions {
from?: string
gasLimit?: number | string
gasPrice?: number | string
}

export interface EthersTransactionResult extends BaseTransactionResult {
transactionResponse: ContractTransaction
options?: EthersTransactionOptions
}
14 changes: 14 additions & 0 deletions packages/safe-ethers-lib/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
import { ContractTransaction } from '@ethersproject/contracts'
import { EthersTransactionOptions, EthersTransactionResult } from '../types'

export function sameString(str1: string, str2: string): boolean {
return str1.toLowerCase() === str2.toLowerCase()
}

export function toTxResult(
transactionResponse: ContractTransaction,
options?: EthersTransactionOptions
): EthersTransactionResult {
return {
hash: transactionResponse.hash,
options,
transactionResponse
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,15 @@
import { BigNumber } from '@ethersproject/bignumber'
import {
BaseTransactionResult,
GnosisSafeContract,
SafeTransaction,
SafeTransactionData,
SafeVersion
} from '@gnosis.pm/safe-core-sdk-types'
import { PromiEvent, TransactionReceipt } from 'web3-core/types'
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 { Web3TransactionOptions } from '../../types'

export interface Web3TransactionResult extends BaseTransactionResult {
promiEvent: PromiEvent<TransactionReceipt>
options?: Web3TransactionOptions
}

function toTxResult(
promiEvent: PromiEvent<TransactionReceipt>,
options?: Web3TransactionOptions
): Promise<Web3TransactionResult> {
return new Promise((resolve, reject) =>
promiEvent
.once('transactionHash', (hash: string) => resolve({ hash, promiEvent, options }))
.catch(reject)
)
}
import { Web3TransactionOptions, Web3TransactionResult } from '../../types'
import { toTxResult } from '../../utils'

abstract class GnosisSafeContractWeb3 implements GnosisSafeContract {
constructor(public contract: GnosisSafe_V1_1_1 | GnosisSafe_V1_2_0 | GnosisSafe_V1_3_0) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { TransactionReceipt } from 'web3-core/types'
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 { Web3TransactionOptions } from '../../types'
import { toTxResult } from '../../utils'

export interface CreateProxyProps {
safeMasterCopyAddress: string
initializer: string
saltNonce: number
options?: Web3TransactionOptions
callback?: (txHash: string) => void
}

class GnosisSafeProxyFactoryWeb3Contract implements GnosisSafeProxyFactoryContract {
Expand All @@ -22,7 +24,8 @@ class GnosisSafeProxyFactoryWeb3Contract implements GnosisSafeProxyFactoryContra
safeMasterCopyAddress,
initializer,
saltNonce,
options
options,
callback
}: CreateProxyProps): Promise<string> {
if (saltNonce < 0) {
throw new Error('saltNonce must be greater than 0')
Expand All @@ -39,6 +42,12 @@ class GnosisSafeProxyFactoryWeb3Contract implements GnosisSafeProxyFactoryContra
const txResponse = this.contract.methods
.createProxyWithNonce(safeMasterCopyAddress, initializer, saltNonce)
.send(options)

if (callback) {
const txResult = await toTxResult(txResponse)
callback(txResult.hash)
}

const txResult: TransactionReceipt = await new Promise((resolve, reject) =>
txResponse.once('receipt', (receipt: TransactionReceipt) => resolve(receipt)).catch(reject)
)
Expand Down
3 changes: 1 addition & 2 deletions packages/safe-web3-lib/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Web3TransactionResult } from './contracts/GnosisSafe/GnosisSafeContractWeb3'
import { CreateProxyProps } from './contracts/GnosisSafeProxyFactory/GnosisSafeProxyFactoryWeb3Contract'
import { Web3TransactionOptions } from './types'
import { Web3TransactionOptions, Web3TransactionResult } from './types'
import Web3Adapter, { Web3AdapterConfig } from './Web3Adapter'

export default Web3Adapter
Expand Down
8 changes: 8 additions & 0 deletions packages/safe-web3-lib/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { BaseTransactionResult } from '@gnosis.pm/safe-core-sdk-types'
import { PromiEvent, TransactionReceipt } from 'web3-core/types'

export interface Web3TransactionOptions {
from?: string
gas?: number | string
gasPrice?: number | string
}

export interface Web3TransactionResult extends BaseTransactionResult {
promiEvent: PromiEvent<TransactionReceipt>
options?: Web3TransactionOptions
}
14 changes: 14 additions & 0 deletions packages/safe-web3-lib/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
import { PromiEvent, TransactionReceipt } from 'web3-core/types'
import { Web3TransactionOptions, Web3TransactionResult } from '../types'

export function sameString(str1: string, str2: string): boolean {
return str1.toLowerCase() === str2.toLowerCase()
}

export async function toTxResult(
promiEvent: PromiEvent<TransactionReceipt>,
options?: Web3TransactionOptions
): Promise<Web3TransactionResult> {
return new Promise((resolve, reject) =>
promiEvent
.once('transactionHash', (hash: string) => resolve({ hash, promiEvent, options }))
.catch(reject)
)
}