From df47f463cd13fec97f1211348382be2b530a8c1e Mon Sep 17 00:00:00 2001 From: Steven Truong Date: Thu, 6 Aug 2026 12:21:06 -0400 Subject: [PATCH] feat(tempo): added withdrawal sender tag utility --- .changeset/clean-zones-withdraw.md | 5 ++ src/tempo/WithdrawalSenderTag.test-d.ts | 14 ++++++ src/tempo/WithdrawalSenderTag.test.ts | 30 +++++++++++ src/tempo/WithdrawalSenderTag.ts | 66 +++++++++++++++++++++++++ src/tempo/index.ts | 24 +++++++++ 5 files changed, 139 insertions(+) create mode 100644 .changeset/clean-zones-withdraw.md create mode 100644 src/tempo/WithdrawalSenderTag.test-d.ts create mode 100644 src/tempo/WithdrawalSenderTag.test.ts create mode 100644 src/tempo/WithdrawalSenderTag.ts diff --git a/.changeset/clean-zones-withdraw.md b/.changeset/clean-zones-withdraw.md new file mode 100644 index 00000000..72b8e2d4 --- /dev/null +++ b/.changeset/clean-zones-withdraw.md @@ -0,0 +1,5 @@ +--- +'ox': patch +--- + +Added `WithdrawalSenderTag` utilities for deriving nonce-bound Tempo Zone withdrawal sender tags. diff --git a/src/tempo/WithdrawalSenderTag.test-d.ts b/src/tempo/WithdrawalSenderTag.test-d.ts new file mode 100644 index 00000000..d7481b84 --- /dev/null +++ b/src/tempo/WithdrawalSenderTag.test-d.ts @@ -0,0 +1,14 @@ +import type { Hex } from 'ox' +import { WithdrawalSenderTag } from 'ox/tempo' +import { expectTypeOf, test } from 'vitest' + +test('from', () => { + expectTypeOf( + WithdrawalSenderTag.from({ + fallbackNonce: 19n, + sender: '0x1234567890abcdef1234567890abcdef12345678', + transactionHash: + '0xabababababababababababababababababababababababababababababababab', + }), + ).toEqualTypeOf() +}) diff --git a/src/tempo/WithdrawalSenderTag.test.ts b/src/tempo/WithdrawalSenderTag.test.ts new file mode 100644 index 00000000..ed443050 --- /dev/null +++ b/src/tempo/WithdrawalSenderTag.test.ts @@ -0,0 +1,30 @@ +import { describe, expect, test } from 'vitest' +import * as WithdrawalSenderTag from './WithdrawalSenderTag.js' + +describe('from', () => { + test('derives the production sender tag', () => { + expect( + WithdrawalSenderTag.from({ + fallbackNonce: 19n, + sender: '0x0F0896dbf0465E5c07963301dcFEA1101Fa91EaC', + transactionHash: + '0xae628bdc4bd24a9f9a917825a208baa16c384ab8a96a40cd5146bd20d9b3f6d9', + }), + ).toMatchInlineSnapshot( + `"0xf1acbae45cd689281144042331e3379cf631a8d2db83057ccf38754a0b0108f2"`, + ) + }) + + test('derives the canonical internal deposit bounce-back tag', () => { + expect( + WithdrawalSenderTag.from({ + fallbackNonce: 0n, + sender: '0x0000000000000000000000000000000000000000', + transactionHash: + '0x0000000000000000000000000000000000000000000000000000000000000000', + }), + ).toMatchInlineSnapshot( + `"0xa86d54e9aab41ae5e520ff0062ff1b4cbd0b2192bb01080a058bb170d84e6457"`, + ) + }) +}) diff --git a/src/tempo/WithdrawalSenderTag.ts b/src/tempo/WithdrawalSenderTag.ts new file mode 100644 index 00000000..47ae183e --- /dev/null +++ b/src/tempo/WithdrawalSenderTag.ts @@ -0,0 +1,66 @@ +import * as AbiParameters from '../core/AbiParameters.js' +import type * as Address from '../core/Address.js' +import * as Hash from '../core/Hash.js' +import type * as Hex from '../core/Hex.js' + +/** + * Derives the sender tag that identifies the indexed parent-chain + * `WithdrawalProcessed` event for a Zone withdrawal. + * + * The `transactionHash` is the Zone transaction containing the + * `ZoneOutbox.requestWithdrawal` call. The protocol defines a user withdrawal + * sender tag as + * `keccak256(abi.encodePacked(sender, transactionHash, fallbackNonce))`. + * Internal deposit bounce-backs use the canonical zero-sender tag derived from + * `address(0)` and `bytes32(0)` without a fallback nonce. + * + * [Authenticated Withdrawals Specification](https://github.com/tempoxyz/zones/blob/main/specs/spec.md#authenticated-withdrawals) + * + * @example + * ```ts twoslash + * import { WithdrawalSenderTag } from 'ox/tempo' + * + * const senderTag = WithdrawalSenderTag.from({ + * fallbackNonce: 19n, + * sender: '0x1234567890abcdef1234567890abcdef12345678', + * transactionHash: + * '0xabababababababababababababababababababababababababababababababab' + * }) + * ``` + * + * @param value - Withdrawal sender, Zone transaction hash, and fallback nonce. + * @returns The sender tag. + */ +export function from(value: from.Value): Hex.Hex { + const { fallbackNonce, sender, transactionHash } = value + if ( + sender === '0x0000000000000000000000000000000000000000' && + fallbackNonce === 0n + ) + return Hash.keccak256( + AbiParameters.encodePacked( + ['address', 'bytes32'], + [ + sender, + '0x0000000000000000000000000000000000000000000000000000000000000000', + ], + ), + ) + return Hash.keccak256( + AbiParameters.encodePacked( + ['address', 'bytes32', 'uint64'], + [sender, transactionHash, fallbackNonce], + ), + ) +} + +export declare namespace from { + export type Value = { + /** Public nonce assigned to the withdrawal's private fallback recipient. */ + fallbackNonce: bigint + /** Address that requested the withdrawal. */ + sender: Address.Address + /** Hash of the Zone transaction containing `ZoneOutbox.requestWithdrawal`. */ + transactionHash: Hex.Hex + } +} diff --git a/src/tempo/index.ts b/src/tempo/index.ts index 8ca13a3a..100fb39e 100644 --- a/src/tempo/index.ts +++ b/src/tempo/index.ts @@ -520,6 +520,30 @@ export * as VirtualAddress from './VirtualAddress.js' * @category Reference */ export * as VirtualMaster from './VirtualMaster.js' +/** + * Utilities for deriving the sender tag that correlates a Zone withdrawal with + * its indexed parent-chain `WithdrawalProcessed` event. + * + * The sender tag commits to the withdrawal sender, the Zone transaction hash + * containing the `ZoneOutbox.requestWithdrawal` call, and its fallback nonce. + * + * [Authenticated Withdrawals Specification](https://github.com/tempoxyz/zones/blob/main/specs/spec.md#authenticated-withdrawals) + * + * @example + * ```ts twoslash + * import { WithdrawalSenderTag } from 'ox/tempo' + * + * const senderTag = WithdrawalSenderTag.from({ + * fallbackNonce: 19n, + * sender: '0x1234567890abcdef1234567890abcdef12345678', + * transactionHash: + * '0xabababababababababababababababababababababababababababababababab' + * }) + * ``` + * + * @category Reference + */ +export * as WithdrawalSenderTag from './WithdrawalSenderTag.js' /** * Zone ID utilities for converting between zone IDs and zone chain IDs.