Skip to content

Commit

Permalink
Introduce baselineSnapshots into testnet client, rewrite tests to use…
Browse files Browse the repository at this point in the history
… them
  • Loading branch information
krzkaczor committed Dec 20, 2024
1 parent 1e110d0 commit 97d3995
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 98 deletions.
3 changes: 3 additions & 0 deletions packages/common-testnets/src/TestnetClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export interface TestnetClient extends WalletClient, PublicActions {
revert(snapshotId: string): Promise<string> // @note: returns new snapshot id (may be the same as the input)
mineBlocks(blocks: bigint): Promise<void>
setNextBlockTimestamp(timestamp: bigint): Promise<void>

baselineSnapshot(): Promise<void> // @note: baseline snapshot can be created only once, useful to easily revert to a known state. Helper on top of the snapshot method
revertToBaseline(): Promise<void> // @note: revert to the baseline snapshot, helper on top of the revert method
}

export function getUrlFromClient(client: TestnetClient): string {
Expand Down
114 changes: 66 additions & 48 deletions packages/common-testnets/src/anvil/AnvilClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,55 +13,73 @@ export function getAnvilClient(rpc: string): TestnetClient {
})
.extend(publicActions)
.extend(dealActions)
.extend((c) => ({
async setErc20Balance(tkn: Address, usr: Address, amt: bigint): Promise<void> {
return await c.deal({
erc20: tkn.toLowerCase() as any,
account: usr.toLowerCase() as any,
amount: amt,
})
},
async setBalance(usr: Address, amt: bigint): Promise<void> {
return c.request({
method: 'anvil_setBalance',
params: [usr.toString(), `0x${amt.toString(16)}`],
} as any)
},
async setStorageAt(addr: Address, slot: Hash, value: string) {
await c.request({
method: 'anvil_setStorageAt',
params: [addr.toString(), slot, value],
} as any)
},
async snapshot(): Promise<string> {
return c.request({
method: 'evm_snapshot',
params: [],
} as any)
},
async revert(snapshotId: string) {
const result = await c.request({
method: 'evm_revert',
params: [snapshotId],
} as any)
.extend((c) => {
let baselineSnapshotId: string | undefined

assert(result === true, 'revert failed')
const newClient = {
async setErc20Balance(tkn: Address, usr: Address, amt: bigint): Promise<void> {
return await c.deal({
erc20: tkn.toLowerCase() as any,
account: usr.toLowerCase() as any,
amount: amt,
})
},
async setBalance(usr: Address, amt: bigint): Promise<void> {
return c.request({
method: 'anvil_setBalance',
params: [usr.toString(), `0x${amt.toString(16)}`],
} as any)
},
async setStorageAt(addr: Address, slot: Hash, value: string) {
await c.request({
method: 'anvil_setStorageAt',
params: [addr.toString(), slot, value],
} as any)
},
async snapshot(): Promise<string> {
return c.request({
method: 'evm_snapshot',
params: [],
} as any)
},
async revert(snapshotId: string) {
const result = await c.request({
method: 'evm_revert',
params: [snapshotId],
} as any)

// anvil snapshots are "burned" after revert so we need to create a new one
return await c.snapshot()
},
async mineBlocks(blocks: bigint) {
await c.request({
method: 'anvil_mine',
params: [numberToHex(blocks), '0x1'],
})
},
async setNextBlockTimestamp(timestamp: bigint) {
await c.request({
method: 'evm_setNextBlockTimestamp',
params: [numberToHex(timestamp)],
})
},
}))
assert(result === true, 'revert failed')

// anvil snapshots are "burned" after revert so we need to create a new one
return await c.snapshot()
},
async mineBlocks(blocks: bigint) {
await c.request({
method: 'anvil_mine',
params: [numberToHex(blocks), '0x1'],
})
},
async setNextBlockTimestamp(timestamp: bigint) {
await c.request({
method: 'evm_setNextBlockTimestamp',
params: [numberToHex(timestamp)],
})
},
}

return {
...newClient,
async baselineSnapshot() {
assert(baselineSnapshotId === undefined, 'baseline snapshot already created')

baselineSnapshotId = await newClient.snapshot()
},
async revertToBaseline() {
assert(baselineSnapshotId !== undefined, 'baseline snapshot not created')

baselineSnapshotId = await newClient.revert(baselineSnapshotId)
},
}
})
.extend(walletActions)
}
9 changes: 6 additions & 3 deletions packages/common-testnets/src/helpers/replaceSafeOwner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,21 @@ describe(replaceSafeOwner.name, () => {
let testnetClient: TestnetClient
let cleanup: () => Promise<void>

beforeEach(async () => {
before(async () => {
;({ client: testnetClient, cleanup } = await factory.create({
id: 'test',
originChainId: 1,
forkChainId: 1,
blockNumber: 21439277n,
}))
await testnetClient.baselineSnapshot()
})

afterEach(async () => {
after(async () => {
await cleanup()
})
beforeEach(async () => {
await testnetClient.revertToBaseline()
})

it('replaces first owner', async () => {
const previousOwners = await getSafeOwners({
Expand Down
1 change: 1 addition & 0 deletions packages/common-testnets/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './TestnetClient'
export * from './TestnetFactory'
export * from './tenderly'
export * from './anvil'
export * from './helpers'
112 changes: 65 additions & 47 deletions packages/common-testnets/src/tenderly/TenderlyClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Hash } from '@marsfoundation/common-universal'
import { assert, Hash } from '@marsfoundation/common-universal'
import { http, Address, createTestClient, numberToHex, publicActions, walletActions } from 'viem'
import { mainnet } from 'viem/chains'
import { TestnetClient } from '../TestnetClient'
Expand All @@ -10,52 +10,70 @@ export function getTenderlyClient(rpc: string): TestnetClient {
transport: http(rpc),
cacheTime: 0, // do not cache block numbers
})
.extend((c) => ({
async setErc20Balance(tkn: Address, usr: Address, amt: bigint): Promise<void> {
return c.request({
method: 'tenderly_setErc20Balance',
params: [tkn.toString(), usr.toString(), numberToHex(amt)],
} as any)
},
async setBalance(usr: Address, amt: bigint): Promise<void> {
return c.request({
method: 'tenderly_setBalance',
params: [usr.toString(), numberToHex(amt)],
} as any)
},
async setStorageAt(addr: Address, slot: Hash, value: string) {
await c.request({
method: 'tenderly_setStorageAt',
params: [addr.toString(), slot, value],
} as any)
},
async snapshot(): Promise<string> {
return c.request({
method: 'evm_snapshot',
params: [],
} as any)
},
async revert(snapshotId: string) {
await c.request({
method: 'evm_revert',
params: [snapshotId],
} as any)
// tenderly doesn't burn the snapshots id so we can reuse it
return snapshotId
},
async mineBlocks(blocks: bigint) {
await c.request({
method: 'evm_increaseBlocks',
params: [numberToHex(blocks)],
} as any)
},
async setNextBlockTimestamp(timestamp: bigint) {
await c.request({
method: 'tenderly_setNextBlockTimestamp',
params: [numberToHex(timestamp)],
} as any)
},
}))
.extend((c) => {
let baselineSnapshotId: string | undefined

const newClient = {
async setErc20Balance(tkn: Address, usr: Address, amt: bigint): Promise<void> {
return c.request({
method: 'tenderly_setErc20Balance',
params: [tkn.toString(), usr.toString(), numberToHex(amt)],
} as any)
},
async setBalance(usr: Address, amt: bigint): Promise<void> {
return c.request({
method: 'tenderly_setBalance',
params: [usr.toString(), numberToHex(amt)],
} as any)
},
async setStorageAt(addr: Address, slot: Hash, value: string) {
await c.request({
method: 'tenderly_setStorageAt',
params: [addr.toString(), slot, value],
} as any)
},
async snapshot(): Promise<string> {
return c.request({
method: 'evm_snapshot',
params: [],
} as any)
},
async revert(snapshotId: string) {
await c.request({
method: 'evm_revert',
params: [snapshotId],
} as any)
// tenderly doesn't burn the snapshots id so we can reuse it
return snapshotId
},
async mineBlocks(blocks: bigint) {
await c.request({
method: 'evm_increaseBlocks',
params: [numberToHex(blocks)],
} as any)
},
async setNextBlockTimestamp(timestamp: bigint) {
await c.request({
method: 'tenderly_setNextBlockTimestamp',
params: [numberToHex(timestamp)],
} as any)
},
}

return {
...newClient,
async baselineSnapshot() {
assert(baselineSnapshotId === undefined, 'baseline snapshot already created')

baselineSnapshotId = await newClient.snapshot()
},
async revertToBaseline() {
assert(baselineSnapshotId !== undefined, 'baseline snapshot not created')

baselineSnapshotId = await newClient.revert(baselineSnapshotId)
},
}
})
.extend(walletActions)
.extend(publicActions)
}

0 comments on commit 97d3995

Please sign in to comment.