Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tidy-zones-withdraw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'ox': patch
---

Fixed `WithdrawalSenderTag.from` to include the Zone withdrawal fallback nonce.
1 change: 1 addition & 0 deletions src/tempo/WithdrawalSenderTag.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { expectTypeOf, test } from 'vp/test'
test('from', () => {
expectTypeOf(
WithdrawalSenderTag.from({
fallbackNonce: 19n,
sender: '0x1234567890abcdef1234567890abcdef12345678',
transactionHash:
'0xabababababababababababababababababababababababababababababababab',
Expand Down
22 changes: 13 additions & 9 deletions src/tempo/WithdrawalSenderTag.test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import { WithdrawalSenderTag } from 'ox/tempo'
import { expect, test } from 'vp/test'

test('from', () => {
test('from: production withdrawal', () => {
const senderTag = WithdrawalSenderTag.from({
sender: '0x1234567890abcdef1234567890abcdef12345678',
fallbackNonce: 19n,
sender: '0x0F0896dbf0465E5c07963301dcFEA1101Fa91EaC',
transactionHash:
'0xabababababababababababababababababababababababababababababababab',
'0xae628bdc4bd24a9f9a917825a208baa16c384ab8a96a40cd5146bd20d9b3f6d9',
})
expect(senderTag).toMatchInlineSnapshot(
`"0x3362fade7333b56b9f3582089ec5915b8a6f6ac13e73a7f90c169e3eb81d8a5e"`,
`"0xf1acbae45cd689281144042331e3379cf631a8d2db83057ccf38754a0b0108f2"`,
)
})

test('deterministic', () => {
const value = {
sender: '0x1234567890abcdef1234567890abcdef12345678',
test('from: internal deposit bounce-back', () => {
const senderTag = WithdrawalSenderTag.from({
fallbackNonce: 0n,
sender: '0x0000000000000000000000000000000000000000',
transactionHash:
'0xabababababababababababababababababababababababababababababababab',
} as const
expect(WithdrawalSenderTag.from(value)).toBe(WithdrawalSenderTag.from(value))
})
expect(senderTag).toMatchInlineSnapshot(
`"0xa86d54e9aab41ae5e520ff0062ff1b4cbd0b2192bb01080a058bb170d84e6457"`,
)
})
31 changes: 25 additions & 6 deletions src/tempo/WithdrawalSenderTag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import * as Hex from '../core/Hex.js'
* `WithdrawalProcessed` event for a Zone withdrawal.
*
* The `transactionHash` is the Zone transaction containing the
* `ZoneOutbox.requestWithdrawal` call. The protocol defines the sender tag as
* `keccak256(abi.encodePacked(sender, transactionHash))`.
* `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)
*
Expand All @@ -18,27 +21,43 @@ import * as Hex from '../core/Hex.js'
* import { WithdrawalSenderTag } from 'ox/tempo'
*
* const senderTag = WithdrawalSenderTag.from({
* fallbackNonce: 19n,
* sender: '0x1234567890abcdef1234567890abcdef12345678',
* transactionHash:
* '0xabababababababababababababababababababababababababababababababab'
* })
* ```
*
* @param value - Withdrawal sender and Zone transaction hash.
* @param value - Withdrawal sender, Zone transaction hash, and fallback nonce.
* @returns The sender tag.
*/
export function from(value: from.Value): Hex.Hex {
const { sender, transactionHash } = value
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'],
[sender, transactionHash],
['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`. */
Expand Down
Loading