ox 1.7.2 → 1.7.4
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 +27 -0
- package/dist/tempo/KeyAuthorization.d.ts +1 -1
- package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/MultisigOperation.d.ts +42 -26
- package/dist/tempo/MultisigOperation.d.ts.map +1 -1
- package/dist/tempo/MultisigOperation.js +78 -48
- package/dist/tempo/MultisigOperation.js.map +1 -1
- package/dist/tempo/MultisigSimulation.d.ts +132 -0
- package/dist/tempo/MultisigSimulation.d.ts.map +1 -0
- package/dist/tempo/MultisigSimulation.js +140 -0
- package/dist/tempo/MultisigSimulation.js.map +1 -0
- package/dist/tempo/RpcSchemaTempo.d.ts +20 -10
- package/dist/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.d.ts +9 -20
- package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.js +12 -18
- package/dist/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/tempo/TransactionRequest.d.ts +6 -6
- package/dist/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/tempo/TransactionRequest.js +7 -7
- package/dist/tempo/TransactionRequest.js.map +1 -1
- package/dist/tempo/index.d.ts +4 -4
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +4 -4
- package/dist/tempo/index.js.map +1 -1
- package/dist/zod/tempo/AuthorizationTempo.d.ts +10 -130
- package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
- package/dist/zod/tempo/KeyAuthorization.d.ts +5 -65
- package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/zod/tempo/MultisigOperation.d.ts +0 -10
- package/dist/zod/tempo/MultisigOperation.d.ts.map +1 -1
- package/dist/zod/tempo/MultisigOperation.js +0 -3
- package/dist/zod/tempo/MultisigOperation.js.map +1 -1
- package/dist/zod/tempo/{MultisigWitness.d.ts → MultisigSimulation.d.ts} +17 -49
- package/dist/zod/tempo/MultisigSimulation.d.ts.map +1 -0
- package/dist/zod/tempo/{MultisigWitness.js → MultisigSimulation.js} +26 -24
- package/dist/zod/tempo/MultisigSimulation.js.map +1 -0
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +83 -204
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.js +19 -10
- package/dist/zod/tempo/RpcSchemaTempo.js.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.d.ts +3 -39
- package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.js +4 -14
- package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +18 -234
- package/dist/zod/tempo/Transaction.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.d.ts +36 -192
- package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.js +10 -10
- package/dist/zod/tempo/TransactionRequest.js.map +1 -1
- package/dist/zod/tempo/z.d.ts +1 -1
- package/dist/zod/tempo/z.d.ts.map +1 -1
- package/dist/zod/tempo/z.js +1 -1
- package/dist/zod/tempo/z.js.map +1 -1
- package/package.json +9 -9
- package/src/tempo/AuthorizationTempo.test.ts +4 -0
- package/src/tempo/KeyAuthorization.test-d.ts +1 -12
- package/src/tempo/KeyAuthorization.test.ts +44 -3
- package/src/tempo/KeyAuthorization.ts +3 -3
- package/src/tempo/MultisigOperation.test-d.ts +8 -6
- package/src/tempo/MultisigOperation.test.ts +236 -169
- package/src/tempo/MultisigOperation.ts +122 -83
- package/src/tempo/MultisigSimulation.test-d.ts +33 -0
- package/src/tempo/MultisigSimulation.test.ts +283 -0
- package/src/tempo/MultisigSimulation.ts +266 -0
- package/src/tempo/RpcSchemaTempo.test-d.ts +45 -10
- package/src/tempo/RpcSchemaTempo.ts +15 -6
- package/src/tempo/SignatureEnvelope.test-d.ts +3 -59
- package/src/tempo/SignatureEnvelope.test.ts +61 -51
- package/src/tempo/SignatureEnvelope.ts +27 -44
- package/src/tempo/Transaction.test.ts +61 -0
- package/src/tempo/TransactionRequest.test-d.ts +7 -7
- package/src/tempo/TransactionRequest.test.ts +129 -208
- package/src/tempo/TransactionRequest.ts +16 -12
- package/src/tempo/index.ts +4 -4
- package/src/tempo/multisig.e2e.test.ts +51 -1
- package/src/version.ts +1 -1
- package/src/zod/tempo/MultisigOperation.ts +0 -3
- package/src/zod/tempo/{MultisigWitness.ts → MultisigSimulation.ts} +39 -42
- package/src/zod/tempo/RpcSchemaTempo.ts +20 -10
- package/src/zod/tempo/SignatureEnvelope.ts +11 -35
- package/src/zod/tempo/TransactionRequest.ts +14 -14
- package/src/zod/tempo/_test/KeyAuthorization.test.ts +2 -15
- package/src/zod/tempo/_test/MultisigOperation.test.ts +0 -2
- package/src/zod/tempo/_test/MultisigSimulation.test-d.ts +13 -0
- package/src/zod/tempo/_test/MultisigSimulation.test.ts +78 -0
- package/src/zod/tempo/_test/RpcSchemaTempo.test-d.ts +19 -2
- package/src/zod/tempo/_test/RpcSchemaTempo.test.ts +46 -4
- package/src/zod/tempo/_test/SignatureEnvelope.test.ts +58 -112
- package/src/zod/tempo/_test/TransactionRequest.test.ts +19 -160
- package/src/zod/tempo/z.ts +1 -1
- package/dist/tempo/MultisigWitness.d.ts +0 -106
- package/dist/tempo/MultisigWitness.d.ts.map +0 -1
- package/dist/tempo/MultisigWitness.js +0 -118
- package/dist/tempo/MultisigWitness.js.map +0 -1
- package/dist/zod/tempo/MultisigWitness.d.ts.map +0 -1
- package/dist/zod/tempo/MultisigWitness.js.map +0 -1
- package/src/tempo/MultisigWitness.test-d.ts +0 -42
- package/src/tempo/MultisigWitness.test.ts +0 -288
- package/src/tempo/MultisigWitness.ts +0 -210
- package/src/zod/tempo/_test/MultisigWitness.test-d.ts +0 -13
- package/src/zod/tempo/_test/MultisigWitness.test.ts +0 -149
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import * as Address from '../core/Address.js'
|
|
2
|
+
import * as Errors from '../core/Errors.js'
|
|
3
|
+
import * as Hex from '../core/Hex.js'
|
|
4
|
+
import type { Compute } from '../core/internal/types.js'
|
|
5
|
+
import * as Rlp from '../core/Rlp.js'
|
|
6
|
+
import * as MultisigConfig from './MultisigConfig.js'
|
|
7
|
+
import type * as SignatureEnvelope from './SignatureEnvelope.js'
|
|
8
|
+
|
|
9
|
+
/** Native multisig owner approval used for RPC simulation. */
|
|
10
|
+
export type Approval = NestedApproval | PrimitiveApproval
|
|
11
|
+
|
|
12
|
+
/** JSON-RPC native multisig owner approval used for RPC simulation. */
|
|
13
|
+
export type ApprovalRpc = NestedApprovalRpc | PrimitiveApproval
|
|
14
|
+
|
|
15
|
+
/** Nested native multisig owner approval used for RPC simulation. */
|
|
16
|
+
export type NestedApproval = {
|
|
17
|
+
/** Depth-2 multisig simulation spec. */
|
|
18
|
+
spec: NestedSpec
|
|
19
|
+
/** Approval type. */
|
|
20
|
+
type: 'multisig'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** JSON-RPC nested native multisig owner approval used for RPC simulation. */
|
|
24
|
+
export type NestedApprovalRpc = {
|
|
25
|
+
/** Depth-2 multisig simulation spec. */
|
|
26
|
+
spec: NestedSpecRpc
|
|
27
|
+
/** Approval type. */
|
|
28
|
+
type: 'multisig'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Primitive approval in a depth-2 multisig simulation spec. */
|
|
32
|
+
export type NestedPrimitiveApproval = {
|
|
33
|
+
/** Optional signature-specific gas-estimation data. */
|
|
34
|
+
keyData?: Hex.Hex | undefined
|
|
35
|
+
/** Signature type to model. Omission uses a maximum-size WebAuthn signature. */
|
|
36
|
+
keyType?: SignatureEnvelope.Type | undefined
|
|
37
|
+
/** Configured owner address. */
|
|
38
|
+
owner: Address.Address
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Depth-2 native multisig spec used for RPC simulation. */
|
|
42
|
+
export type NestedSpec = {
|
|
43
|
+
/** Nested multisig account. */
|
|
44
|
+
account: Address.Address
|
|
45
|
+
/** Primitive owner approvals to model. */
|
|
46
|
+
approvals: readonly NestedPrimitiveApproval[]
|
|
47
|
+
/** Complete applicable configuration. */
|
|
48
|
+
config: MultisigConfig.Config
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** JSON-RPC depth-2 native multisig spec used for RPC simulation. */
|
|
52
|
+
export type NestedSpecRpc = {
|
|
53
|
+
/** Nested multisig account. */
|
|
54
|
+
account: Address.Address
|
|
55
|
+
/** Primitive owner approvals to model. */
|
|
56
|
+
approvals: readonly NestedPrimitiveApproval[]
|
|
57
|
+
/** Canonical RLP-encoded applicable configuration. */
|
|
58
|
+
config: Hex.Hex
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Primitive owner approval used for RPC simulation. */
|
|
62
|
+
export type PrimitiveApproval = {
|
|
63
|
+
/** Optional signature-specific gas-estimation data. */
|
|
64
|
+
keyData?: Hex.Hex | undefined
|
|
65
|
+
/** Signature type to model. Omission uses a maximum-size WebAuthn signature. */
|
|
66
|
+
keyType?: SignatureEnvelope.Type | undefined
|
|
67
|
+
/** Configured owner address. */
|
|
68
|
+
owner: Address.Address
|
|
69
|
+
/** Approval type. */
|
|
70
|
+
type: 'primitive'
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** JSON-RPC representation of a native multisig simulation spec. */
|
|
74
|
+
export type Rpc = Compute<{
|
|
75
|
+
/** Account authorized by this spec. */
|
|
76
|
+
account: Address.Address
|
|
77
|
+
/** Owner approvals to model. */
|
|
78
|
+
approvals: readonly ApprovalRpc[]
|
|
79
|
+
/** Canonical RLP-encoded applicable configuration. */
|
|
80
|
+
config: Hex.Hex
|
|
81
|
+
}>
|
|
82
|
+
|
|
83
|
+
/** Native multisig spec used to construct an RPC simulation signature. */
|
|
84
|
+
export type Spec = Compute<{
|
|
85
|
+
/** Account authorized by this spec. */
|
|
86
|
+
account: Address.Address
|
|
87
|
+
/** Owner approvals to model. */
|
|
88
|
+
approvals: readonly Approval[]
|
|
89
|
+
/** Complete applicable configuration. */
|
|
90
|
+
config: MultisigConfig.Config
|
|
91
|
+
}>
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Converts a JSON-RPC multisig simulation spec to its domain representation.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts twoslash
|
|
98
|
+
* // @noErrors
|
|
99
|
+
* import { MultisigSimulation } from 'ox/tempo'
|
|
100
|
+
*
|
|
101
|
+
* const spec = MultisigSimulation.fromRpc(specRpc)
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @param spec - JSON-RPC multisig simulation spec.
|
|
105
|
+
* @returns The multisig simulation spec with decoded configurations.
|
|
106
|
+
*/
|
|
107
|
+
export function fromRpc(spec: Rpc): Spec {
|
|
108
|
+
const { account, approvals, config } = spec
|
|
109
|
+
assertApprovalCount(approvals)
|
|
110
|
+
return {
|
|
111
|
+
account,
|
|
112
|
+
approvals: approvals.map((approval) => {
|
|
113
|
+
if (approval.type === 'primitive') return approval
|
|
114
|
+
assertApprovalCount(approval.spec.approvals)
|
|
115
|
+
return {
|
|
116
|
+
spec: {
|
|
117
|
+
account: approval.spec.account,
|
|
118
|
+
approvals: approval.spec.approvals,
|
|
119
|
+
config: deserializeConfig(approval.spec.config),
|
|
120
|
+
},
|
|
121
|
+
type: 'multisig',
|
|
122
|
+
}
|
|
123
|
+
}),
|
|
124
|
+
config: deserializeConfig(config),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export declare namespace fromRpc {
|
|
129
|
+
/** Error type for `fromRpc`. */
|
|
130
|
+
export type ErrorType =
|
|
131
|
+
| Hex.isEqual.ErrorType
|
|
132
|
+
| InvalidSimulationError
|
|
133
|
+
| MultisigConfig.assert.ErrorType
|
|
134
|
+
| Rlp.fromHex.ErrorType
|
|
135
|
+
| Rlp.toHex.ErrorType
|
|
136
|
+
| Errors.GlobalErrorType
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Converts a multisig simulation spec to its JSON-RPC representation.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts twoslash
|
|
144
|
+
* // @noErrors
|
|
145
|
+
* import { MultisigSimulation } from 'ox/tempo'
|
|
146
|
+
*
|
|
147
|
+
* const specRpc = MultisigSimulation.toRpc(spec)
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* @param spec - Multisig simulation spec.
|
|
151
|
+
* @returns The JSON-RPC multisig simulation spec with encoded configurations.
|
|
152
|
+
*/
|
|
153
|
+
export function toRpc(spec: Spec): Rpc {
|
|
154
|
+
const { account, approvals, config } = spec
|
|
155
|
+
assertApprovalCount(approvals)
|
|
156
|
+
return {
|
|
157
|
+
account,
|
|
158
|
+
approvals: approvals.map((approval) => {
|
|
159
|
+
if (approval.type === 'primitive')
|
|
160
|
+
return {
|
|
161
|
+
...approval,
|
|
162
|
+
...(typeof approval.keyData !== 'undefined'
|
|
163
|
+
? { keyData: shimKeyData(approval.keyData) }
|
|
164
|
+
: {}),
|
|
165
|
+
}
|
|
166
|
+
assertApprovalCount(approval.spec.approvals)
|
|
167
|
+
return {
|
|
168
|
+
spec: {
|
|
169
|
+
account: approval.spec.account,
|
|
170
|
+
approvals: approval.spec.approvals.map((approval) => ({
|
|
171
|
+
...approval,
|
|
172
|
+
...(typeof approval.keyData !== 'undefined'
|
|
173
|
+
? { keyData: shimKeyData(approval.keyData) }
|
|
174
|
+
: {}),
|
|
175
|
+
})),
|
|
176
|
+
config: serializeConfig(approval.spec.config),
|
|
177
|
+
},
|
|
178
|
+
type: 'multisig',
|
|
179
|
+
}
|
|
180
|
+
}),
|
|
181
|
+
config: serializeConfig(config),
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export declare namespace toRpc {
|
|
186
|
+
/** Error type for `toRpc`. */
|
|
187
|
+
export type ErrorType =
|
|
188
|
+
| Hex.fromNumber.ErrorType
|
|
189
|
+
| InvalidSimulationError
|
|
190
|
+
| MultisigConfig.assert.ErrorType
|
|
191
|
+
| Rlp.fromHex.ErrorType
|
|
192
|
+
| Errors.GlobalErrorType
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** @internal */
|
|
196
|
+
function assertApprovalCount(approvals: readonly unknown[]) {
|
|
197
|
+
if (approvals.length > MultisigConfig.maxSignatures)
|
|
198
|
+
throw new InvalidSimulationError({
|
|
199
|
+
reason: `approval count exceeds ${MultisigConfig.maxSignatures}`,
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** @internal */
|
|
204
|
+
function deserializeConfig(config: Hex.Hex): MultisigConfig.Config {
|
|
205
|
+
const tuple = Rlp.toHex(config)
|
|
206
|
+
if (!Array.isArray(tuple) || tuple.length !== 4)
|
|
207
|
+
throw new InvalidSimulationError({ reason: 'invalid config encoding' })
|
|
208
|
+
const [salt, version, threshold, owners] = tuple
|
|
209
|
+
if (
|
|
210
|
+
Array.isArray(salt) ||
|
|
211
|
+
Hex.size(salt) !== 32 ||
|
|
212
|
+
Array.isArray(version) ||
|
|
213
|
+
Hex.size(version) > 8 ||
|
|
214
|
+
(version !== '0x' && Hex.slice(version, 0, 1) === '0x00') ||
|
|
215
|
+
Array.isArray(threshold) ||
|
|
216
|
+
Hex.size(threshold) > 1 ||
|
|
217
|
+
!Array.isArray(owners) ||
|
|
218
|
+
owners.some(
|
|
219
|
+
(owner) =>
|
|
220
|
+
!Array.isArray(owner) ||
|
|
221
|
+
owner.length !== 2 ||
|
|
222
|
+
owner.some(Array.isArray) ||
|
|
223
|
+
!Address.validate(owner[0]) ||
|
|
224
|
+
Hex.size(owner[1] as Hex.Hex) > 1,
|
|
225
|
+
)
|
|
226
|
+
)
|
|
227
|
+
throw new InvalidSimulationError({ reason: 'invalid config encoding' })
|
|
228
|
+
const value = MultisigConfig.fromTuple(
|
|
229
|
+
tuple as unknown as MultisigConfig.Tuple,
|
|
230
|
+
)
|
|
231
|
+
MultisigConfig.assert(value)
|
|
232
|
+
if (!Hex.isEqual(config, serializeConfig(value)))
|
|
233
|
+
throw new InvalidSimulationError({ reason: 'noncanonical config encoding' })
|
|
234
|
+
return value
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** @internal */
|
|
238
|
+
function serializeConfig(config: MultisigConfig.Input): Hex.Hex {
|
|
239
|
+
return Rlp.fromHex(MultisigConfig.toTuple(config))
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Shims long key data into the length hint accepted by Tempo's gas estimator.
|
|
244
|
+
* @internal
|
|
245
|
+
*/
|
|
246
|
+
function shimKeyData(data: Hex.Hex): Hex.Hex {
|
|
247
|
+
const size = Hex.size(data)
|
|
248
|
+
if (size <= 4) return data
|
|
249
|
+
return Hex.fromNumber(size, { size: 2 })
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Thrown when a native multisig simulation spec is invalid. */
|
|
253
|
+
export class InvalidSimulationError extends Errors.BaseError {
|
|
254
|
+
override readonly name = 'MultisigSimulation.InvalidSimulationError'
|
|
255
|
+
constructor(options: InvalidSimulationError.Options) {
|
|
256
|
+
super(`Invalid multisig simulation: ${options.reason}.`)
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export declare namespace InvalidSimulationError {
|
|
261
|
+
/** Error construction options. */
|
|
262
|
+
export type Options = {
|
|
263
|
+
/** Validation failure. */
|
|
264
|
+
reason: string
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
import type { Provider } from 'ox'
|
|
2
2
|
import type {
|
|
3
3
|
KeyAuthorization,
|
|
4
|
+
MultisigConfig,
|
|
4
5
|
MultisigOperation,
|
|
6
|
+
MultisigSimulation,
|
|
5
7
|
RpcSchemaTempo,
|
|
6
8
|
} from 'ox/tempo'
|
|
7
9
|
import { expectTypeOf, test } from 'vp/test'
|
|
10
|
+
import type * as Address from '../core/Address.js'
|
|
8
11
|
import type * as Hex from '../core/Hex.js'
|
|
9
12
|
|
|
10
13
|
declare const provider: Provider.Provider<{ schema: RpcSchemaTempo.Multisig }>
|
|
14
|
+
declare const tempoProvider: Provider.Provider<{
|
|
15
|
+
schema: RpcSchemaTempo.Tempo
|
|
16
|
+
}>
|
|
17
|
+
declare const address: Address.Address
|
|
11
18
|
declare const hash: Hex.Hex
|
|
12
19
|
declare const keyAuthorization: KeyAuthorization.Rpc
|
|
20
|
+
declare const multisigSimulation: MultisigSimulation.Rpc
|
|
13
21
|
declare const serializedTransaction: Hex.Hex
|
|
14
22
|
declare const signature: Hex.Hex
|
|
15
23
|
|
|
16
24
|
test('multisig provider methods', () => {
|
|
25
|
+
expectTypeOf(
|
|
26
|
+
provider.request({
|
|
27
|
+
method: 'multisig_approveKeyAuthorization',
|
|
28
|
+
params: [{ keyAuthorization }],
|
|
29
|
+
}),
|
|
30
|
+
).resolves.toEqualTypeOf<MultisigOperation.KeyAuthorizationRpc>()
|
|
31
|
+
|
|
32
|
+
expectTypeOf(
|
|
33
|
+
provider.request({
|
|
34
|
+
method: 'multisig_approveKeyAuthorization',
|
|
35
|
+
params: [{ hash, signature }],
|
|
36
|
+
}),
|
|
37
|
+
).resolves.toEqualTypeOf<MultisigOperation.KeyAuthorizationRpc>()
|
|
38
|
+
|
|
17
39
|
expectTypeOf(
|
|
18
40
|
provider.request({
|
|
19
41
|
method: 'multisig_approveRawTransaction',
|
|
@@ -35,17 +57,10 @@ test('multisig provider methods', () => {
|
|
|
35
57
|
|
|
36
58
|
expectTypeOf(
|
|
37
59
|
provider.request({
|
|
38
|
-
method: '
|
|
39
|
-
params: [{
|
|
60
|
+
method: 'multisig_getConfig',
|
|
61
|
+
params: [{ address }],
|
|
40
62
|
}),
|
|
41
|
-
).resolves.toEqualTypeOf<
|
|
42
|
-
|
|
43
|
-
expectTypeOf(
|
|
44
|
-
provider.request({
|
|
45
|
-
method: 'multisig_approveKeyAuthorization',
|
|
46
|
-
params: [{ hash, signature }],
|
|
47
|
-
}),
|
|
48
|
-
).resolves.toEqualTypeOf<MultisigOperation.KeyAuthorizationRpc>()
|
|
63
|
+
).resolves.toEqualTypeOf<MultisigConfig.Rpc | null>()
|
|
49
64
|
|
|
50
65
|
expectTypeOf(
|
|
51
66
|
provider.request({
|
|
@@ -54,3 +69,23 @@ test('multisig provider methods', () => {
|
|
|
54
69
|
}),
|
|
55
70
|
).resolves.toEqualTypeOf<MultisigOperation.Rpc | null>()
|
|
56
71
|
})
|
|
72
|
+
|
|
73
|
+
test('tempo simulation accepts multisig specs', () => {
|
|
74
|
+
void tempoProvider.request({
|
|
75
|
+
method: 'tempo_simulateV1',
|
|
76
|
+
params: [
|
|
77
|
+
{
|
|
78
|
+
blockStateCalls: [
|
|
79
|
+
{
|
|
80
|
+
calls: [
|
|
81
|
+
{
|
|
82
|
+
multisigSimulation,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
'latest',
|
|
89
|
+
],
|
|
90
|
+
})
|
|
91
|
+
})
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type * as Address from '../core/Address.js'
|
|
1
2
|
import type * as Block from '../core/Block.js'
|
|
2
3
|
import type * as BlockOverrides from '../core/BlockOverrides.js'
|
|
3
4
|
import type * as Hex from '../core/Hex.js'
|
|
@@ -5,6 +6,7 @@ import type * as Log from '../core/Log.js'
|
|
|
5
6
|
import type * as RpcSchema from '../core/RpcSchema.js'
|
|
6
7
|
import type * as StateOverrides from '../core/StateOverrides.js'
|
|
7
8
|
import type * as KeyAuthorization from './KeyAuthorization.js'
|
|
9
|
+
import type * as MultisigConfig from './MultisigConfig.js'
|
|
8
10
|
import type * as MultisigOperation from './MultisigOperation.js'
|
|
9
11
|
import type * as TransactionRequest from './TransactionRequest.js'
|
|
10
12
|
|
|
@@ -58,6 +60,16 @@ export type Tempo = RpcSchema.From<{
|
|
|
58
60
|
|
|
59
61
|
/** Union of all JSON-RPC methods for the `multisig_` namespace. */
|
|
60
62
|
export type Multisig = RpcSchema.From<
|
|
63
|
+
| {
|
|
64
|
+
Request: {
|
|
65
|
+
method: 'multisig_approveKeyAuthorization'
|
|
66
|
+
params: [
|
|
67
|
+
| { keyAuthorization: KeyAuthorization.Rpc }
|
|
68
|
+
| { hash: Hex.Hex; signature: Hex.Hex },
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
ReturnType: MultisigOperation.KeyAuthorizationRpc
|
|
72
|
+
}
|
|
61
73
|
| {
|
|
62
74
|
Request: {
|
|
63
75
|
method: 'multisig_approveRawTransaction'
|
|
@@ -74,13 +86,10 @@ export type Multisig = RpcSchema.From<
|
|
|
74
86
|
}
|
|
75
87
|
| {
|
|
76
88
|
Request: {
|
|
77
|
-
method: '
|
|
78
|
-
params: [
|
|
79
|
-
| { keyAuthorization: KeyAuthorization.Rpc }
|
|
80
|
-
| { hash: Hex.Hex; signature: Hex.Hex },
|
|
81
|
-
]
|
|
89
|
+
method: 'multisig_getConfig'
|
|
90
|
+
params: [{ address: Address.Address }]
|
|
82
91
|
}
|
|
83
|
-
ReturnType:
|
|
92
|
+
ReturnType: MultisigConfig.Rpc | null
|
|
84
93
|
}
|
|
85
94
|
| {
|
|
86
95
|
Request: {
|
|
@@ -2,13 +2,6 @@ import { expectTypeOf, test } from 'vp/test'
|
|
|
2
2
|
import * as MultisigConfig from './MultisigConfig.js'
|
|
3
3
|
import * as SignatureEnvelope from './SignatureEnvelope.js'
|
|
4
4
|
|
|
5
|
-
const signatureRpc = {
|
|
6
|
-
r: '0x01',
|
|
7
|
-
s: '0x02',
|
|
8
|
-
type: 'secp256k1',
|
|
9
|
-
yParity: '0x0',
|
|
10
|
-
} as const satisfies SignatureEnvelope.SignatureEnvelopeRpc
|
|
11
|
-
|
|
12
5
|
const signature = {
|
|
13
6
|
signature: {
|
|
14
7
|
r: '0x01',
|
|
@@ -34,20 +27,9 @@ test('toRpc preserves the signature type', () => {
|
|
|
34
27
|
).toEqualTypeOf<SignatureEnvelope.Secp256k1Rpc>()
|
|
35
28
|
})
|
|
36
29
|
|
|
37
|
-
test('MultisigRpc
|
|
38
|
-
const rpc =
|
|
39
|
-
|
|
40
|
-
config: MultisigConfig.toRpc(config),
|
|
41
|
-
signatures: [signatureRpc],
|
|
42
|
-
} as const satisfies SignatureEnvelope.MultisigRpc
|
|
43
|
-
|
|
44
|
-
expectTypeOf(rpc.config).toMatchTypeOf<MultisigConfig.Rpc>()
|
|
45
|
-
expectTypeOf(rpc.signatures).toMatchTypeOf<
|
|
46
|
-
readonly SignatureEnvelope.SignatureEnvelopeRpc[]
|
|
47
|
-
>()
|
|
48
|
-
expectTypeOf<
|
|
49
|
-
SignatureEnvelope.GetType<typeof rpc>
|
|
50
|
-
>().toEqualTypeOf<'multisig'>()
|
|
30
|
+
test('MultisigRpc contains an untagged serialized multisig signature', () => {
|
|
31
|
+
const rpc = '0xf8' as const satisfies SignatureEnvelope.MultisigRpc
|
|
32
|
+
expectTypeOf(rpc).toMatchTypeOf<`0x${string}`>()
|
|
51
33
|
})
|
|
52
34
|
|
|
53
35
|
test('from derives an initial account', () => {
|
|
@@ -74,41 +56,3 @@ test('from requires an account for a current config', () => {
|
|
|
74
56
|
})
|
|
75
57
|
expectTypeOf(multisig).toMatchTypeOf<SignatureEnvelope.Multisig>()
|
|
76
58
|
})
|
|
77
|
-
|
|
78
|
-
test('MultisigRpc rejects old witness shapes', () => {
|
|
79
|
-
const accountOnly = {
|
|
80
|
-
account: '0x1111111111111111111111111111111111111111',
|
|
81
|
-
signatures: [signatureRpc],
|
|
82
|
-
} as const
|
|
83
|
-
// @ts-expect-error Multisig RPC signatures require config.
|
|
84
|
-
const accountOnlyRpc: SignatureEnvelope.MultisigRpc = accountOnly
|
|
85
|
-
|
|
86
|
-
const init = {
|
|
87
|
-
init: config,
|
|
88
|
-
signatures: [signatureRpc],
|
|
89
|
-
} as const
|
|
90
|
-
// @ts-expect-error Multisig RPC signatures no longer accept init.
|
|
91
|
-
const initRpc: SignatureEnvelope.MultisigRpc = init
|
|
92
|
-
|
|
93
|
-
const serialized = {
|
|
94
|
-
account: '0x1111111111111111111111111111111111111111',
|
|
95
|
-
config: MultisigConfig.toRpc(config),
|
|
96
|
-
signatures: ['0x1234'],
|
|
97
|
-
} as const
|
|
98
|
-
// @ts-expect-error Owner approvals use structured RPC envelopes.
|
|
99
|
-
const serializedRpc: SignatureEnvelope.MultisigRpc = serialized
|
|
100
|
-
|
|
101
|
-
const tagged = {
|
|
102
|
-
account: '0x1111111111111111111111111111111111111111',
|
|
103
|
-
config: MultisigConfig.toRpc(config),
|
|
104
|
-
signatures: [signatureRpc],
|
|
105
|
-
type: 'multisig',
|
|
106
|
-
} as const
|
|
107
|
-
// @ts-expect-error Multisig RPC signatures are untagged.
|
|
108
|
-
const taggedRpc: SignatureEnvelope.MultisigRpc = tagged
|
|
109
|
-
|
|
110
|
-
expectTypeOf(accountOnlyRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
|
|
111
|
-
expectTypeOf(initRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
|
|
112
|
-
expectTypeOf(serializedRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
|
|
113
|
-
expectTypeOf(taggedRpc).toEqualTypeOf<SignatureEnvelope.MultisigRpc>()
|
|
114
|
-
})
|
|
@@ -68,6 +68,9 @@ const signature_keychain_webauthn = SignatureEnvelope.from({
|
|
|
68
68
|
version: 'v2',
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
+
const signature_multisig_rpc =
|
|
72
|
+
'0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b' as const
|
|
73
|
+
|
|
71
74
|
describe('assert', () => {
|
|
72
75
|
describe('secp256k1', () => {
|
|
73
76
|
test('behavior: validates valid signature', () => {
|
|
@@ -2492,6 +2495,41 @@ describe('fromRpc', () => {
|
|
|
2492
2495
|
expect(envelope.keyId).toBe('0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c')
|
|
2493
2496
|
})
|
|
2494
2497
|
})
|
|
2498
|
+
describe('multisig', () => {
|
|
2499
|
+
test('behavior: converts an untagged serialized multisig signature', () => {
|
|
2500
|
+
expect(
|
|
2501
|
+
SignatureEnvelope.serialize(
|
|
2502
|
+
SignatureEnvelope.fromRpc(signature_multisig_rpc),
|
|
2503
|
+
),
|
|
2504
|
+
).toMatchInlineSnapshot(`"0x05${signature_multisig_rpc.slice(2)}"`)
|
|
2505
|
+
})
|
|
2506
|
+
|
|
2507
|
+
test('error: rejects a payload with the transaction type byte', () => {
|
|
2508
|
+
expect(() =>
|
|
2509
|
+
SignatureEnvelope.fromRpc(`0x05${signature_multisig_rpc.slice(2)}`),
|
|
2510
|
+
).toThrowError()
|
|
2511
|
+
})
|
|
2512
|
+
|
|
2513
|
+
test('error: rejects trailing bytes', () => {
|
|
2514
|
+
expect(() =>
|
|
2515
|
+
SignatureEnvelope.fromRpc(`${signature_multisig_rpc}00`),
|
|
2516
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
2517
|
+
`[Rlp.TrailingBytesError: RLP payload encodes a single item, but \`1\` trailing byte remains.]`,
|
|
2518
|
+
)
|
|
2519
|
+
})
|
|
2520
|
+
|
|
2521
|
+
test('error: rejects a structured multisig signature', () => {
|
|
2522
|
+
expect(() =>
|
|
2523
|
+
SignatureEnvelope.fromRpc({
|
|
2524
|
+
account: '0x9dba7f426b711d4893c11611eacf7cc334e7146b',
|
|
2525
|
+
config: {},
|
|
2526
|
+
signatures: [],
|
|
2527
|
+
} as never),
|
|
2528
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
2529
|
+
`[SignatureEnvelope.CoercionError: Unable to coerce value (\`{"account":"0x9dba7f426b711d4893c11611eacf7cc334e7146b","config":{},"signatures":[]}\`) to a valid signature envelope.]`,
|
|
2530
|
+
)
|
|
2531
|
+
})
|
|
2532
|
+
})
|
|
2495
2533
|
})
|
|
2496
2534
|
|
|
2497
2535
|
describe('toRpc', () => {
|
|
@@ -2589,6 +2627,7 @@ describe('toRpc', () => {
|
|
|
2589
2627
|
const rpc = SignatureEnvelope.toRpc(
|
|
2590
2628
|
signature_keychain_p256,
|
|
2591
2629
|
) as SignatureEnvelope.KeychainRpc
|
|
2630
|
+
if (typeof rpc.signature === 'string') throw new Error('unreachable')
|
|
2592
2631
|
|
|
2593
2632
|
expect(rpc.type).toBe('keychain')
|
|
2594
2633
|
expect(rpc.userAddress).toBe(signature_keychain_p256.userAddress)
|
|
@@ -2601,6 +2640,7 @@ describe('toRpc', () => {
|
|
|
2601
2640
|
const rpc = SignatureEnvelope.toRpc(
|
|
2602
2641
|
signature_keychain_webauthn,
|
|
2603
2642
|
) as SignatureEnvelope.KeychainRpc
|
|
2643
|
+
if (typeof rpc.signature === 'string') throw new Error('unreachable')
|
|
2604
2644
|
|
|
2605
2645
|
expect(rpc.type).toBe('keychain')
|
|
2606
2646
|
expect(rpc.userAddress).toBe(signature_keychain_webauthn.userAddress)
|
|
@@ -2644,6 +2684,18 @@ describe('toRpc', () => {
|
|
|
2644
2684
|
expect(rpc.keyId).toBeUndefined()
|
|
2645
2685
|
})
|
|
2646
2686
|
})
|
|
2687
|
+
|
|
2688
|
+
describe('multisig', () => {
|
|
2689
|
+
test('behavior: removes the transaction type byte', () => {
|
|
2690
|
+
const envelope = SignatureEnvelope.deserialize(
|
|
2691
|
+
`0x05${signature_multisig_rpc.slice(2)}`,
|
|
2692
|
+
)
|
|
2693
|
+
|
|
2694
|
+
expect(SignatureEnvelope.toRpc(envelope)).toMatchInlineSnapshot(
|
|
2695
|
+
`"${signature_multisig_rpc}"`,
|
|
2696
|
+
)
|
|
2697
|
+
})
|
|
2698
|
+
})
|
|
2647
2699
|
})
|
|
2648
2700
|
|
|
2649
2701
|
describe('roundtrip: toRpc <-> fromRpc', () => {
|
|
@@ -2888,7 +2940,7 @@ describe('multisig', () => {
|
|
|
2888
2940
|
const nested =
|
|
2889
2941
|
'0x05f8f0949969e2243075b27a8eab61009c59b77ab13b83f6f83ba033333333333333333333333333333333333333333333333333333333333333338001d7d6944f9f5b162a7464bcc260ce44d7ae0d935f9c583701f89cb89a05f897944f9f5b162a7464bcc260ce44d7ae0d935f9c5837f83ba022222222222222222222222222222222222222222222222222222222222222228001d7d6946813eb9362372eef6200f3b1dbc3f819671cba6901f843b841032aa6f3ea7b0b7069720d0f3891983c493d149326c5c957d864ed7371b8475e3e95325079b24491f4b6d68920e4b3bacd7c6094df6ed88b8674864ab559c8fb1b' as const
|
|
2890
2942
|
|
|
2891
|
-
test('example: decodes and re-encodes the frozen initial
|
|
2943
|
+
test('example: decodes and re-encodes the frozen initial signature', () => {
|
|
2892
2944
|
const envelope = SignatureEnvelope.deserialize(initial)
|
|
2893
2945
|
|
|
2894
2946
|
expect(envelope).toMatchInlineSnapshot(`
|
|
@@ -2921,7 +2973,7 @@ describe('multisig', () => {
|
|
|
2921
2973
|
expect(SignatureEnvelope.serialize(envelope)).toBe(initial)
|
|
2922
2974
|
})
|
|
2923
2975
|
|
|
2924
|
-
test('example: decodes and re-encodes the frozen current
|
|
2976
|
+
test('example: decodes and re-encodes the frozen current signature', () => {
|
|
2925
2977
|
const envelope = SignatureEnvelope.deserialize(current)
|
|
2926
2978
|
|
|
2927
2979
|
expect(envelope).toMatchInlineSnapshot(`
|
|
@@ -2954,7 +3006,7 @@ describe('multisig', () => {
|
|
|
2954
3006
|
expect(SignatureEnvelope.serialize(envelope)).toBe(current)
|
|
2955
3007
|
})
|
|
2956
3008
|
|
|
2957
|
-
test('example: decodes and re-encodes the frozen nested
|
|
3009
|
+
test('example: decodes and re-encodes the frozen nested signature', () => {
|
|
2958
3010
|
const envelope = SignatureEnvelope.deserialize(nested)
|
|
2959
3011
|
|
|
2960
3012
|
expect(envelope).toMatchInlineSnapshot(`
|
|
@@ -3042,7 +3094,7 @@ describe('multisig', () => {
|
|
|
3042
3094
|
`)
|
|
3043
3095
|
})
|
|
3044
3096
|
|
|
3045
|
-
test('behavior: round trips initial and current RPC
|
|
3097
|
+
test('behavior: round trips initial and current RPC signatures', () => {
|
|
3046
3098
|
const initialEnvelope = SignatureEnvelope.deserialize(initial)
|
|
3047
3099
|
const currentEnvelope = SignatureEnvelope.deserialize(current)
|
|
3048
3100
|
const rpc = {
|
|
@@ -3052,50 +3104,8 @@ describe('multisig', () => {
|
|
|
3052
3104
|
|
|
3053
3105
|
expect(rpc).toMatchInlineSnapshot(`
|
|
3054
3106
|
{
|
|
3055
|
-
"current":
|
|
3056
|
-
|
|
3057
|
-
"config": {
|
|
3058
|
-
"owners": [
|
|
3059
|
-
{
|
|
3060
|
-
"owner": "0x2b5ad5c4795c026514f8317c7a215e218dccd6cf",
|
|
3061
|
-
"weight": 1,
|
|
3062
|
-
},
|
|
3063
|
-
],
|
|
3064
|
-
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
3065
|
-
"threshold": 1,
|
|
3066
|
-
"version": "0x1",
|
|
3067
|
-
},
|
|
3068
|
-
"signatures": [
|
|
3069
|
-
{
|
|
3070
|
-
"r": "0x4a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e050",
|
|
3071
|
-
"s": "0x4a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf5",
|
|
3072
|
-
"type": "secp256k1",
|
|
3073
|
-
"yParity": "0x0",
|
|
3074
|
-
},
|
|
3075
|
-
],
|
|
3076
|
-
},
|
|
3077
|
-
"initial": {
|
|
3078
|
-
"account": "0x9dba7f426b711d4893c11611eacf7cc334e7146b",
|
|
3079
|
-
"config": {
|
|
3080
|
-
"owners": [
|
|
3081
|
-
{
|
|
3082
|
-
"owner": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
|
|
3083
|
-
"weight": 1,
|
|
3084
|
-
},
|
|
3085
|
-
],
|
|
3086
|
-
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
3087
|
-
"threshold": 1,
|
|
3088
|
-
"version": "0x0",
|
|
3089
|
-
},
|
|
3090
|
-
"signatures": [
|
|
3091
|
-
{
|
|
3092
|
-
"r": "0x869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f985",
|
|
3093
|
-
"s": "0x35dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f4",
|
|
3094
|
-
"type": "secp256k1",
|
|
3095
|
-
"yParity": "0x0",
|
|
3096
|
-
},
|
|
3097
|
-
],
|
|
3098
|
-
},
|
|
3107
|
+
"current": "0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000000101d7d6942b5ad5c4795c026514f8317c7a215e218dccd6cf01f843b8414a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e0504a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf51b",
|
|
3108
|
+
"initial": "0xf897949dba7f426b711d4893c11611eacf7cc334e7146bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b",
|
|
3099
3109
|
}
|
|
3100
3110
|
`)
|
|
3101
3111
|
expect(SignatureEnvelope.fromRpc(rpc.initial)).toStrictEqual(
|
|
@@ -3106,7 +3116,7 @@ describe('multisig', () => {
|
|
|
3106
3116
|
)
|
|
3107
3117
|
})
|
|
3108
3118
|
|
|
3109
|
-
test('behavior: derives the account only for an initial
|
|
3119
|
+
test('behavior: derives the account only for an initial config', () => {
|
|
3110
3120
|
const initialEnvelope = SignatureEnvelope.deserialize(initial)
|
|
3111
3121
|
if (initialEnvelope.type !== 'multisig') throw new Error('unreachable')
|
|
3112
3122
|
if (initialEnvelope.config.version !== 0n) throw new Error('unreachable')
|
|
@@ -3177,7 +3187,7 @@ describe('multisig', () => {
|
|
|
3177
3187
|
`)
|
|
3178
3188
|
})
|
|
3179
3189
|
|
|
3180
|
-
test('behavior: accepts initial and current nested
|
|
3190
|
+
test('behavior: accepts initial and current nested signatures', () => {
|
|
3181
3191
|
const initialEnvelope = SignatureEnvelope.deserialize(initial)
|
|
3182
3192
|
const currentEnvelope = SignatureEnvelope.deserialize(current)
|
|
3183
3193
|
if (
|
|
@@ -3219,7 +3229,7 @@ describe('multisig', () => {
|
|
|
3219
3229
|
)
|
|
3220
3230
|
})
|
|
3221
3231
|
|
|
3222
|
-
test('error: rejects a self-owned current
|
|
3232
|
+
test('error: rejects a self-owned current signature', () => {
|
|
3223
3233
|
const envelope = SignatureEnvelope.deserialize(current)
|
|
3224
3234
|
if (envelope.type !== 'multisig') throw new Error('unreachable')
|
|
3225
3235
|
const account = envelope.config.owners[0]!.owner
|