ox 0.14.44 → 0.14.45
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 +6 -0
- package/_cjs/tempo/KeyAuthorization.js +43 -10
- package/_cjs/tempo/KeyAuthorization.js.map +1 -1
- package/_cjs/tempo/MultisigConfig.js +33 -11
- package/_cjs/tempo/MultisigConfig.js.map +1 -1
- package/_cjs/tempo/MultisigOperation.js +18 -94
- package/_cjs/tempo/MultisigOperation.js.map +1 -1
- package/_cjs/tempo/MultisigSimulation.js +18 -46
- package/_cjs/tempo/MultisigSimulation.js.map +1 -1
- package/_cjs/tempo/SignatureEnvelope.js +72 -109
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/tempo/TransactionRequest.js +5 -1
- package/_cjs/tempo/TransactionRequest.js.map +1 -1
- package/_cjs/tempo/index.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/tempo/KeyAuthorization.js +42 -9
- package/_esm/tempo/KeyAuthorization.js.map +1 -1
- package/_esm/tempo/MultisigConfig.js +41 -22
- package/_esm/tempo/MultisigConfig.js.map +1 -1
- package/_esm/tempo/MultisigOperation.js +20 -100
- package/_esm/tempo/MultisigOperation.js.map +1 -1
- package/_esm/tempo/MultisigSimulation.js +18 -46
- package/_esm/tempo/MultisigSimulation.js.map +1 -1
- package/_esm/tempo/SignatureEnvelope.js +107 -169
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/tempo/TransactionRequest.js +5 -1
- package/_esm/tempo/TransactionRequest.js.map +1 -1
- package/_esm/tempo/index.js +3 -1
- package/_esm/tempo/index.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_types/tempo/KeyAuthorization.d.ts +48 -38
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/MultisigConfig.d.ts +15 -14
- package/_types/tempo/MultisigConfig.d.ts.map +1 -1
- package/_types/tempo/MultisigOperation.d.ts +3 -2
- package/_types/tempo/MultisigOperation.d.ts.map +1 -1
- package/_types/tempo/MultisigSimulation.d.ts +2 -49
- package/_types/tempo/MultisigSimulation.d.ts.map +1 -1
- package/_types/tempo/SignatureEnvelope.d.ts +69 -128
- package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/_types/tempo/TransactionRequest.d.ts +5 -2
- package/_types/tempo/TransactionRequest.d.ts.map +1 -1
- package/_types/tempo/index.d.ts +3 -1
- package/_types/tempo/index.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/package.json +1 -1
- package/tempo/AuthorizationTempo.test.ts +8 -4
- package/tempo/KeyAuthorization.test-d.ts +68 -33
- package/tempo/KeyAuthorization.test.ts +89 -8
- package/tempo/KeyAuthorization.ts +124 -61
- package/tempo/MultisigConfig.test.ts +34 -26
- package/tempo/MultisigConfig.ts +54 -24
- package/tempo/MultisigOperation.test.ts +79 -550
- package/tempo/MultisigOperation.ts +31 -146
- package/tempo/MultisigSimulation.test-d.ts +2 -2
- package/tempo/MultisigSimulation.test.ts +21 -110
- package/tempo/MultisigSimulation.ts +25 -99
- package/tempo/SignatureEnvelope.test-d.ts +29 -67
- package/tempo/SignatureEnvelope.test.ts +301 -568
- package/tempo/SignatureEnvelope.ts +156 -274
- package/tempo/TransactionRequest.test-d.ts +6 -5
- package/tempo/TransactionRequest.test.ts +17 -80
- package/tempo/TransactionRequest.ts +23 -3
- package/tempo/index.ts +3 -1
- package/tempo/multisig.e2e.test.ts +322 -861
- package/version.ts +1 -1
|
@@ -78,7 +78,89 @@ const signature_keychain = {
|
|
|
78
78
|
userAddress: address,
|
|
79
79
|
} as const satisfies SignatureEnvelope.Keychain
|
|
80
80
|
|
|
81
|
+
describe('multisig account binding', () => {
|
|
82
|
+
test('resolves Tempo account bindings before encoding', () => {
|
|
83
|
+
const signed = KeyAuthorization.from(
|
|
84
|
+
{ account: `tempox${address}`, address, chainId: 1n, type: 'multisig' },
|
|
85
|
+
{ signature: signature_secp256k1 },
|
|
86
|
+
)
|
|
87
|
+
expect(signed.account).toMatchInlineSnapshot(
|
|
88
|
+
`"0xBE95c3f554e9Fc85ec51bE69a3D807A0D55BCF2C"`,
|
|
89
|
+
)
|
|
90
|
+
expect(KeyAuthorization.toRpc(signed).account).toMatchInlineSnapshot(
|
|
91
|
+
`"0xBE95c3f554e9Fc85ec51bE69a3D807A0D55BCF2C"`,
|
|
92
|
+
)
|
|
93
|
+
expect(
|
|
94
|
+
KeyAuthorization.deserialize(KeyAuthorization.serialize(signed)).account,
|
|
95
|
+
).toMatchInlineSnapshot(`"0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c"`)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('rejects unbound grants before signing or encoding', () => {
|
|
99
|
+
const authorization = { address, chainId: 1n, type: 'multisig' } as const
|
|
100
|
+
const signed = {
|
|
101
|
+
...authorization,
|
|
102
|
+
signature: SignatureEnvelope.from(signature_secp256k1),
|
|
103
|
+
}
|
|
104
|
+
for (const encode of [
|
|
105
|
+
// @ts-expect-error Deliberately unbound multisig grant.
|
|
106
|
+
() => KeyAuthorization.from(authorization),
|
|
107
|
+
// @ts-expect-error Deliberately unbound multisig grant.
|
|
108
|
+
() => KeyAuthorization.toTuple(authorization),
|
|
109
|
+
// @ts-expect-error Deliberately unbound multisig grant.
|
|
110
|
+
() => KeyAuthorization.getSignPayload(authorization),
|
|
111
|
+
// @ts-expect-error Deliberately unbound multisig grant.
|
|
112
|
+
() => KeyAuthorization.serialize(authorization),
|
|
113
|
+
// @ts-expect-error Deliberately unbound multisig grant.
|
|
114
|
+
() => KeyAuthorization.toRpc(signed),
|
|
115
|
+
])
|
|
116
|
+
expect(encode).toThrowErrorMatchingInlineSnapshot(
|
|
117
|
+
`[KeyAuthorization.MissingAccountError: Multisig key grants require a parent account binding.]`,
|
|
118
|
+
)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('rejects unbound RPC and RLP grants', () => {
|
|
122
|
+
const signed = KeyAuthorization.from(
|
|
123
|
+
{ account: address, address, chainId: 1n, type: 'multisig' },
|
|
124
|
+
{ signature: signature_secp256k1 },
|
|
125
|
+
)
|
|
126
|
+
const rpc = KeyAuthorization.toRpc(signed)
|
|
127
|
+
for (const account of [undefined, null]) {
|
|
128
|
+
// @ts-expect-error Deliberately unbound multisig grant.
|
|
129
|
+
expect(() => KeyAuthorization.fromRpc({ ...rpc, account })).toThrowError(
|
|
130
|
+
KeyAuthorization.MissingAccountError,
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
expect(() =>
|
|
134
|
+
KeyAuthorization.fromTuple([['0x01', '0x03', address]]),
|
|
135
|
+
).toThrowError(KeyAuthorization.MissingAccountError)
|
|
136
|
+
expect(KeyAuthorization.fromRpc(rpc).account).toBe(address)
|
|
137
|
+
expect(
|
|
138
|
+
KeyAuthorization.deserialize(KeyAuthorization.serialize(signed)).account,
|
|
139
|
+
).toBe(address)
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
81
143
|
describe('from', () => {
|
|
144
|
+
test('accepts multisig grants and delegates', () => {
|
|
145
|
+
const signed = KeyAuthorization.from(
|
|
146
|
+
{
|
|
147
|
+
address,
|
|
148
|
+
chainId: 1n,
|
|
149
|
+
type: 'multisig',
|
|
150
|
+
account: signature_multisig.account,
|
|
151
|
+
},
|
|
152
|
+
{ signature: signature_multisig },
|
|
153
|
+
)
|
|
154
|
+
expect(
|
|
155
|
+
KeyAuthorization.deserialize(KeyAuthorization.serialize(signed)),
|
|
156
|
+
).toEqual(signed)
|
|
157
|
+
expect(KeyAuthorization.fromRpc(KeyAuthorization.toRpc(signed))).toEqual(
|
|
158
|
+
signed,
|
|
159
|
+
)
|
|
160
|
+
})
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
describe('fromRpc', () => {
|
|
82
164
|
test('default', () => {
|
|
83
165
|
const authorization = KeyAuthorization.from({
|
|
84
166
|
address,
|
|
@@ -1154,7 +1236,6 @@ describe('deserialize', () => {
|
|
|
1154
1236
|
"account": "0xc4a590afa7337e5cd5eb3aa60cacf91c5400044b",
|
|
1155
1237
|
"address": "0x1eff47bc3a10a45d4b230b5d10e37751fe6aa718",
|
|
1156
1238
|
"chainId": 4217n,
|
|
1157
|
-
"isAdmin": false,
|
|
1158
1239
|
"signature": {
|
|
1159
1240
|
"account": "0xc4a590afa7337e5cd5eb3aa60cacf91c5400044b",
|
|
1160
1241
|
"config": {
|
|
@@ -2589,11 +2670,11 @@ describe('admin keys (TIP-1049)', () => {
|
|
|
2589
2670
|
expect((authTuple as unknown as unknown[]).length).toBe(3)
|
|
2590
2671
|
})
|
|
2591
2672
|
|
|
2592
|
-
test('fromTuple:
|
|
2673
|
+
test('fromTuple: preserves isAdmin without account', () => {
|
|
2593
2674
|
const restored = KeyAuthorization.fromTuple([
|
|
2594
2675
|
['0x01', '0x', address, '0x', [], [], '0x', '0x01'],
|
|
2595
2676
|
])
|
|
2596
|
-
expect(restored.isAdmin).
|
|
2677
|
+
expect(restored.isAdmin).toBe(true)
|
|
2597
2678
|
expect(restored.account).toBeUndefined()
|
|
2598
2679
|
})
|
|
2599
2680
|
|
|
@@ -2601,7 +2682,7 @@ describe('admin keys (TIP-1049)', () => {
|
|
|
2601
2682
|
const restored = KeyAuthorization.fromTuple([
|
|
2602
2683
|
['0x01', '0x', address, '0x', [], [], '0x', '0x', account],
|
|
2603
2684
|
])
|
|
2604
|
-
expect(restored.isAdmin).
|
|
2685
|
+
expect(restored.isAdmin).toBeUndefined()
|
|
2605
2686
|
expect(restored.account).toBe(account)
|
|
2606
2687
|
})
|
|
2607
2688
|
|
|
@@ -2647,7 +2728,7 @@ describe('admin keys (TIP-1049)', () => {
|
|
|
2647
2728
|
})
|
|
2648
2729
|
const serialized = KeyAuthorization.serialize(authorization)
|
|
2649
2730
|
const restored = KeyAuthorization.deserialize(serialized)
|
|
2650
|
-
expect(restored.isAdmin).
|
|
2731
|
+
expect(restored.isAdmin).toBeUndefined()
|
|
2651
2732
|
expect(restored.account).toBe(account)
|
|
2652
2733
|
})
|
|
2653
2734
|
|
|
@@ -2670,14 +2751,14 @@ describe('admin keys (TIP-1049)', () => {
|
|
|
2670
2751
|
expect(restored.account).toBe(account)
|
|
2671
2752
|
})
|
|
2672
2753
|
|
|
2673
|
-
test('fromRpc:
|
|
2754
|
+
test('fromRpc: preserves isAdmin without account', () => {
|
|
2674
2755
|
const authorization = KeyAuthorization.from(
|
|
2675
2756
|
{ address, chainId: 1n, type: 'secp256k1' },
|
|
2676
2757
|
{ signature: SignatureEnvelope.from(signature_secp256k1) },
|
|
2677
2758
|
)
|
|
2678
2759
|
const rpc = KeyAuthorization.toRpc(authorization)
|
|
2679
2760
|
const restored = KeyAuthorization.fromRpc({ ...rpc, isAdmin: true })
|
|
2680
|
-
expect(restored.isAdmin).
|
|
2761
|
+
expect(restored.isAdmin).toBe(true)
|
|
2681
2762
|
expect(restored.account).toBeUndefined()
|
|
2682
2763
|
})
|
|
2683
2764
|
|
|
@@ -2688,7 +2769,7 @@ describe('admin keys (TIP-1049)', () => {
|
|
|
2688
2769
|
)
|
|
2689
2770
|
const rpc = KeyAuthorization.toRpc(authorization)
|
|
2690
2771
|
const restored = KeyAuthorization.fromRpc({ ...rpc, account })
|
|
2691
|
-
expect(restored.isAdmin).
|
|
2772
|
+
expect(restored.isAdmin).toBeUndefined()
|
|
2692
2773
|
expect(restored.account).toBe(account)
|
|
2693
2774
|
})
|
|
2694
2775
|
|
|
@@ -3,7 +3,11 @@ import type * as Address from '../core/Address.js'
|
|
|
3
3
|
import type * as Errors from '../core/Errors.js'
|
|
4
4
|
import * as Hash from '../core/Hash.js'
|
|
5
5
|
import * as Hex from '../core/Hex.js'
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
Compute,
|
|
8
|
+
PartialBy,
|
|
9
|
+
UnionPartialBy,
|
|
10
|
+
} from '../core/internal/types.js'
|
|
7
11
|
import * as Rlp from '../core/Rlp.js'
|
|
8
12
|
import * as SignatureEnvelope from './SignatureEnvelope.js'
|
|
9
13
|
import * as TempoAddress from './TempoAddress.js'
|
|
@@ -25,7 +29,7 @@ import * as TempoAddress from './TempoAddress.js'
|
|
|
25
29
|
* - `chainId`: Chain ID for replay protection (0 = valid on any chain)
|
|
26
30
|
* - `expiry`: Unix timestamp when the key expires (undefined = never expires)
|
|
27
31
|
* - `limits`: Per-TIP-20 token spending limits (only applies to `transfer()` and `approve()` calls)
|
|
28
|
-
* - `type`: Key type (`secp256k1`, `p256`, or `
|
|
32
|
+
* - `type`: Key type (`secp256k1`, `p256`, `webAuthn`, or `multisig`)
|
|
29
33
|
*
|
|
30
34
|
* [Access Keys Specification](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#access-keys)
|
|
31
35
|
*/
|
|
@@ -41,6 +45,8 @@ export type KeyAuthorization<
|
|
|
41
45
|
chainId: bigintType
|
|
42
46
|
/** Unix timestamp when key expires (undefined = never expires). */
|
|
43
47
|
expiry?: numberType | null | undefined
|
|
48
|
+
/** Whether this authorization provisions an admin access key. */
|
|
49
|
+
isAdmin?: boolean | undefined
|
|
44
50
|
/** TIP20 spending limits for this key. */
|
|
45
51
|
limits?:
|
|
46
52
|
| readonly TokenLimit<bigintType, numberType, addressType>[]
|
|
@@ -53,8 +59,6 @@ export type KeyAuthorization<
|
|
|
53
59
|
* - `[...]` = only listed contract+selector combinations allowed
|
|
54
60
|
*/
|
|
55
61
|
scopes?: readonly Scope<addressType>[] | undefined
|
|
56
|
-
/** Key type. (secp256k1, P256, WebAuthn). */
|
|
57
|
-
type: SignatureEnvelope.Type
|
|
58
62
|
/**
|
|
59
63
|
* Optional 32-byte witness bound into the signing hash.
|
|
60
64
|
*
|
|
@@ -65,32 +69,37 @@ export type KeyAuthorization<
|
|
|
65
69
|
* [TIP-1053 Specification](https://tips.sh/1053)
|
|
66
70
|
*/
|
|
67
71
|
witness?: Hex.Hex | undefined
|
|
68
|
-
} &
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
} & (
|
|
73
|
+
| {
|
|
74
|
+
/** Optional account address binding. */
|
|
75
|
+
account?: addressType | undefined
|
|
76
|
+
/** Primitive key type. */
|
|
77
|
+
type: SignatureEnvelope.Type
|
|
78
|
+
}
|
|
75
79
|
| {
|
|
76
|
-
/**
|
|
80
|
+
/** Required parent account binding for a multisig grant. */
|
|
77
81
|
account: addressType
|
|
78
|
-
/**
|
|
79
|
-
|
|
82
|
+
/** Multisig key type. */
|
|
83
|
+
type: 'multisig'
|
|
80
84
|
}
|
|
81
|
-
|
|
82
|
-
> &
|
|
85
|
+
) &
|
|
83
86
|
(signed extends true
|
|
84
|
-
? {
|
|
87
|
+
? {
|
|
88
|
+
signature:
|
|
89
|
+
| SignatureEnvelope.Primitive<bigintType, numberType>
|
|
90
|
+
| SignatureEnvelope.Multisig<bigintType, numberType>
|
|
91
|
+
}
|
|
85
92
|
: {
|
|
86
|
-
signature?:
|
|
93
|
+
signature?:
|
|
94
|
+
| SignatureEnvelope.Primitive<bigintType, numberType>
|
|
95
|
+
| SignatureEnvelope.Multisig<bigintType, numberType>
|
|
96
|
+
| undefined
|
|
87
97
|
})
|
|
88
98
|
|
|
89
99
|
/** Signature that can authorize an access key. */
|
|
90
|
-
export type Signature<bigintType = bigint, numberType = number> =
|
|
100
|
+
export type Signature<bigintType = bigint, numberType = number> =
|
|
91
101
|
| SignatureEnvelope.Primitive<bigintType, numberType>
|
|
92
102
|
| SignatureEnvelope.Multisig<bigintType, numberType>
|
|
93
|
-
>
|
|
94
103
|
|
|
95
104
|
/** RPC-formatted signature that can authorize an access key. */
|
|
96
105
|
export type SignatureRpc =
|
|
@@ -107,8 +116,6 @@ export type Input = KeyAuthorization<
|
|
|
107
116
|
|
|
108
117
|
/** RPC representation matching the node's wire format. */
|
|
109
118
|
export type Rpc = {
|
|
110
|
-
/** Optional account address binding (TIP-1049). */
|
|
111
|
-
account?: Address.Address | null | undefined
|
|
112
119
|
/** Allowed call scopes (node field: `allowedCalls`). */
|
|
113
120
|
allowedCalls?: readonly RpcCallScope[] | undefined
|
|
114
121
|
/** Chain ID (hex quantity). */
|
|
@@ -119,33 +126,44 @@ export type Rpc = {
|
|
|
119
126
|
isAdmin?: boolean | null | undefined
|
|
120
127
|
/** Key identifier. */
|
|
121
128
|
keyId: Address.Address
|
|
122
|
-
/** Key type. */
|
|
123
|
-
keyType: SignatureEnvelope.Type
|
|
124
129
|
/** Token spending limits. */
|
|
125
130
|
limits?: readonly RpcTokenLimit[] | undefined
|
|
126
131
|
/** Signature authorizing this key. */
|
|
127
132
|
signature: SignatureRpc
|
|
128
133
|
/** Optional 32-byte witness (hex). */
|
|
129
134
|
witness?: Hex.Hex | null | undefined
|
|
130
|
-
}
|
|
135
|
+
} & (
|
|
136
|
+
| {
|
|
137
|
+
/** Optional account address binding (TIP-1049). */
|
|
138
|
+
account?: Address.Address | null | undefined
|
|
139
|
+
/** Primitive key type. */
|
|
140
|
+
keyType: SignatureEnvelope.Type
|
|
141
|
+
}
|
|
142
|
+
| {
|
|
143
|
+
/** Required parent account binding for a multisig grant. */
|
|
144
|
+
account: Address.Address
|
|
145
|
+
/** Multisig key type. */
|
|
146
|
+
keyType: 'multisig'
|
|
147
|
+
}
|
|
148
|
+
)
|
|
131
149
|
|
|
132
150
|
/** RPC representation of a token limit (matches node's `TokenLimit` serde). */
|
|
133
151
|
export type RpcTokenLimit = {
|
|
134
|
-
token: Address.Address
|
|
135
152
|
limit: Hex.Hex
|
|
136
153
|
period?: Hex.Hex | undefined
|
|
154
|
+
token: Address.Address
|
|
137
155
|
}
|
|
138
156
|
|
|
139
157
|
/** RPC representation of a call scope (matches node's `CallScope` serde). */
|
|
140
158
|
export type RpcCallScope = {
|
|
159
|
+
selectorRules?: readonly RpcSelectorRule[] | undefined
|
|
141
160
|
target: Address.Address
|
|
142
|
-
selectorRules?: readonly RpcSelectorRule[]
|
|
143
161
|
}
|
|
144
162
|
|
|
145
163
|
/** RPC representation of a selector rule (matches node's `SelectorRule` serde). */
|
|
146
164
|
export type RpcSelectorRule = {
|
|
165
|
+
recipients?: readonly Address.Address[] | undefined
|
|
147
166
|
selector: Hex.Hex
|
|
148
|
-
recipients?: readonly Address.Address[]
|
|
149
167
|
}
|
|
150
168
|
|
|
151
169
|
/** Signed representation of a Key Authorization. */
|
|
@@ -156,10 +174,11 @@ export type Signed<
|
|
|
156
174
|
> = KeyAuthorization<true, bigintType, numberType, addressType>
|
|
157
175
|
|
|
158
176
|
type SignatureValue =
|
|
159
|
-
|
|
|
177
|
+
| SignatureEnvelope.from.MultisigFromConfig
|
|
178
|
+
| UnionPartialBy<SignatureEnvelope.Primitive, 'prehash' | 'type'>
|
|
179
|
+
| PartialBy<SignatureEnvelope.Multisig, 'type'>
|
|
160
180
|
| SignatureEnvelope.Secp256k1Flat
|
|
161
181
|
| SignatureEnvelope.Serialized
|
|
162
|
-
| SignatureEnvelope.from.MultisigFromConfig
|
|
163
182
|
|
|
164
183
|
type BaseTuple = readonly [
|
|
165
184
|
chainId: Hex.Hex,
|
|
@@ -235,14 +254,6 @@ export type Tuple<signed extends boolean = boolean> = signed extends true
|
|
|
235
254
|
export type Scope<addressType = Address.Address> = {
|
|
236
255
|
/** Target contract address. */
|
|
237
256
|
address: addressType
|
|
238
|
-
/**
|
|
239
|
-
* 4-byte function selector, or a human-readable ABI signature
|
|
240
|
-
* (e.g. `'transfer(address,uint256)'` or `'function transfer(address,uint256)'`).
|
|
241
|
-
*
|
|
242
|
-
* Signatures are encoded into a 4-byte selector automatically.
|
|
243
|
-
* Omit to allow any selector on this contract.
|
|
244
|
-
*/
|
|
245
|
-
selector?: Hex.Hex | string | undefined
|
|
246
257
|
/**
|
|
247
258
|
* Recipient allowlist for this selector (first ABI `address` argument).
|
|
248
259
|
*
|
|
@@ -252,6 +263,8 @@ export type Scope<addressType = Address.Address> = {
|
|
|
252
263
|
* Only valid for constrained selectors: `transfer`, `approve`, `transferWithMemo`.
|
|
253
264
|
*/
|
|
254
265
|
recipients?: readonly addressType[] | undefined
|
|
266
|
+
/** Function selector or human-readable ABI signature. */
|
|
267
|
+
selector?: Hex.Hex | string | undefined
|
|
255
268
|
}
|
|
256
269
|
|
|
257
270
|
/**
|
|
@@ -267,8 +280,6 @@ export type TokenLimit<
|
|
|
267
280
|
numberType = number,
|
|
268
281
|
addressType = Address.Address,
|
|
269
282
|
> = {
|
|
270
|
-
/** Address of the TIP-20 token. */
|
|
271
|
-
token: addressType
|
|
272
283
|
/** Maximum spending amount for this token (enforced over the key's lifetime, or per period if `period` \> 0). */
|
|
273
284
|
limit: bigintType
|
|
274
285
|
/**
|
|
@@ -278,6 +289,8 @@ export type TokenLimit<
|
|
|
278
289
|
* - `\> 0` = periodic limit that resets every `period` seconds
|
|
279
290
|
*/
|
|
280
291
|
period?: numberType | undefined
|
|
292
|
+
/** Address of the TIP-20 token. */
|
|
293
|
+
token: addressType
|
|
281
294
|
}
|
|
282
295
|
|
|
283
296
|
/**
|
|
@@ -424,6 +437,7 @@ export function from<
|
|
|
424
437
|
options: from.Options<signature> = {},
|
|
425
438
|
): from.ReturnType<authorization, signature> {
|
|
426
439
|
if ('keyId' in authorization) return fromRpc(authorization as Rpc) as never
|
|
440
|
+
assertAccountBinding(authorization.type, authorization.account)
|
|
427
441
|
const auth = authorization as KeyAuthorization & {
|
|
428
442
|
limits?: readonly { token: TempoAddress.Address; limit: bigint }[]
|
|
429
443
|
scopes?: readonly {
|
|
@@ -436,6 +450,9 @@ export function from<
|
|
|
436
450
|
if (auth.signature) assertSignature(auth.signature)
|
|
437
451
|
const resolved = {
|
|
438
452
|
...auth,
|
|
453
|
+
...(auth.account !== undefined
|
|
454
|
+
? { account: TempoAddress.resolve(auth.account) }
|
|
455
|
+
: {}),
|
|
439
456
|
address: TempoAddress.resolve(auth.address as TempoAddress.Address),
|
|
440
457
|
...(auth.limits
|
|
441
458
|
? {
|
|
@@ -479,8 +496,12 @@ export declare namespace from {
|
|
|
479
496
|
type Options<
|
|
480
497
|
signature extends SignatureValue | undefined = SignatureValue | undefined,
|
|
481
498
|
> = {
|
|
482
|
-
/** The signature to attach to the Key Authorization. */
|
|
483
|
-
signature?:
|
|
499
|
+
/** The primitive or multisig signature to attach to the Key Authorization. */
|
|
500
|
+
signature?:
|
|
501
|
+
| signature
|
|
502
|
+
| SignatureEnvelope.Primitive
|
|
503
|
+
| SignatureEnvelope.Multisig
|
|
504
|
+
| undefined
|
|
484
505
|
}
|
|
485
506
|
|
|
486
507
|
type ReturnType<
|
|
@@ -502,7 +523,10 @@ export declare namespace from {
|
|
|
502
523
|
>
|
|
503
524
|
>
|
|
504
525
|
|
|
505
|
-
type ErrorType =
|
|
526
|
+
type ErrorType =
|
|
527
|
+
| InvalidSignatureTypeError
|
|
528
|
+
| MissingAccountError
|
|
529
|
+
| Errors.GlobalErrorType
|
|
506
530
|
}
|
|
507
531
|
|
|
508
532
|
/**
|
|
@@ -536,6 +560,7 @@ export function fromRpc(authorization: Rpc): Signed {
|
|
|
536
560
|
const witness = authorization.witness ?? undefined
|
|
537
561
|
const isAdmin = authorization.isAdmin ?? undefined
|
|
538
562
|
const account = authorization.account ?? undefined
|
|
563
|
+
assertAccountBinding(keyType, account)
|
|
539
564
|
const signature = SignatureEnvelope.fromRpc(authorization.signature)
|
|
540
565
|
assertSignature(signature)
|
|
541
566
|
if (witness !== undefined) assertWitness(witness)
|
|
@@ -557,9 +582,6 @@ export function fromRpc(authorization: Rpc): Signed {
|
|
|
557
582
|
})
|
|
558
583
|
: undefined
|
|
559
584
|
|
|
560
|
-
const accountBinding =
|
|
561
|
-
account !== undefined ? { account, isAdmin: isAdmin ?? false } : {}
|
|
562
|
-
|
|
563
585
|
return {
|
|
564
586
|
address: keyId,
|
|
565
587
|
chainId: chainId === '0x' ? 0n : Hex.toBigInt(chainId),
|
|
@@ -573,14 +595,20 @@ export function fromRpc(authorization: Rpc): Signed {
|
|
|
573
595
|
})),
|
|
574
596
|
...(scopes ? { scopes } : {}),
|
|
575
597
|
signature,
|
|
576
|
-
|
|
598
|
+
...(keyType === 'multisig'
|
|
599
|
+
? { account: account!, type: keyType }
|
|
600
|
+
: { type: keyType }),
|
|
577
601
|
...(witness !== undefined ? { witness } : {}),
|
|
578
|
-
...
|
|
602
|
+
...(account !== undefined ? { account } : {}),
|
|
603
|
+
...(isAdmin ? { isAdmin: true as const } : {}),
|
|
579
604
|
}
|
|
580
605
|
}
|
|
581
606
|
|
|
582
607
|
export declare namespace fromRpc {
|
|
583
|
-
type ErrorType =
|
|
608
|
+
type ErrorType =
|
|
609
|
+
| InvalidSignatureTypeError
|
|
610
|
+
| MissingAccountError
|
|
611
|
+
| Errors.GlobalErrorType
|
|
584
612
|
}
|
|
585
613
|
|
|
586
614
|
/**
|
|
@@ -642,6 +670,8 @@ export function fromTuple<const tuple extends Tuple>(
|
|
|
642
670
|
return 'p256'
|
|
643
671
|
case '0x02':
|
|
644
672
|
return 'webAuthn'
|
|
673
|
+
case '0x03':
|
|
674
|
+
return 'multisig'
|
|
645
675
|
default:
|
|
646
676
|
throw new Error(`Invalid key type: ${keyType_hex}`)
|
|
647
677
|
}
|
|
@@ -697,17 +727,19 @@ export function fromTuple<const tuple extends Tuple>(
|
|
|
697
727
|
const account = isAbsent(rawAccount)
|
|
698
728
|
? undefined
|
|
699
729
|
: (rawAccount as Address.Address)
|
|
700
|
-
|
|
701
|
-
account !== undefined ? { account, isAdmin: isAdmin ?? false } : {}
|
|
730
|
+
assertAccountBinding(keyType, account)
|
|
702
731
|
const args: KeyAuthorization = {
|
|
703
732
|
address: keyId,
|
|
704
733
|
chainId: chainId === '0x' ? 0n : Hex.toBigInt(chainId),
|
|
705
|
-
|
|
734
|
+
...(keyType === 'multisig'
|
|
735
|
+
? { account: account!, type: keyType }
|
|
736
|
+
: { type: keyType }),
|
|
706
737
|
...(expiry !== undefined ? { expiry } : {}),
|
|
707
738
|
...(limits !== undefined ? { limits } : {}),
|
|
708
739
|
...(scopes !== undefined ? { scopes } : {}),
|
|
709
740
|
...(witness !== undefined ? { witness } : {}),
|
|
710
|
-
...
|
|
741
|
+
...(account !== undefined ? { account } : {}),
|
|
742
|
+
...(isAdmin ? { isAdmin: true as const } : {}),
|
|
711
743
|
}
|
|
712
744
|
if (signatureSerialized) {
|
|
713
745
|
const signature = SignatureEnvelope.deserialize(signatureSerialized)
|
|
@@ -722,7 +754,10 @@ export declare namespace fromTuple {
|
|
|
722
754
|
KeyAuthorization<authorization extends Tuple<true> ? true : false>
|
|
723
755
|
>
|
|
724
756
|
|
|
725
|
-
type ErrorType =
|
|
757
|
+
type ErrorType =
|
|
758
|
+
| InvalidSignatureTypeError
|
|
759
|
+
| MissingAccountError
|
|
760
|
+
| Errors.GlobalErrorType
|
|
726
761
|
}
|
|
727
762
|
|
|
728
763
|
/**
|
|
@@ -914,6 +949,7 @@ export declare namespace serialize {
|
|
|
914
949
|
* @returns An RPC-formatted Key Authorization.
|
|
915
950
|
*/
|
|
916
951
|
export function toRpc(authorization: Signed): Rpc {
|
|
952
|
+
assertAccountBinding(authorization.type, authorization.account)
|
|
917
953
|
const {
|
|
918
954
|
address,
|
|
919
955
|
scopes,
|
|
@@ -957,13 +993,15 @@ export function toRpc(authorization: Signed): Rpc {
|
|
|
957
993
|
chainId: chainId === 0n ? '0x' : Hex.fromNumber(chainId),
|
|
958
994
|
expiry: typeof expiry === 'number' ? Hex.fromNumber(expiry) : null,
|
|
959
995
|
keyId: TempoAddress.resolve(address),
|
|
960
|
-
keyType: type,
|
|
996
|
+
...(type === 'multisig' ? { account, keyType: type } : { keyType: type }),
|
|
961
997
|
limits: limits?.map(({ token, limit, period }) => ({
|
|
962
998
|
token,
|
|
963
999
|
limit: Hex.fromNumber(limit),
|
|
964
1000
|
...(period ? { period: numberToHex(period) } : {}),
|
|
965
1001
|
})),
|
|
966
|
-
signature: SignatureEnvelope.toRpc(signature) as
|
|
1002
|
+
signature: SignatureEnvelope.toRpc(signature) as
|
|
1003
|
+
| SignatureEnvelope.PrimitiveRpc
|
|
1004
|
+
| SignatureEnvelope.MultisigRpc,
|
|
967
1005
|
...(allowedCalls ? { allowedCalls } : {}),
|
|
968
1006
|
...(witness !== undefined ? { witness } : {}),
|
|
969
1007
|
...(isAdmin ? { isAdmin: true } : {}),
|
|
@@ -972,7 +1010,10 @@ export function toRpc(authorization: Signed): Rpc {
|
|
|
972
1010
|
}
|
|
973
1011
|
|
|
974
1012
|
export declare namespace toRpc {
|
|
975
|
-
type ErrorType =
|
|
1013
|
+
type ErrorType =
|
|
1014
|
+
| InvalidSignatureTypeError
|
|
1015
|
+
| MissingAccountError
|
|
1016
|
+
| Errors.GlobalErrorType
|
|
976
1017
|
}
|
|
977
1018
|
|
|
978
1019
|
/**
|
|
@@ -1009,6 +1050,7 @@ export declare namespace toRpc {
|
|
|
1009
1050
|
export function toTuple<const authorization extends KeyAuthorization>(
|
|
1010
1051
|
authorization: authorization,
|
|
1011
1052
|
): toTuple.ReturnType<authorization> {
|
|
1053
|
+
assertAccountBinding(authorization.type, authorization.account)
|
|
1012
1054
|
const {
|
|
1013
1055
|
address,
|
|
1014
1056
|
chainId,
|
|
@@ -1025,16 +1067,19 @@ export function toTuple<const authorization extends KeyAuthorization>(
|
|
|
1025
1067
|
assertSignature(authorization.signature)
|
|
1026
1068
|
return SignatureEnvelope.serialize(authorization.signature)
|
|
1027
1069
|
})()
|
|
1070
|
+
const keyType = authorization.type
|
|
1028
1071
|
const type = (() => {
|
|
1029
|
-
switch (
|
|
1072
|
+
switch (keyType) {
|
|
1030
1073
|
case 'secp256k1':
|
|
1031
1074
|
return '0x'
|
|
1032
1075
|
case 'p256':
|
|
1033
1076
|
return '0x01'
|
|
1034
1077
|
case 'webAuthn':
|
|
1035
1078
|
return '0x02'
|
|
1079
|
+
case 'multisig':
|
|
1080
|
+
return '0x03'
|
|
1036
1081
|
default:
|
|
1037
|
-
throw new Error(`Invalid key type: ${
|
|
1082
|
+
throw new Error(`Invalid key type: ${keyType}`)
|
|
1038
1083
|
}
|
|
1039
1084
|
})()
|
|
1040
1085
|
const limitsValue = limits?.map((limit) => {
|
|
@@ -1083,7 +1128,7 @@ export function toTuple<const authorization extends KeyAuthorization>(
|
|
|
1083
1128
|
// single entry to this list with `placeholder: '0x'`.
|
|
1084
1129
|
const hasTip1053Plus =
|
|
1085
1130
|
witness !== undefined || isAdmin || account !== undefined
|
|
1086
|
-
const optionals: readonly {
|
|
1131
|
+
const optionals: readonly { placeholder: unknown; value: unknown }[] = [
|
|
1087
1132
|
{
|
|
1088
1133
|
value:
|
|
1089
1134
|
expiry !== null && expiry !== undefined && expiry !== 0
|
|
@@ -1120,7 +1165,10 @@ export declare namespace toTuple {
|
|
|
1120
1165
|
type ReturnType<authorization extends KeyAuthorization = KeyAuthorization> =
|
|
1121
1166
|
Compute<Tuple<authorization extends KeyAuthorization<true> ? true : false>>
|
|
1122
1167
|
|
|
1123
|
-
type ErrorType =
|
|
1168
|
+
type ErrorType =
|
|
1169
|
+
| InvalidSignatureTypeError
|
|
1170
|
+
| MissingAccountError
|
|
1171
|
+
| Errors.GlobalErrorType
|
|
1124
1172
|
}
|
|
1125
1173
|
|
|
1126
1174
|
function bigintToHex(value: bigint): Hex.Hex {
|
|
@@ -1154,6 +1202,13 @@ function resolveSelector(
|
|
|
1154
1202
|
return AbiItem.getSelector(selector)
|
|
1155
1203
|
}
|
|
1156
1204
|
|
|
1205
|
+
function assertAccountBinding(
|
|
1206
|
+
type: KeyAuthorization['type'],
|
|
1207
|
+
account: TempoAddress.Address | null | undefined,
|
|
1208
|
+
): void {
|
|
1209
|
+
if (type === 'multisig' && account == null) throw new MissingAccountError()
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1157
1212
|
function assertWitness(witness: Hex.Hex): void {
|
|
1158
1213
|
if (Hex.size(witness) !== 32) throw new InvalidWitnessSizeError(witness)
|
|
1159
1214
|
}
|
|
@@ -1198,3 +1253,11 @@ export class InvalidSignatureTypeError extends Error {
|
|
|
1198
1253
|
)
|
|
1199
1254
|
}
|
|
1200
1255
|
}
|
|
1256
|
+
|
|
1257
|
+
/** Thrown when a multisig key grant omits its parent account binding. */
|
|
1258
|
+
export class MissingAccountError extends Error {
|
|
1259
|
+
override readonly name = 'KeyAuthorization.MissingAccountError'
|
|
1260
|
+
constructor() {
|
|
1261
|
+
super('Multisig key grants require a parent account binding.')
|
|
1262
|
+
}
|
|
1263
|
+
}
|