Skip to content

Commit

Permalink
Added unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniSomoza committed Aug 10, 2023
1 parent 9a51b1b commit 33cc417
Show file tree
Hide file tree
Showing 9 changed files with 717 additions and 17 deletions.
4 changes: 2 additions & 2 deletions packages/protocol-kit/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ import { encodeMultiSendData, standardizeSafeTransactionData } from './utils/tra
import {
getERC20Decimals,
isGasTokenCompatibleWithHandlePayment,
createERC20tokenTransferTransaction
createERC20TokenTransferTransaction
} from './utils/erc-20'

export {
Expand All @@ -88,7 +88,7 @@ export {
ContractNetworksConfig,
CreateCallEthersContract,
CreateCallWeb3Contract,
createERC20tokenTransferTransaction,
createERC20TokenTransferTransaction,
CreateEthersProxyProps,
CreateTransactionProps,
CreateWeb3ProxyProps,
Expand Down
11 changes: 8 additions & 3 deletions packages/protocol-kit/src/utils/erc-20/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const ERC20_ABI = [
* @param {string} tokenAddress - The address of the ERC-20 token.
* @param {Safe} safe - The Safe object.
* @returns {Promise<number>} The number of decimals that the token uses.
* @throws "Invalid ERC-20 decimals"
*/
export async function getERC20Decimals(tokenAddress: string, safe: Safe): Promise<number> {
const ethAdapter = safe.getEthAdapter()
Expand All @@ -32,6 +33,10 @@ export async function getERC20Decimals(tokenAddress: string, safe: Safe): Promis

const decimals = Number(response)

if (Number.isNaN(decimals)) {
throw new Error('Invalid ERC-20 decimals')
}

return decimals
}

Expand Down Expand Up @@ -59,9 +64,9 @@ export async function isGasTokenCompatibleWithHandlePayment(

// Only ERC20 tokens with the standard 18 decimals are compatible
const gasTokenDecimals = await getERC20Decimals(gasToken, safe)
const isStandardERC20Token = gasTokenDecimals === STANDARD_ERC20_DECIMALS
const hasTokenStandardERC20Decimals = gasTokenDecimals === STANDARD_ERC20_DECIMALS

return isStandardERC20Token
return hasTokenStandardERC20Decimals
}

/**
Expand All @@ -73,7 +78,7 @@ export async function isGasTokenCompatibleWithHandlePayment(
* @param {string} amount - The amount of tokens to transfer.
* @returns {Transaction} Returns a transaction object that represents the transfer.
*/
export function createERC20tokenTransferTransaction(
export function createERC20TokenTransferTransaction(
tokenAddress: string,
toAddress: string,
amount: string
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import chai from 'chai'
import chaiAsPromised from 'chai-as-promised'
import { deployments, waffle } from 'hardhat'
import { safeVersionDeployed } from '@safe-global/protocol-kit/hardhat/deploy/deploy-contracts'
import Safe, {
PREDETERMINED_SALT_NONCE,
PredictedSafeProps,
encodeSetupCallData
} from '@safe-global/protocol-kit/index'
import { getContractNetworks } from './utils/setupContractNetworks'
import { getSafeWithOwners, getFactory } from './utils/setupContracts'
import { getEthAdapter } from './utils/setupEthAdapter'
import { getAccounts } from './utils/setupTestNetwork'

chai.use(chaiAsPromised)

describe('createSafeDeploymentTransaction', () => {
const setupTests = deployments.createFixture(async ({ deployments }) => {
await deployments.fixture()
const accounts = await getAccounts()
const chainId: number = (await waffle.provider.getNetwork()).chainId
const contractNetworks = await getContractNetworks(chainId)

const predictedSafe: PredictedSafeProps = {
safeAccountConfig: {
owners: [accounts[0].address],
threshold: 1
},
safeDeploymentConfig: {
safeVersion: safeVersionDeployed
}
}

return {
accounts,
contractNetworks,
predictedSafe,
chainId
}
})

it('should return a Safe deployment transactions', async () => {
const { accounts, contractNetworks, predictedSafe } = await setupTests()
const [account1] = accounts

const ethAdapter = await getEthAdapter(account1.signer)

const safeSdk = await Safe.create({
ethAdapter,
predictedSafe,
contractNetworks
})

const deploymentTransaction = await safeSdk.createSafeDeploymentTransaction()

const safeFactoryAddress = await (await getFactory()).contract.address

chai.expect(deploymentTransaction).to.be.deep.equal({
to: safeFactoryAddress,
value: '0',
// safe deployment data (createProxyWithNonce)
data: '0x1688f0b90000000000000000000000008e6332da7ccd5430bfb27df39fbf386b463c31a50000000000000000000000000000000000000000000000000000000000000060b1073742015cbcf5a3a4d9d1ae33ecf619439710b89475f92e2abd2117e90f900000000000000000000000000000000000000000000000000000000000000164b63e800d000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000085692cd6f0b50e6d48b98153cba504a09564e776000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000090f8bf6a479f320ead074411a4b0e7944ea8c9c1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
})
})

it('should contain the initializer setup call in the deployment data to sets the threshold & owners of the deployed Safe', async () => {
const { accounts, contractNetworks, predictedSafe, chainId } = await setupTests()
const [account1] = accounts

const ethAdapter = await getEthAdapter(account1.signer)

const safeSdk = await Safe.create({
ethAdapter,
predictedSafe,
contractNetworks
})

const deploymentTransaction = await safeSdk.createSafeDeploymentTransaction()

const customContract = contractNetworks[chainId]
const safeContract = await ethAdapter.getSafeContract({
safeVersion: safeVersionDeployed,
customContractAddress: customContract?.safeMasterCopyAddress,
customContractAbi: customContract?.safeMasterCopyAbi
})

// this is the call to the setup method that sets the threshold & owners of the new Safe
const initializer = await encodeSetupCallData({
ethAdapter,
safeContract,
safeAccountConfig: predictedSafe.safeAccountConfig,
customContracts: contractNetworks[chainId]
})

// should contain the initializer setup call in the deployment data
chai.expect(deploymentTransaction.data).to.contains(initializer.replace('0x', ''))
})

describe('salt nonce', () => {
it('should include the predetermined salt nonce in the Safe deployment data', async () => {
const { accounts, contractNetworks, predictedSafe } = await setupTests()
const [account1] = accounts

const ethAdapter = await getEthAdapter(account1.signer)

const safeSdk = await Safe.create({
ethAdapter,
predictedSafe,
contractNetworks
})

const predeterminedSaltNonceEncoded = ethAdapter.encodeParameters(
['uint256'],
[PREDETERMINED_SALT_NONCE]
)

const deploymentTransaction = await safeSdk.createSafeDeploymentTransaction()

// predetermined salt nonce included in the deployment data
chai
.expect(deploymentTransaction.data)
.to.contains(predeterminedSaltNonceEncoded.replace('0x', ''))
})

it('should include the custom salt nonce in the Safe deployment data', async () => {
const { accounts, contractNetworks, predictedSafe } = await setupTests()
const [account1] = accounts

const ethAdapter = await getEthAdapter(account1.signer)

const safeSdk = await Safe.create({
ethAdapter,
predictedSafe,
contractNetworks
})

const customSaltNonce = '123456789'

const customSaltNonceEncoded = ethAdapter.encodeParameters(['uint256'], [customSaltNonce])

const deploymentTransaction = await safeSdk.createSafeDeploymentTransaction(customSaltNonce)

// custom salt nonce included in the deployment data
chai.expect(deploymentTransaction.data).to.contains(customSaltNonceEncoded.replace('0x', ''))
})

it('should include the salt nonce included in the safeDeploymentConfig in the Safe deployment data', async () => {
const { accounts, contractNetworks, predictedSafe } = await setupTests()
const [account1] = accounts

const ethAdapter = await getEthAdapter(account1.signer)

const customSaltNonce = '123456789'

const safeSdk = await Safe.create({
ethAdapter,
predictedSafe: {
...predictedSafe,
safeDeploymentConfig: {
...predictedSafe.safeDeploymentConfig,
saltNonce: customSaltNonce
}
},
contractNetworks
})

const saltNonceEncoded = ethAdapter.encodeParameters(['uint256'], [customSaltNonce])

const deploymentTransaction = await safeSdk.createSafeDeploymentTransaction(customSaltNonce)

// custom salt nonce included in the deployment data
chai.expect(deploymentTransaction.data).to.contains(saltNonceEncoded.replace('0x', ''))
})
})

it('should throw an error if predicted Safe is not present', async () => {
const { accounts, contractNetworks } = await setupTests()
const [account1] = accounts

const safe = await getSafeWithOwners([account1.address])
const ethAdapter = await getEthAdapter(account1.signer)

const safeSdk = await Safe.create({
ethAdapter,
safeAddress: safe.address,
contractNetworks
})

await chai
.expect(safeSdk.createSafeDeploymentTransaction())
.to.be.rejectedWith('Predict Safe should be present')
})
})
78 changes: 78 additions & 0 deletions packages/protocol-kit/tests/e2e/createTransactionBatch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { MetaTransactionData } from '@safe-global/safe-core-sdk-types'
import chai from 'chai'
import chaiAsPromised from 'chai-as-promised'
import { deployments, waffle } from 'hardhat'
import { safeVersionDeployed } from '@safe-global/protocol-kit/hardhat/deploy/deploy-contracts'
import Safe, { PredictedSafeProps } from '@safe-global/protocol-kit/index'
import { getContractNetworks } from './utils/setupContractNetworks'
import { getERC20Mintable, getSafeWithOwners, getMultiSendCallOnly } from './utils/setupContracts'
import { getEthAdapter } from './utils/setupEthAdapter'
import { getAccounts } from './utils/setupTestNetwork'
import { OperationType } from 'packages/safe-core-sdk-types/dist/src'

chai.use(chaiAsPromised)

const AMOUNT_TO_TRANSFER = '500000000000000000' // 0.5 ETH

describe('createTransactionBatch', () => {
const setupTests = deployments.createFixture(async ({ deployments }) => {
await deployments.fixture()
const accounts = await getAccounts()
const chainId: number = (await waffle.provider.getNetwork()).chainId
const contractNetworks = await getContractNetworks(chainId)

const predictedSafe: PredictedSafeProps = {
safeAccountConfig: {
owners: [accounts[0].address],
threshold: 1
},
safeDeploymentConfig: {
safeVersion: safeVersionDeployed
}
}

return {
erc20Mintable: await getERC20Mintable(),
accounts,
contractNetworks,
predictedSafe,
chainId
}
})

it('should return a batch of the provided transactions', async () => {
const { accounts, contractNetworks, erc20Mintable } = await setupTests()
const [account1, account2] = accounts

const safe = await getSafeWithOwners([account1.address])
const ethAdapter = await getEthAdapter(account1.signer)

const safeSdk = await Safe.create({
ethAdapter,
safeAddress: safe.address,
contractNetworks
})

const dumpTransfer = {
to: erc20Mintable.address,
value: '0',
data: erc20Mintable.interface.encodeFunctionData('transfer', [
account2.address,
AMOUNT_TO_TRANSFER
]),
operation: OperationType.Call
}

const transactions: MetaTransactionData[] = [dumpTransfer, dumpTransfer]

const batchTransaction = await safeSdk.createTransactionBatch(transactions)

const multiSendContractAddress = await (await getMultiSendCallOnly()).contract.address

chai.expect(batchTransaction).to.be.deep.equal({
to: multiSendContractAddress,
value: '0',
data: '0x8d80ff0a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001320067b5656d60a809915323bf2c40a8bef15a152e3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000ffcf8fdee72ac11b5c542428b35eef5769c409f000000000000000000000000000000000000000000000000006f05b59d3b200000067b5656d60a809915323bf2c40a8bef15a152e3e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044a9059cbb000000000000000000000000ffcf8fdee72ac11b5c542428b35eef5769c409f000000000000000000000000000000000000000000000000006f05b59d3b200000000000000000000000000000000'
})
})
})
Loading

0 comments on commit 33cc417

Please sign in to comment.