Skip to content

Commit

Permalink
Add abitype
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniSomoza committed Nov 24, 2023
1 parent 2e3a550 commit 40d4f76
Show file tree
Hide file tree
Showing 15 changed files with 3,114 additions and 254 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"tsc-alias": "^1.8.8",
"typescript": "^4.9.5"
"typescript": "^5.3.2"
},
"lint-staged": {
"./packages/**/*.{js,jsx,ts,tsx}": [
Expand Down
2 changes: 1 addition & 1 deletion packages/onramp-kit/example/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"@types/react-dom": "^18.2.14",
"@vitejs/plugin-react": "^3.1.0",
"rollup-plugin-polyfill-node": "^0.12.0",
"typescript": "^4.9.5",
"typescript": "^5.3.2",
"vite": "^4.5.0"
}
}
2 changes: 1 addition & 1 deletion packages/onramp-kit/example/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@
"@types/node": "^18.18.8",
"nodemon": "^2.0.22",
"ts-node": "^10.9.1",
"typescript": "^4.9.5"
"typescript": "^5.3.2"
}
}
4 changes: 3 additions & 1 deletion packages/protocol-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"@types/semver": "^7.5.4",
"@types/web3": "1.0.20",
"@types/yargs": "^16.0.7",
"abitype": "^0.10.2",
"chai": "^4.3.10",
"chai-as-promised": "^7.1.1",
"coveralls": "^3.1.1",
Expand All @@ -79,7 +80,8 @@
"ts-generator": "^0.1.1",
"tsconfig-paths": "^4.2.0",
"typechain": "^8.3.2",
"yargs": "^17.7.2"
"yargs": "^17.7.2",
"@truffle/hdwallet-provider": "2.1.15"
},
"dependencies": {
"@noble/hashes": "^1.3.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import { Contract } from 'ethers'
import SafeBaseContractEthersv6 from '@safe-global/protocol-kit/adapters/ethers-v6/contracts/SafeBaseContractEthersv6'
import EthersAdapter from '@safe-global/protocol-kit/adapters/ethers/EthersAdapter'
import {
EthersTransactionOptions,
EthersTransactionResult
} from '@safe-global/protocol-kit/adapters/ethers/types'
import SafeContract_v1_3_0_Contract, {
EncodeSafeFunction,
EstimateSafeFunction,
SafeContract_v1_3_0_Abi,
Safe_v1_3_0_Write_Functions
} from '@safe-global/protocol-kit/contracts/AbiType/Safe/SafeContract_v1_3_0'
import { SafeTransaction } from 'packages/safe-core-sdk-types'
import { toTxResult } from '@safe-global/protocol-kit/adapters/ethers/utils'
import safe_1_3_0_ContractArtifacts from '@safe-global/protocol-kit/contracts/AbiType/assets/Safe/v1.3.0/gnosis_safe_l2'
import { SENTINEL_ADDRESS } from '@safe-global/protocol-kit/adapters/ethers/utils/constants'

// TODO: add docs (see safe.sol methods)
// TODO: create address type?

class SafeContract_v1_3_0_Ethers
extends SafeBaseContractEthersv6<SafeContract_v1_3_0_Abi>
implements SafeContract_v1_3_0_Contract
{
contract: Contract
adapter: EthersAdapter
// TODO: define contractVersion (only for safe contract?)
// TODO: define contractName
// TODO: contract version detection based on the address ???

constructor(
ethersAdapter: EthersAdapter,
chainId: bigint,
// TODO: create safeAddress ???
// TODO: create customContractAddress ???
customAddress?: string, // returns the Safe Singleton instance if is empty
customAbi?: SafeContract_v1_3_0_Abi,
isL1SafeSingleton = false
) {
super(chainId, '1.3.0', customAddress, customAbi, isL1SafeSingleton)

// if no customAbi and no abi is present in the safe-deployments we our the hardcoded abi
this.contractAbi = this.contractAbi || safe_1_3_0_ContractArtifacts.abi

this.adapter = ethersAdapter
this.contract = new Contract(this.contractAddress, this.contractAbi, ethersAdapter.getSigner())
}

// TODO: move this to SafeBaseContractEthersv6 ???
encode: EncodeSafeFunction<Safe_v1_3_0_Write_Functions> = (functionToEncode, args) => {
return this.contract.interface.encodeFunctionData(functionToEncode, args)
}

// TODO: move this to SafeBaseContractEthersv6 ???
estimateGas: EstimateSafeFunction<Safe_v1_3_0_Write_Functions> = (
functionToEstimate,
args,
options = {}
) => {
const contractMethodToStimate = this.contract.getFunction(functionToEstimate)

return contractMethodToStimate.estimateGas(...args, options)
}

async VERSION(): Promise<[string]> {
return [await this.contract.VERSION()]
}

async approvedHashes([owner, txHash]: readonly [string, string]): Promise<[bigint]> {
return [await this.contract.approvedHashes(owner, txHash)]
}

// TODO: rename the args
async checkNSignatures(args: readonly [string, string, string, bigint]): Promise<[]> {
// this method just checks whether the signature provided is valid for the provided data and hash. Reverts otherwise.
await this.contract.checkNSignatures(...args)
return []
}

// TODO: rename the args
async checkSignatures(args: readonly [string, string, string]): Promise<[]> {
await this.contract.checkSignatures(...args)
return []
}

async domainSeparator(): Promise<[string]> {
return [await this.contract.domainSeparator()]
}

// TODO: rename the args
async encodeTransactionData(
args: readonly [string, bigint, string, number, bigint, bigint, bigint, string, string, bigint]
): Promise<[string]> {
return [await this.contract.encodeTransactionData(...args)]
}

async getChainId(): Promise<[bigint]> {
return [await this.contract.getChainId()]
}

// TODO: rename the args
getModulesPaginated(
args: readonly [start: string, pageSize: bigint]
): Promise<[modules: string[], next: string]> {
return this.contract.getModulesPaginated(...args)
}

async getOwners(): Promise<readonly [string[]]> {
return [await this.contract.getOwners()]
}

// TODO: rename the args
async getStorageAt(args: readonly [bigint, bigint]): Promise<[string]> {
return [await this.contract.getStorageAt(...args)]
}

async getThreshold(): Promise<[bigint]> {
return [await this.contract.getThreshold()]
}

// TODO: rename the args
async getTransactionHash(
args: readonly [string, bigint, string, number, bigint, bigint, bigint, string, string, bigint]
): Promise<[string]> {
return [await this.contract.getTransactionHash(...args)]
}

// TODO: rename the args
async isModuleEnabled(args: readonly [string]): Promise<[boolean]> {
return [await this.contract.isModuleEnabled(...args)]
}

// TODO: rename the args
async isOwner(args: readonly [string]): Promise<[boolean]> {
return [await this.contract.isOwner(...args)]
}

async nonce(): Promise<[bigint]> {
return [await this.contract.nonce()]
}

// TODO: rename the args
async signedMessages(args: readonly [string]): Promise<[bigint]> {
return [await this.contract.signedMessages(...args)]
}

// custom methods (not defined in the Safe Contract)
async isValidTransaction(
safeTransaction: SafeTransaction,
options: EthersTransactionOptions = {}
) {
try {
const gasLimit =
options?.gasLimit ||
(await this.estimateGas(
'execTransaction',
[
safeTransaction.data.to,
BigInt(safeTransaction.data.value),
safeTransaction.data.data,
safeTransaction.data.operation,
BigInt(safeTransaction.data.safeTxGas),
BigInt(safeTransaction.data.baseGas),
BigInt(safeTransaction.data.gasPrice),
safeTransaction.data.gasToken,
safeTransaction.data.refundReceiver,
safeTransaction.encodedSignatures()
],
options
))

return await this.contract.execTransaction.staticCall(
safeTransaction.data.to,
BigInt(safeTransaction.data.value),
safeTransaction.data.data,
safeTransaction.data.operation,
BigInt(safeTransaction.data.safeTxGas),
BigInt(safeTransaction.data.baseGas),
BigInt(safeTransaction.data.gasPrice),
safeTransaction.data.gasToken,
safeTransaction.data.refundReceiver,
safeTransaction.encodedSignatures(),
{ ...options, gasLimit }
)
} catch (error) {
return false
}
}

async execTransaction(
safeTransaction: SafeTransaction,
options?: EthersTransactionOptions
): Promise<EthersTransactionResult> {
const gasLimit =
options?.gasLimit ||
(await this.estimateGas(
'execTransaction',
[
safeTransaction.data.to,
BigInt(safeTransaction.data.value),
safeTransaction.data.data,
safeTransaction.data.operation,
BigInt(safeTransaction.data.safeTxGas),
BigInt(safeTransaction.data.baseGas),
BigInt(safeTransaction.data.gasPrice),
safeTransaction.data.gasToken,
safeTransaction.data.refundReceiver,
safeTransaction.encodedSignatures()
],
options
))

const txResponse = await this.contract.execTransaction(
safeTransaction.data.to,
safeTransaction.data.value,
safeTransaction.data.data,
safeTransaction.data.operation,
safeTransaction.data.safeTxGas,
safeTransaction.data.baseGas,
safeTransaction.data.gasPrice,
safeTransaction.data.gasToken,
safeTransaction.data.refundReceiver,
safeTransaction.encodedSignatures(),
{ ...options, gasLimit }
)

return toTxResult(txResponse, options)
}

// TODO: review this custom method
async getModules(): Promise<string[]> {
const [modules] = await this.contract.getModulesPaginated(SENTINEL_ADDRESS, 10)
return modules
}
}

export default SafeContract_v1_3_0_Ethers
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Contract } from 'ethers'

