ox 1.6.1 → 1.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/tempo/KeyAuthorization.d.ts +16 -12
- package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/KeyAuthorization.js +7 -13
- package/dist/tempo/KeyAuthorization.js.map +1 -1
- package/dist/tempo/MultisigConfig.d.ts +20 -17
- package/dist/tempo/MultisigConfig.d.ts.map +1 -1
- package/dist/tempo/MultisigConfig.js +17 -16
- package/dist/tempo/MultisigConfig.js.map +1 -1
- package/dist/tempo/SignatureEnvelope.d.ts +19 -17
- package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.js +35 -27
- package/dist/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/tempo/WithdrawalSenderTag.d.ts +9 -3
- package/dist/tempo/WithdrawalSenderTag.d.ts.map +1 -1
- package/dist/tempo/WithdrawalSenderTag.js +15 -5
- package/dist/tempo/WithdrawalSenderTag.js.map +1 -1
- package/dist/tempo/index.d.ts +5 -4
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +5 -4
- package/dist/tempo/index.js.map +1 -1
- package/dist/zod/tempo/KeyAuthorization.d.ts +231 -22
- package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/zod/tempo/KeyAuthorization.js +10 -2
- package/dist/zod/tempo/KeyAuthorization.js.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +82 -8
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +246 -24
- package/dist/zod/tempo/Transaction.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.d.ts +149 -14
- package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts +78 -6
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/tempo/KeyAuthorization.test-d.ts +41 -6
- package/src/tempo/KeyAuthorization.test.ts +77 -18
- package/src/tempo/KeyAuthorization.ts +36 -36
- package/src/tempo/MultisigConfig.test.ts +28 -8
- package/src/tempo/MultisigConfig.ts +28 -19
- package/src/tempo/SignatureEnvelope.test.ts +64 -19
- package/src/tempo/SignatureEnvelope.ts +45 -35
- package/src/tempo/WithdrawalSenderTag.test-d.ts +1 -0
- package/src/tempo/WithdrawalSenderTag.test.ts +13 -9
- package/src/tempo/WithdrawalSenderTag.ts +25 -6
- package/src/tempo/index.ts +5 -4
- package/src/tempo/multisig.e2e.test.ts +402 -145
- package/src/version.ts +1 -1
- package/src/zod/tempo/KeyAuthorization.ts +12 -2
- package/src/zod/tempo/_test/KeyAuthorization.test.ts +19 -5
|
@@ -1116,7 +1116,7 @@ describe('from', () => {
|
|
|
1116
1116
|
})
|
|
1117
1117
|
|
|
1118
1118
|
describe('multisig', () => {
|
|
1119
|
-
const
|
|
1119
|
+
const initialConfig = MultisigConfig.from({
|
|
1120
1120
|
threshold: 1,
|
|
1121
1121
|
owners: [
|
|
1122
1122
|
{
|
|
@@ -1126,29 +1126,29 @@ describe('from', () => {
|
|
|
1126
1126
|
],
|
|
1127
1127
|
})
|
|
1128
1128
|
|
|
1129
|
-
test('behavior: derives `account` from `
|
|
1129
|
+
test('behavior: derives `account` from `initialConfig`', () => {
|
|
1130
1130
|
const envelope = SignatureEnvelope.from({
|
|
1131
|
-
|
|
1131
|
+
initialConfig,
|
|
1132
1132
|
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
1133
1133
|
})
|
|
1134
1134
|
|
|
1135
1135
|
expect(envelope).toMatchObject({
|
|
1136
1136
|
type: 'multisig',
|
|
1137
|
-
account: MultisigConfig.getAddress(
|
|
1137
|
+
account: MultisigConfig.getAddress(initialConfig),
|
|
1138
1138
|
})
|
|
1139
|
-
expect('
|
|
1139
|
+
expect('initialConfig' in envelope).toBe(false)
|
|
1140
1140
|
expect((envelope as SignatureEnvelope.Multisig).init).toBeUndefined()
|
|
1141
1141
|
})
|
|
1142
1142
|
|
|
1143
|
-
test('behavior: `init: true` opts into bootstrap (uses `
|
|
1143
|
+
test('behavior: `init: true` opts into bootstrap (uses `initialConfig` as `init`)', () => {
|
|
1144
1144
|
const envelope = SignatureEnvelope.from({
|
|
1145
|
-
|
|
1145
|
+
initialConfig,
|
|
1146
1146
|
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
1147
1147
|
init: true,
|
|
1148
1148
|
})
|
|
1149
1149
|
|
|
1150
1150
|
expect((envelope as SignatureEnvelope.Multisig).init).toEqual(
|
|
1151
|
-
|
|
1151
|
+
initialConfig,
|
|
1152
1152
|
)
|
|
1153
1153
|
})
|
|
1154
1154
|
|
|
@@ -1163,7 +1163,7 @@ describe('from', () => {
|
|
|
1163
1163
|
],
|
|
1164
1164
|
})
|
|
1165
1165
|
const envelope = SignatureEnvelope.from({
|
|
1166
|
-
|
|
1166
|
+
initialConfig,
|
|
1167
1167
|
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
1168
1168
|
init: otherConfig,
|
|
1169
1169
|
})
|
|
@@ -1172,7 +1172,7 @@ describe('from', () => {
|
|
|
1172
1172
|
})
|
|
1173
1173
|
|
|
1174
1174
|
test('behavior: `{ account }` form still works', () => {
|
|
1175
|
-
const account = MultisigConfig.getAddress(
|
|
1175
|
+
const account = MultisigConfig.getAddress(initialConfig)
|
|
1176
1176
|
const envelope = SignatureEnvelope.from({
|
|
1177
1177
|
account,
|
|
1178
1178
|
signatures: [SignatureEnvelope.from(signature_secp256k1)],
|
|
@@ -1793,7 +1793,7 @@ describe('serialize', () => {
|
|
|
1793
1793
|
})
|
|
1794
1794
|
|
|
1795
1795
|
describe('sortMultisigApprovals', () => {
|
|
1796
|
-
// Build owner key pairs first so we can construct a real
|
|
1796
|
+
// Build owner key pairs first so we can construct a real initial config
|
|
1797
1797
|
// whose owner set matches the keys that produce the approvals below.
|
|
1798
1798
|
const ownerKeys = Array.from({ length: 3 }, () => {
|
|
1799
1799
|
const privateKey = Secp256k1.randomPrivateKey()
|
|
@@ -1806,12 +1806,15 @@ describe('sortMultisigApprovals', () => {
|
|
|
1806
1806
|
Hex.toBigInt(a.address) < Hex.toBigInt(b.address) ? -1 : 1,
|
|
1807
1807
|
)
|
|
1808
1808
|
|
|
1809
|
-
const
|
|
1809
|
+
const initialConfig = MultisigConfig.from({
|
|
1810
1810
|
threshold: 2,
|
|
1811
1811
|
owners: ascendingOwners.map((o) => ({ owner: o.address, weight: 1 })),
|
|
1812
1812
|
})
|
|
1813
1813
|
const payload = `0x${'42'.repeat(32)}` as const
|
|
1814
|
-
const digest = MultisigConfig.getSignPayload({
|
|
1814
|
+
const digest = MultisigConfig.getSignPayload({
|
|
1815
|
+
payload,
|
|
1816
|
+
initialConfig,
|
|
1817
|
+
})
|
|
1815
1818
|
|
|
1816
1819
|
const owners = ownerKeys.map((owner) => ({
|
|
1817
1820
|
address: owner.address,
|
|
@@ -1826,7 +1829,7 @@ describe('sortMultisigApprovals', () => {
|
|
|
1826
1829
|
|
|
1827
1830
|
test('behavior: orders approvals ascending by recovered owner address', () => {
|
|
1828
1831
|
const ordered = SignatureEnvelope.sortMultisigApprovals({
|
|
1829
|
-
|
|
1832
|
+
initialConfig,
|
|
1830
1833
|
payload,
|
|
1831
1834
|
// Provide approvals in reverse of the canonical order.
|
|
1832
1835
|
signatures: [...ascending].reverse().map((owner) => owner.signature),
|
|
@@ -1838,7 +1841,7 @@ describe('sortMultisigApprovals', () => {
|
|
|
1838
1841
|
const signatures = ascending.map((owner) => owner.signature)
|
|
1839
1842
|
expect(
|
|
1840
1843
|
SignatureEnvelope.sortMultisigApprovals({
|
|
1841
|
-
|
|
1844
|
+
initialConfig,
|
|
1842
1845
|
payload,
|
|
1843
1846
|
signatures,
|
|
1844
1847
|
}),
|
|
@@ -1847,7 +1850,7 @@ describe('sortMultisigApprovals', () => {
|
|
|
1847
1850
|
|
|
1848
1851
|
test('behavior: recovered order matches the config owner order', () => {
|
|
1849
1852
|
const ordered = SignatureEnvelope.sortMultisigApprovals({
|
|
1850
|
-
|
|
1853
|
+
initialConfig,
|
|
1851
1854
|
payload,
|
|
1852
1855
|
signatures: owners.map((owner) => owner.signature),
|
|
1853
1856
|
})
|
|
@@ -1857,12 +1860,12 @@ describe('sortMultisigApprovals', () => {
|
|
|
1857
1860
|
expect(recovered).toEqual(ascending.map((owner) => owner.address))
|
|
1858
1861
|
})
|
|
1859
1862
|
|
|
1860
|
-
test('behavior: `
|
|
1861
|
-
const account = MultisigConfig.getAddress(
|
|
1863
|
+
test('behavior: `initialConfig` and `{ account }` produce identical ordering', () => {
|
|
1864
|
+
const account = MultisigConfig.getAddress(initialConfig)
|
|
1862
1865
|
const signatures = owners.map((owner) => owner.signature)
|
|
1863
1866
|
|
|
1864
1867
|
const fromConfig = SignatureEnvelope.sortMultisigApprovals({
|
|
1865
|
-
|
|
1868
|
+
initialConfig,
|
|
1866
1869
|
payload,
|
|
1867
1870
|
signatures,
|
|
1868
1871
|
})
|
|
@@ -3041,6 +3044,14 @@ describe('multisig', () => {
|
|
|
3041
3044
|
).toThrowErrorMatchingInlineSnapshot(
|
|
3042
3045
|
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig owner signature exceeds 2049 bytes.]`,
|
|
3043
3046
|
)
|
|
3047
|
+
|
|
3048
|
+
const serialized = Hex.concat(
|
|
3049
|
+
'0x05',
|
|
3050
|
+
Rlp.fromHex([account, [SignatureEnvelope.serialize(oversized)]]),
|
|
3051
|
+
)
|
|
3052
|
+
expect(() => SignatureEnvelope.deserialize(serialized)).toThrowError(
|
|
3053
|
+
SignatureEnvelope.InvalidSerializedError,
|
|
3054
|
+
)
|
|
3044
3055
|
})
|
|
3045
3056
|
|
|
3046
3057
|
test('assert: rejects keychain owner approvals', () => {
|
|
@@ -3082,6 +3093,40 @@ describe('multisig', () => {
|
|
|
3082
3093
|
expect(() => SignatureEnvelope.assert(depth2)).not.toThrowError()
|
|
3083
3094
|
})
|
|
3084
3095
|
|
|
3096
|
+
test('accepts a nested approval above the primitive byte limit', () => {
|
|
3097
|
+
const primitive = SignatureEnvelope.from({
|
|
3098
|
+
...signature_webauthn,
|
|
3099
|
+
metadata: {
|
|
3100
|
+
...signature_webauthn.metadata,
|
|
3101
|
+
clientDataJSON: JSON.stringify({ value: 'x'.repeat(1_050) }),
|
|
3102
|
+
},
|
|
3103
|
+
signature: {
|
|
3104
|
+
r: signature_webauthn.signature.r,
|
|
3105
|
+
s: signature_webauthn.signature.s,
|
|
3106
|
+
},
|
|
3107
|
+
})
|
|
3108
|
+
const largeNested = SignatureEnvelope.from({
|
|
3109
|
+
type: 'multisig',
|
|
3110
|
+
account: nestedAccount,
|
|
3111
|
+
signatures: [primitive, primitive],
|
|
3112
|
+
})
|
|
3113
|
+
expect(Hex.size(SignatureEnvelope.serialize(primitive))).toBeLessThan(
|
|
3114
|
+
MultisigConfig.maxOwnerSignatureBytes,
|
|
3115
|
+
)
|
|
3116
|
+
expect(
|
|
3117
|
+
Hex.size(SignatureEnvelope.serialize(largeNested)),
|
|
3118
|
+
).toBeGreaterThan(MultisigConfig.maxOwnerSignatureBytes)
|
|
3119
|
+
|
|
3120
|
+
const parent = SignatureEnvelope.from({
|
|
3121
|
+
type: 'multisig',
|
|
3122
|
+
account,
|
|
3123
|
+
signatures: [largeNested],
|
|
3124
|
+
})
|
|
3125
|
+
expect(
|
|
3126
|
+
SignatureEnvelope.deserialize(SignatureEnvelope.serialize(parent)),
|
|
3127
|
+
).toEqual(parent)
|
|
3128
|
+
})
|
|
3129
|
+
|
|
3085
3130
|
test('assert: rejects nesting deeper than the maximum', () => {
|
|
3086
3131
|
const depth3 = SignatureEnvelope.from({
|
|
3087
3132
|
type: 'multisig',
|
|
@@ -434,12 +434,13 @@ function assertMultisig(envelope: Multisig, depth: number): void {
|
|
|
434
434
|
reason: 'nested multisig owner approvals cannot carry `init`',
|
|
435
435
|
})
|
|
436
436
|
assertMultisig(multisig, depth + 1)
|
|
437
|
-
} else
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
437
|
+
} else {
|
|
438
|
+
assert(inner)
|
|
439
|
+
if (Hex.size(serialize(inner)) > MultisigConfig.maxOwnerSignatureBytes)
|
|
440
|
+
throw new InvalidMultisigApprovalError({
|
|
441
|
+
reason: `multisig owner signature exceeds ${MultisigConfig.maxOwnerSignatureBytes} bytes`,
|
|
442
|
+
})
|
|
443
|
+
}
|
|
443
444
|
}
|
|
444
445
|
}
|
|
445
446
|
|
|
@@ -750,7 +751,10 @@ function deserialize_(
|
|
|
750
751
|
})
|
|
751
752
|
for (const signature of signatures)
|
|
752
753
|
if (
|
|
753
|
-
Hex.size(signature as Hex.Hex) >
|
|
754
|
+
Hex.size(signature as Hex.Hex) >
|
|
755
|
+
MultisigConfig.maxOwnerSignatureBytes &&
|
|
756
|
+
Hex.slice(signature as Hex.Hex, 0, 1) !==
|
|
757
|
+
MultisigConfig.signatureTypeByte
|
|
754
758
|
)
|
|
755
759
|
throw new InvalidSerializedError({
|
|
756
760
|
reason: `multisig owner signature exceeds ${MultisigConfig.maxOwnerSignatureBytes} bytes`,
|
|
@@ -935,17 +939,17 @@ function deserialize_(
|
|
|
935
939
|
* ```
|
|
936
940
|
*
|
|
937
941
|
* @example
|
|
938
|
-
* ### Multisig (from
|
|
942
|
+
* ### Multisig (from initial config)
|
|
939
943
|
*
|
|
940
|
-
* Pass `
|
|
941
|
-
* opt into bootstrap (uses `
|
|
944
|
+
* Pass `initialConfig` to derive `account` automatically. Set `init: true` to
|
|
945
|
+
* opt into bootstrap (uses `initialConfig` as the bootstrap `init`); omit
|
|
942
946
|
* `init` for subsequent (non-bootstrap) transactions.
|
|
943
947
|
*
|
|
944
948
|
* ```ts twoslash
|
|
945
949
|
* import { Secp256k1 } from 'ox'
|
|
946
950
|
* import { MultisigConfig, SignatureEnvelope } from 'ox/tempo'
|
|
947
951
|
*
|
|
948
|
-
* const
|
|
952
|
+
* const initialConfig = MultisigConfig.from({
|
|
949
953
|
* threshold: 1,
|
|
950
954
|
* owners: [
|
|
951
955
|
* {
|
|
@@ -962,14 +966,14 @@ function deserialize_(
|
|
|
962
966
|
*
|
|
963
967
|
* // Bootstrap transaction
|
|
964
968
|
* const bootstrap = SignatureEnvelope.from({
|
|
965
|
-
*
|
|
969
|
+
* initialConfig,
|
|
966
970
|
* signatures: [signature],
|
|
967
971
|
* init: true
|
|
968
972
|
* })
|
|
969
973
|
*
|
|
970
974
|
* // Subsequent (non-bootstrap) transactions
|
|
971
975
|
* const subsequent = SignatureEnvelope.from({
|
|
972
|
-
*
|
|
976
|
+
* initialConfig,
|
|
973
977
|
* signatures: [signature]
|
|
974
978
|
* })
|
|
975
979
|
* ```
|
|
@@ -996,20 +1000,20 @@ export function from<const value extends from.Value>(
|
|
|
996
1000
|
|
|
997
1001
|
if (type === 'multisig') {
|
|
998
1002
|
const multisig = value as Multisig & {
|
|
999
|
-
|
|
1003
|
+
initialConfig?: MultisigConfig.Config | undefined
|
|
1000
1004
|
init?: MultisigConfig.Config | boolean | undefined
|
|
1001
1005
|
}
|
|
1002
|
-
const {
|
|
1003
|
-
// Derive `account` from `
|
|
1006
|
+
const { initialConfig, init, ...rest } = multisig
|
|
1007
|
+
// Derive `account` from `initialConfig` when not provided explicitly.
|
|
1004
1008
|
const account = (() => {
|
|
1005
1009
|
if (rest.account) return rest.account
|
|
1006
|
-
if (
|
|
1010
|
+
if (initialConfig) return MultisigConfig.getAddress(initialConfig)
|
|
1007
1011
|
return rest.account
|
|
1008
1012
|
})()
|
|
1009
|
-
// `init: true` opts into bootstrap using the supplied `
|
|
1013
|
+
// `init: true` opts into bootstrap using the supplied `initialConfig`.
|
|
1010
1014
|
// Otherwise, `init` is treated as the explicit bootstrap config (or
|
|
1011
1015
|
// omitted).
|
|
1012
|
-
const initSource = init === true ?
|
|
1016
|
+
const initSource = init === true ? initialConfig : init || undefined
|
|
1013
1017
|
return {
|
|
1014
1018
|
...rest,
|
|
1015
1019
|
account,
|
|
@@ -1065,13 +1069,13 @@ export declare namespace from {
|
|
|
1065
1069
|
|
|
1066
1070
|
/**
|
|
1067
1071
|
* Multisig envelope input variant where `account` is derived from the
|
|
1068
|
-
* supplied `
|
|
1069
|
-
* `
|
|
1072
|
+
* supplied `initialConfig`. Pass `init: true` to opt into bootstrap (uses
|
|
1073
|
+
* `initialConfig` as the bootstrap `init`); omit `init` for subsequent
|
|
1070
1074
|
* (non-bootstrap) transactions.
|
|
1071
1075
|
*/
|
|
1072
|
-
type
|
|
1076
|
+
type MultisigFromInitialConfig = {
|
|
1073
1077
|
type?: 'multisig' | undefined
|
|
1074
|
-
|
|
1078
|
+
initialConfig: MultisigConfig.Config
|
|
1075
1079
|
signatures: readonly SignatureEnvelope[]
|
|
1076
1080
|
init?: MultisigConfig.Config | boolean | undefined
|
|
1077
1081
|
}
|
|
@@ -1080,7 +1084,7 @@ export declare namespace from {
|
|
|
1080
1084
|
| UnionPartialBy<SignatureEnvelope, 'prehash' | 'type'>
|
|
1081
1085
|
| Secp256k1Flat
|
|
1082
1086
|
| Serialized
|
|
1083
|
-
|
|
|
1087
|
+
| MultisigFromInitialConfig
|
|
1084
1088
|
|
|
1085
1089
|
type ReturnValue<value extends Value> = Compute<
|
|
1086
1090
|
OneOf<
|
|
@@ -1088,7 +1092,7 @@ export declare namespace from {
|
|
|
1088
1092
|
? SignatureEnvelope
|
|
1089
1093
|
: value extends Secp256k1Flat
|
|
1090
1094
|
? Secp256k1
|
|
1091
|
-
: value extends
|
|
1095
|
+
: value extends MultisigFromInitialConfig
|
|
1092
1096
|
? Multisig
|
|
1093
1097
|
: IsNarrowable<value, SignatureEnvelope> extends true
|
|
1094
1098
|
? SignatureEnvelope
|
|
@@ -1315,7 +1319,7 @@ export function getType<
|
|
|
1315
1319
|
// Detect Multisig signature
|
|
1316
1320
|
if (
|
|
1317
1321
|
('account' in envelope ||
|
|
1318
|
-
'
|
|
1322
|
+
'initialConfig' in envelope ||
|
|
1319
1323
|
'init' in envelope) &&
|
|
1320
1324
|
'signatures' in envelope
|
|
1321
1325
|
)
|
|
@@ -1470,7 +1474,7 @@ export declare namespace serialize {
|
|
|
1470
1474
|
* recovered owner address. Works for any owner key type (secp256k1, p256,
|
|
1471
1475
|
* webAuthn).
|
|
1472
1476
|
*
|
|
1473
|
-
* Config updates never change `account`, so the
|
|
1477
|
+
* Config updates never change `account`, so the initial config is the correct
|
|
1474
1478
|
* input even for post-update transactions.
|
|
1475
1479
|
*
|
|
1476
1480
|
* @example
|
|
@@ -1482,7 +1486,7 @@ export declare namespace serialize {
|
|
|
1482
1486
|
* TxEnvelopeTempo
|
|
1483
1487
|
* } from 'ox/tempo'
|
|
1484
1488
|
*
|
|
1485
|
-
* const
|
|
1489
|
+
* const initialConfig = MultisigConfig.from({
|
|
1486
1490
|
* threshold: 2,
|
|
1487
1491
|
* owners: [
|
|
1488
1492
|
* {
|
|
@@ -1505,7 +1509,7 @@ export declare namespace serialize {
|
|
|
1505
1509
|
* ]
|
|
1506
1510
|
* const digest = MultisigConfig.getSignPayload({
|
|
1507
1511
|
* payload,
|
|
1508
|
-
*
|
|
1512
|
+
* initialConfig
|
|
1509
1513
|
* })
|
|
1510
1514
|
* const signatures = privateKeys.map((privateKey) =>
|
|
1511
1515
|
* SignatureEnvelope.from(
|
|
@@ -1515,7 +1519,7 @@ export declare namespace serialize {
|
|
|
1515
1519
|
*
|
|
1516
1520
|
* const ordered = SignatureEnvelope.sortMultisigApprovals({
|
|
1517
1521
|
* // [!code focus]
|
|
1518
|
-
*
|
|
1522
|
+
* initialConfig, // [!code focus]
|
|
1519
1523
|
* payload, // [!code focus]
|
|
1520
1524
|
* signatures // [!code focus]
|
|
1521
1525
|
* }) // [!code focus]
|
|
@@ -1527,11 +1531,15 @@ export declare namespace serialize {
|
|
|
1527
1531
|
export function sortMultisigApprovals(
|
|
1528
1532
|
value: sortMultisigApprovals.Value,
|
|
1529
1533
|
): readonly SignatureEnvelope[] {
|
|
1530
|
-
const { payload, signatures } = value
|
|
1534
|
+
const { payload, signatures, version = 0n } = value
|
|
1531
1535
|
const digest = MultisigConfig.getSignPayload(
|
|
1532
|
-
'
|
|
1533
|
-
? { payload,
|
|
1534
|
-
: {
|
|
1536
|
+
'initialConfig' in value && value.initialConfig
|
|
1537
|
+
? { payload, initialConfig: value.initialConfig, version }
|
|
1538
|
+
: {
|
|
1539
|
+
payload,
|
|
1540
|
+
account: (value as { account: Address.Address }).account,
|
|
1541
|
+
version,
|
|
1542
|
+
},
|
|
1535
1543
|
)
|
|
1536
1544
|
// Recover each signer once (decorate–sort–undecorate) rather than inside the
|
|
1537
1545
|
// comparator.
|
|
@@ -1550,6 +1558,8 @@ export declare namespace sortMultisigApprovals {
|
|
|
1550
1558
|
payload: Hex.Hex | Bytes.Bytes
|
|
1551
1559
|
/** The owner approvals to order. */
|
|
1552
1560
|
signatures: readonly SignatureEnvelope[]
|
|
1561
|
+
/** Current multisig config version. Defaults to `0n`. */
|
|
1562
|
+
version?: bigint | undefined
|
|
1553
1563
|
} & OneOf<
|
|
1554
1564
|
| {
|
|
1555
1565
|
/** The native multisig account address. */
|
|
@@ -1560,7 +1570,7 @@ export declare namespace sortMultisigApprovals {
|
|
|
1560
1570
|
* The initial multisig config (the bootstrap config that derived the
|
|
1561
1571
|
* permanent `account`). Used to derive the account automatically.
|
|
1562
1572
|
*/
|
|
1563
|
-
|
|
1573
|
+
initialConfig: MultisigConfig.Config
|
|
1564
1574
|
}
|
|
1565
1575
|
>
|
|
1566
1576
|
|
|
@@ -5,6 +5,7 @@ import { expectTypeOf, test } from 'vp/test'
|
|
|
5
5
|
test('from', () => {
|
|
6
6
|
expectTypeOf(
|
|
7
7
|
WithdrawalSenderTag.from({
|
|
8
|
+
fallbackNonce: 19n,
|
|
8
9
|
sender: '0x1234567890abcdef1234567890abcdef12345678',
|
|
9
10
|
transactionHash:
|
|
10
11
|
'0xabababababababababababababababababababababababababababababababab',
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
import { WithdrawalSenderTag } from 'ox/tempo'
|
|
2
2
|
import { expect, test } from 'vp/test'
|
|
3
3
|
|
|
4
|
-
test('from', () => {
|
|
4
|
+
test('from: production withdrawal', () => {
|
|
5
5
|
const senderTag = WithdrawalSenderTag.from({
|
|
6
|
-
|
|
6
|
+
fallbackNonce: 19n,
|
|
7
|
+
sender: '0x0F0896dbf0465E5c07963301dcFEA1101Fa91EaC',
|
|
7
8
|
transactionHash:
|
|
8
|
-
'
|
|
9
|
+
'0xae628bdc4bd24a9f9a917825a208baa16c384ab8a96a40cd5146bd20d9b3f6d9',
|
|
9
10
|
})
|
|
10
11
|
expect(senderTag).toMatchInlineSnapshot(
|
|
11
|
-
`"
|
|
12
|
+
`"0xf1acbae45cd689281144042331e3379cf631a8d2db83057ccf38754a0b0108f2"`,
|
|
12
13
|
)
|
|
13
14
|
})
|
|
14
15
|
|
|
15
|
-
test('
|
|
16
|
-
const
|
|
17
|
-
|
|
16
|
+
test('from: internal deposit bounce-back', () => {
|
|
17
|
+
const senderTag = WithdrawalSenderTag.from({
|
|
18
|
+
fallbackNonce: 0n,
|
|
19
|
+
sender: '0x0000000000000000000000000000000000000000',
|
|
18
20
|
transactionHash:
|
|
19
21
|
'0xabababababababababababababababababababababababababababababababab',
|
|
20
|
-
}
|
|
21
|
-
expect(
|
|
22
|
+
})
|
|
23
|
+
expect(senderTag).toMatchInlineSnapshot(
|
|
24
|
+
`"0xa86d54e9aab41ae5e520ff0062ff1b4cbd0b2192bb01080a058bb170d84e6457"`,
|
|
25
|
+
)
|
|
22
26
|
})
|
|
@@ -8,8 +8,11 @@ import * as Hex from '../core/Hex.js'
|
|
|
8
8
|
* `WithdrawalProcessed` event for a Zone withdrawal.
|
|
9
9
|
*
|
|
10
10
|
* The `transactionHash` is the Zone transaction containing the
|
|
11
|
-
* `ZoneOutbox.requestWithdrawal` call. The protocol defines
|
|
12
|
-
*
|
|
11
|
+
* `ZoneOutbox.requestWithdrawal` call. The protocol defines a user withdrawal
|
|
12
|
+
* sender tag as
|
|
13
|
+
* `keccak256(abi.encodePacked(sender, transactionHash, fallbackNonce))`.
|
|
14
|
+
* Internal deposit bounce-backs use the canonical zero-sender tag derived from
|
|
15
|
+
* `address(0)` and `bytes32(0)` without a fallback nonce.
|
|
13
16
|
*
|
|
14
17
|
* [Authenticated Withdrawals Specification](https://github.com/tempoxyz/zones/blob/main/specs/spec.md#authenticated-withdrawals)
|
|
15
18
|
*
|
|
@@ -18,27 +21,43 @@ import * as Hex from '../core/Hex.js'
|
|
|
18
21
|
* import { WithdrawalSenderTag } from 'ox/tempo'
|
|
19
22
|
*
|
|
20
23
|
* const senderTag = WithdrawalSenderTag.from({
|
|
24
|
+
* fallbackNonce: 19n,
|
|
21
25
|
* sender: '0x1234567890abcdef1234567890abcdef12345678',
|
|
22
26
|
* transactionHash:
|
|
23
27
|
* '0xabababababababababababababababababababababababababababababababab'
|
|
24
28
|
* })
|
|
25
29
|
* ```
|
|
26
30
|
*
|
|
27
|
-
* @param value - Withdrawal sender
|
|
31
|
+
* @param value - Withdrawal sender, Zone transaction hash, and fallback nonce.
|
|
28
32
|
* @returns The sender tag.
|
|
29
33
|
*/
|
|
30
34
|
export function from(value: from.Value): Hex.Hex {
|
|
31
|
-
const { sender, transactionHash } = value
|
|
35
|
+
const { fallbackNonce, sender, transactionHash } = value
|
|
36
|
+
if (
|
|
37
|
+
sender === '0x0000000000000000000000000000000000000000' &&
|
|
38
|
+
fallbackNonce === 0n
|
|
39
|
+
)
|
|
40
|
+
return Hash.keccak256(
|
|
41
|
+
AbiParameters.encodePacked(
|
|
42
|
+
['address', 'bytes32'],
|
|
43
|
+
[
|
|
44
|
+
sender,
|
|
45
|
+
'0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
46
|
+
],
|
|
47
|
+
),
|
|
48
|
+
)
|
|
32
49
|
return Hash.keccak256(
|
|
33
50
|
AbiParameters.encodePacked(
|
|
34
|
-
['address', 'bytes32'],
|
|
35
|
-
[sender, transactionHash],
|
|
51
|
+
['address', 'bytes32', 'uint64'],
|
|
52
|
+
[sender, transactionHash, fallbackNonce],
|
|
36
53
|
),
|
|
37
54
|
)
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
export declare namespace from {
|
|
41
58
|
export type Value = {
|
|
59
|
+
/** Public nonce assigned to the withdrawal's private fallback recipient. */
|
|
60
|
+
fallbackNonce: bigint
|
|
42
61
|
/** Address that requested the withdrawal. */
|
|
43
62
|
sender: Address.Address
|
|
44
63
|
/** Hash of the Zone transaction containing `ZoneOutbox.requestWithdrawal`. */
|
package/src/tempo/index.ts
CHANGED
|
@@ -153,7 +153,7 @@ export * as KeyAuthorization from './KeyAuthorization.js'
|
|
|
153
153
|
* ```ts twoslash
|
|
154
154
|
* import { MultisigConfig } from 'ox/tempo'
|
|
155
155
|
*
|
|
156
|
-
* const
|
|
156
|
+
* const initialConfig = MultisigConfig.from({
|
|
157
157
|
* threshold: 2,
|
|
158
158
|
* owners: [
|
|
159
159
|
* {
|
|
@@ -167,7 +167,7 @@ export * as KeyAuthorization from './KeyAuthorization.js'
|
|
|
167
167
|
* ]
|
|
168
168
|
* })
|
|
169
169
|
*
|
|
170
|
-
* const account = MultisigConfig.getAddress(
|
|
170
|
+
* const account = MultisigConfig.getAddress(initialConfig)
|
|
171
171
|
* ```
|
|
172
172
|
*
|
|
173
173
|
* @category Reference
|
|
@@ -533,8 +533,8 @@ export * as VirtualMaster from './VirtualMaster.js'
|
|
|
533
533
|
* Utilities for deriving the sender tag that correlates a Zone withdrawal with
|
|
534
534
|
* its indexed parent-chain `WithdrawalProcessed` event.
|
|
535
535
|
*
|
|
536
|
-
* The sender tag commits to the withdrawal sender
|
|
537
|
-
* containing the `ZoneOutbox.requestWithdrawal` call.
|
|
536
|
+
* The sender tag commits to the withdrawal sender, the Zone transaction hash
|
|
537
|
+
* containing the `ZoneOutbox.requestWithdrawal` call, and the fallback nonce.
|
|
538
538
|
*
|
|
539
539
|
* [Authenticated Withdrawals Specification](https://github.com/tempoxyz/zones/blob/main/specs/spec.md#authenticated-withdrawals)
|
|
540
540
|
*
|
|
@@ -543,6 +543,7 @@ export * as VirtualMaster from './VirtualMaster.js'
|
|
|
543
543
|
* import { WithdrawalSenderTag } from 'ox/tempo'
|
|
544
544
|
*
|
|
545
545
|
* const senderTag = WithdrawalSenderTag.from({
|
|
546
|
+
* fallbackNonce: 19n,
|
|
546
547
|
* sender: '0x1234567890abcdef1234567890abcdef12345678',
|
|
547
548
|
* transactionHash:
|
|
548
549
|
* '0xabababababababababababababababababababababababababababababababab'
|