accounts
Version:
Tempo Accounts SDK
95 lines (83 loc) • 3.35 kB
text/typescript
import { getTransactionReceipt, prepareTransactionRequest, signTransaction } from 'viem/actions'
import { Account } from 'viem/tempo'
import { afterAll, beforeAll, describe, expect, test } from 'vp/test'
import { accounts, chain, getClient, http } from '../../../../test/config.js'
import { createServer, type Server } from '../../../../test/utils.js'
import * as Multisig from './multisig.js'
import { relay } from './relay.js'
const owner_1 = accounts[1]!
const owner_2 = accounts[2]!
const feePayer = accounts[0]!
const feeToken = '0x20c0000000000000000000000000000000000000' as const
// Requires a multisig-capable node image (updated TIP-1061 wire format),
// e.g. VITE_NODE_TAG=sha-8968664. Skipped otherwise.
const tag = import.meta.env.VITE_NODE_TAG || ''
describe.skipIf(!tag.startsWith('sha-'))('relay multisig', () => {
const store = Multisig.memoryStore()
let client: ReturnType<typeof getClient<typeof chain>>
let server: Server
beforeAll(async () => {
server = await createServer(
relay({
chains: [chain],
feePayer: { account: feePayer, feeToken },
multisig: { store },
transports: { [chain.id]: http() },
}).listener,
)
client = getClient({ transport: http(server.url) })
})
afterAll(() => {
server.close()
})
test('behavior: collects approvals and resolves operation-id receipt aliases on localnet', async () => {
const account = Account.fromMultisig({
threshold: 2,
owners: [
{ owner: owner_1.address, weight: 1 },
{ owner: owner_2.address, weight: 1 },
],
})
const transaction = await prepareTransactionRequest(client, {
account,
feePayer: true,
to: account.address,
value: 0n,
})
const [signature_1, signature_2] = await Promise.all(
[owner_1, owner_2].map((owner) =>
signTransaction(client, { ...transaction, account: owner } as never),
),
)
// `feePayerSignature: null` requests relay sponsorship at broadcast; the
// fill-time fee payer signature is stale once the multisig signature is
// attached.
const serialize = (signature: unknown) =>
account.signTransaction({
...transaction,
feePayerSignature: null,
signatures: [signature],
} as never)
// Below-quorum submission stores the approval and returns the operation id.
const pending = (await client.request({
method: 'eth_sendRawTransactionSync',
params: [await serialize(signature_1)],
} as never)) as `0x${string}`
expect(pending).toMatch(/^0x[0-9a-f]{64}$/)
await expect(
client.request({ method: 'eth_getTransactionReceipt', params: [pending] }),
).resolves.toBeNull()
// Quorum reached: the relay merges the stored approval and broadcasts.
const receipt = (await client.request({
method: 'eth_sendRawTransactionSync',
params: [await serialize(signature_2)],
} as never)) as { status: string; transactionHash: `0x${string}` }
const hash = receipt.transactionHash
if (!hash) throw new Error('Expected multisig transaction hash.')
expect(hash === pending).toBe(false)
expect(receipt).toMatchObject({ status: '0x1' })
await expect(getTransactionReceipt(client, { hash: pending })).resolves.toMatchObject({
transactionHash: hash,
})
})
})