import { SafeVersion } from '@safe-global/safe-core-sdk-types'
import SafeBaseContract from '@safe-global/protocol-kit/contracts/AbiType/Safe/SafeBaseContract'
import EthersAdapter from '../../ethers/EthersAdapter'
import { contractName, safeDeploymentsL1ChainIds } from '@safe-global/protocol-kit/contracts/config'

// TODO: add docs
abstract class SafeBaseContractEthersv6<AbiType> extends SafeBaseContract<AbiType> {
abstract contract: Contract
abstract adapter: EthersAdapter

constructor(
chainId: bigint,
safeVersion: SafeVersion,
customContractAddress?: string,
customContractAbi?: AbiType,
isL1SafeSingleton = false
) {
const isL1Contract = safeDeploymentsL1ChainIds.includes(chainId) || isL1SafeSingleton

const contractName: contractName = isL1Contract
? 'safeSingletonVersion'
: 'safeSingletonL2Version'

super(chainId, safeVersion, contractName, customContractAddress, customContractAbi)
}
}

export default SafeBaseContractEthersv6
13 changes: 11 additions & 2 deletions packages/protocol-kit/src/adapters/ethers/EthersAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ class EthersAdapter implements EthAdapter {
async getSafeContract({
safeVersion,
singletonDeployment,
customContractAddress
customContractAddress,
customContractAbi,
isL1SafeSingleton
}: GetContractProps): Promise<SafeContractEthers> {
const chainId = await this.getChainId()
const contractAddress =
Expand All @@ -104,7 +106,14 @@ class EthersAdapter implements EthAdapter {
throw new Error('Invalid SafeProxy contract address')
}
const signerOrProvider = this.#signer || this.#provider
return getSafeContractInstance(safeVersion, contractAddress, signerOrProvider)
return getSafeContractInstance(
safeVersion,
contractAddress,
signerOrProvider,
this,
customContractAbi,
isL1SafeSingleton
)
}

async getSafeProxyFactoryContract({
Expand Down
Loading

0 comments on commit 40d4f76

Please sign in to comment.