ox 1.8.5 → 1.8.7
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 +37 -0
- package/dist/tempo/{MultisigConfig.d.ts → AccountConfig.d.ts} +116 -63
- package/dist/tempo/AccountConfig.d.ts.map +1 -0
- package/dist/tempo/{MultisigConfig.js → AccountConfig.js} +109 -55
- package/dist/tempo/AccountConfig.js.map +1 -0
- package/dist/tempo/{MultisigOperation.d.ts → AccountOperation.d.ts} +62 -62
- package/dist/tempo/AccountOperation.d.ts.map +1 -0
- package/dist/tempo/{MultisigOperation.js → AccountOperation.js} +64 -64
- package/dist/tempo/AccountOperation.js.map +1 -0
- package/dist/tempo/{MultisigSimulation.d.ts → AccountSimulation.d.ts} +21 -21
- package/dist/tempo/AccountSimulation.d.ts.map +1 -0
- package/dist/tempo/{MultisigSimulation.js → AccountSimulation.js} +20 -20
- package/dist/tempo/AccountSimulation.js.map +1 -0
- package/dist/tempo/KeyAuthorization.d.ts +37 -27
- package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/KeyAuthorization.js +17 -19
- package/dist/tempo/KeyAuthorization.js.map +1 -1
- package/dist/tempo/RpcSchemaTempo.d.ts +13 -13
- package/dist/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.d.ts +36 -33
- package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/tempo/SignatureEnvelope.js +77 -77
- package/dist/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/tempo/TokenRole.d.ts +4 -2
- package/dist/tempo/TokenRole.d.ts.map +1 -1
- package/dist/tempo/TokenRole.js +4 -1
- package/dist/tempo/TokenRole.js.map +1 -1
- package/dist/tempo/TransactionRequest.d.ts +11 -9
- package/dist/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/tempo/TransactionRequest.js +14 -10
- package/dist/tempo/TransactionRequest.js.map +1 -1
- package/dist/tempo/index.d.ts +56 -54
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +56 -54
- package/dist/tempo/index.js.map +1 -1
- package/dist/zod/tempo/{MultisigConfig.d.ts → AccountConfig.d.ts} +6 -6
- package/dist/zod/tempo/AccountConfig.d.ts.map +1 -0
- package/dist/zod/tempo/{MultisigConfig.js → AccountConfig.js} +12 -12
- package/dist/zod/tempo/AccountConfig.js.map +1 -0
- package/dist/zod/tempo/{MultisigOperation.d.ts → AccountOperation.d.ts} +5 -5
- package/dist/zod/tempo/AccountOperation.d.ts.map +1 -0
- package/dist/zod/tempo/{MultisigOperation.js → AccountOperation.js} +10 -10
- package/dist/zod/tempo/AccountOperation.js.map +1 -0
- package/dist/zod/tempo/{MultisigSimulation.d.ts → AccountSimulation.d.ts} +5 -5
- package/dist/zod/tempo/AccountSimulation.d.ts.map +1 -0
- package/dist/zod/tempo/AccountSimulation.js +44 -0
- package/dist/zod/tempo/AccountSimulation.js.map +1 -0
- package/dist/zod/tempo/AuthorizationTempo.d.ts +10 -10
- package/dist/zod/tempo/KeyAuthorization.d.ts +12 -12
- package/dist/zod/tempo/KeyAuthorization.js +6 -6
- package/dist/zod/tempo/KeyAuthorization.js.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.d.ts +59 -59
- package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
- package/dist/zod/tempo/RpcSchemaTempo.js +28 -28
- package/dist/zod/tempo/RpcSchemaTempo.js.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.d.ts +7 -7
- package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/dist/zod/tempo/SignatureEnvelope.js +22 -16
- package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
- package/dist/zod/tempo/TokenRole.d.ts +1 -1
- package/dist/zod/tempo/TokenRole.d.ts.map +1 -1
- package/dist/zod/tempo/Transaction.d.ts +24 -24
- package/dist/zod/tempo/TransactionRequest.d.ts +84 -84
- package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
- package/dist/zod/tempo/TransactionRequest.js +26 -17
- package/dist/zod/tempo/TransactionRequest.js.map +1 -1
- package/dist/zod/tempo/TxEnvelopeTempo.d.ts +12 -12
- package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +3 -3
- package/dist/zod/tempo/z.d.ts +3 -3
- package/dist/zod/tempo/z.d.ts.map +1 -1
- package/dist/zod/tempo/z.js +3 -3
- package/dist/zod/tempo/z.js.map +1 -1
- package/package.json +31 -31
- package/src/tempo/{MultisigConfig.test-d.ts → AccountConfig.test-d.ts} +22 -12
- package/src/tempo/{MultisigConfig.test.ts → AccountConfig.test.ts} +155 -37
- package/src/tempo/{MultisigConfig.ts → AccountConfig.ts} +138 -66
- package/src/tempo/AccountOperation.test-d.ts +79 -0
- package/src/tempo/{MultisigOperation.test.ts → AccountOperation.test.ts} +97 -97
- package/src/tempo/{MultisigOperation.ts → AccountOperation.ts} +95 -97
- package/src/tempo/{MultisigSimulation.test-d.ts → AccountSimulation.test-d.ts} +7 -9
- package/src/tempo/{MultisigSimulation.test.ts → AccountSimulation.test.ts} +28 -28
- package/src/tempo/{MultisigSimulation.ts → AccountSimulation.ts} +28 -30
- package/src/tempo/KeyAuthorization.test-d.ts +15 -6
- package/src/tempo/KeyAuthorization.test.ts +117 -37
- package/src/tempo/KeyAuthorization.ts +68 -59
- package/src/tempo/RpcSchemaTempo.test-d.ts +19 -19
- package/src/tempo/RpcSchemaTempo.ts +13 -13
- package/src/tempo/SignatureEnvelope.test-d.ts +7 -7
- package/src/tempo/SignatureEnvelope.test.ts +34 -34
- package/src/tempo/SignatureEnvelope.ts +112 -108
- package/src/tempo/TokenRole.ts +4 -1
- package/src/tempo/Transaction.test.ts +4 -4
- package/src/tempo/TransactionRequest.test-d.ts +17 -6
- package/src/tempo/TransactionRequest.test.ts +32 -17
- package/src/tempo/TransactionRequest.ts +27 -19
- package/src/tempo/{multisig.e2e.test.ts → configurableAccounts.e2e.test.ts} +16 -16
- package/src/tempo/e2e.test.ts +32 -20
- package/src/tempo/index.ts +56 -54
- package/src/version.ts +1 -1
- package/src/zod/tempo/{MultisigConfig.ts → AccountConfig.ts} +12 -12
- package/src/zod/tempo/{MultisigOperation.ts → AccountOperation.ts} +9 -9
- package/src/zod/tempo/AccountSimulation.ts +53 -0
- package/src/zod/tempo/KeyAuthorization.ts +8 -8
- package/src/zod/tempo/RpcSchemaTempo.ts +28 -28
- package/src/zod/tempo/SignatureEnvelope.ts +23 -17
- package/src/zod/tempo/TransactionRequest.ts +29 -18
- package/src/zod/tempo/_test/AccountConfig.test-d.ts +13 -0
- package/src/zod/tempo/_test/{MultisigConfig.test.ts → AccountConfig.test.ts} +7 -7
- package/src/zod/tempo/_test/AccountOperation.test-d.ts +27 -0
- package/src/zod/tempo/_test/{MultisigOperation.test.ts → AccountOperation.test.ts} +9 -9
- package/src/zod/tempo/_test/AccountSimulation.test-d.ts +13 -0
- package/src/zod/tempo/_test/{MultisigSimulation.test.ts → AccountSimulation.test.ts} +13 -13
- package/src/zod/tempo/_test/KeyAuthorization.test.ts +9 -9
- package/src/zod/tempo/_test/RpcSchemaTempo.test-d.ts +27 -27
- package/src/zod/tempo/_test/RpcSchemaTempo.test.ts +16 -18
- package/src/zod/tempo/_test/SignatureEnvelope.test-d.ts +5 -5
- package/src/zod/tempo/_test/SignatureEnvelope.test.ts +12 -9
- package/src/zod/tempo/_test/TransactionRequest.test.ts +28 -8
- package/src/zod/tempo/z.ts +3 -3
- package/dist/tempo/MultisigConfig.d.ts.map +0 -1
- package/dist/tempo/MultisigConfig.js.map +0 -1
- package/dist/tempo/MultisigOperation.d.ts.map +0 -1
- package/dist/tempo/MultisigOperation.js.map +0 -1
- package/dist/tempo/MultisigSimulation.d.ts.map +0 -1
- package/dist/tempo/MultisigSimulation.js.map +0 -1
- package/dist/zod/tempo/MultisigConfig.d.ts.map +0 -1
- package/dist/zod/tempo/MultisigConfig.js.map +0 -1
- package/dist/zod/tempo/MultisigOperation.d.ts.map +0 -1
- package/dist/zod/tempo/MultisigOperation.js.map +0 -1
- package/dist/zod/tempo/MultisigSimulation.d.ts.map +0 -1
- package/dist/zod/tempo/MultisigSimulation.js +0 -44
- package/dist/zod/tempo/MultisigSimulation.js.map +0 -1
- package/src/tempo/MultisigOperation.test-d.ts +0 -79
- package/src/zod/tempo/MultisigSimulation.ts +0 -53
- package/src/zod/tempo/_test/MultisigConfig.test-d.ts +0 -13
- package/src/zod/tempo/_test/MultisigOperation.test-d.ts +0 -27
- package/src/zod/tempo/_test/MultisigSimulation.test-d.ts +0 -13
|
@@ -18,14 +18,14 @@ import * as ox_Secp256k1 from '../core/Secp256k1.js'
|
|
|
18
18
|
import * as Signature from '../core/Signature.js'
|
|
19
19
|
import type * as WebAuthnP256 from '../core/WebAuthnP256.js'
|
|
20
20
|
import * as ox_WebAuthnP256 from '../core/WebAuthnP256.js'
|
|
21
|
-
import * as
|
|
21
|
+
import * as AccountConfig from './AccountConfig.js'
|
|
22
22
|
|
|
23
23
|
/** Signature type identifiers for encoding/decoding */
|
|
24
24
|
const serializedP256Type = '0x01'
|
|
25
25
|
const serializedWebAuthnType = '0x02'
|
|
26
26
|
const serializedKeychainType = '0x03'
|
|
27
27
|
const serializedKeychainV2Type = '0x04'
|
|
28
|
-
const
|
|
28
|
+
const serializedConfigurableType = '0x05'
|
|
29
29
|
|
|
30
30
|
/** Serialized magic identifier for Tempo signature envelopes. */
|
|
31
31
|
export const magicBytes =
|
|
@@ -86,10 +86,10 @@ export type GetType<
|
|
|
86
86
|
? 'keychain'
|
|
87
87
|
: envelope extends {
|
|
88
88
|
account: Address.Address
|
|
89
|
-
config:
|
|
89
|
+
config: AccountConfig.Input
|
|
90
90
|
signatures: any
|
|
91
91
|
}
|
|
92
|
-
? '
|
|
92
|
+
? 'configurable'
|
|
93
93
|
: never
|
|
94
94
|
|
|
95
95
|
/**
|
|
@@ -113,6 +113,9 @@ export type GetType<
|
|
|
113
113
|
* V2 binds the signature to the user account via `keccak256(sigHash || userAddress)`.
|
|
114
114
|
* The protocol validates the access key authorization via the AccountKeychain precompile.
|
|
115
115
|
*
|
|
116
|
+
* - **configurable** (type `0x05`): Configurable account signature carrying the account address,
|
|
117
|
+
* its complete current configuration, and primitive owner approvals over the version-bound digest.
|
|
118
|
+
*
|
|
116
119
|
* [Signature Types Specification](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
|
|
117
120
|
*/
|
|
118
121
|
export type SignatureEnvelope<numberType = number> = OneOf<
|
|
@@ -120,7 +123,7 @@ export type SignatureEnvelope<numberType = number> = OneOf<
|
|
|
120
123
|
| P256<numberType>
|
|
121
124
|
| WebAuthn<numberType>
|
|
122
125
|
| Keychain<numberType>
|
|
123
|
-
|
|
|
126
|
+
| Configurable<numberType>
|
|
124
127
|
>
|
|
125
128
|
|
|
126
129
|
/**
|
|
@@ -128,7 +131,7 @@ export type SignatureEnvelope<numberType = number> = OneOf<
|
|
|
128
131
|
*/
|
|
129
132
|
export type SignatureEnvelopeRpc =
|
|
130
133
|
| OneOf<Secp256k1Rpc | P256Rpc | WebAuthnRpc | KeychainRpc>
|
|
131
|
-
|
|
|
134
|
+
| ConfigurableRpc
|
|
132
135
|
|
|
133
136
|
/** Primitive signature envelope accepted by protocol sidecars. */
|
|
134
137
|
export type Primitive<numberType = number> = OneOf<
|
|
@@ -168,24 +171,24 @@ export type KeychainRpc = {
|
|
|
168
171
|
}
|
|
169
172
|
|
|
170
173
|
/**
|
|
171
|
-
*
|
|
174
|
+
* Configurable account signature (type `0x05`).
|
|
172
175
|
*
|
|
173
176
|
* Carries the account's complete current configuration and primitive owner
|
|
174
177
|
* approvals over its version-bound digest. The node validates the account
|
|
175
178
|
* identity or stored commitment and the owner quorum against chain state.
|
|
176
179
|
*/
|
|
177
|
-
export type
|
|
178
|
-
/** Permanent
|
|
180
|
+
export type Configurable<numberType = number> = {
|
|
181
|
+
/** Permanent account address. */
|
|
179
182
|
account: Address.Address
|
|
180
183
|
/** Complete current configuration, included in every signature. */
|
|
181
|
-
config:
|
|
184
|
+
config: AccountConfig.Config<bigint, numberType>
|
|
182
185
|
/** Primitive owner approvals in ascending recovered-address order. */
|
|
183
186
|
signatures: readonly Primitive<numberType>[]
|
|
184
|
-
type: '
|
|
187
|
+
type: 'configurable'
|
|
185
188
|
}
|
|
186
189
|
|
|
187
190
|
/** RLP-encoded `[account, config, signatures]`, without the `0x05` type byte. */
|
|
188
|
-
export type
|
|
191
|
+
export type ConfigurableRpc = Hex.Hex
|
|
189
192
|
|
|
190
193
|
export type P256<numberType = number> = {
|
|
191
194
|
prehash: boolean
|
|
@@ -328,9 +331,9 @@ export function assert(envelope: PartialBy<SignatureEnvelope, 'type'>): void {
|
|
|
328
331
|
return
|
|
329
332
|
}
|
|
330
333
|
|
|
331
|
-
if (type === '
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
+
if (type === 'configurable') {
|
|
335
|
+
const configurable = envelope as Configurable
|
|
336
|
+
assertConfigurable(configurable)
|
|
334
337
|
return
|
|
335
338
|
}
|
|
336
339
|
}
|
|
@@ -338,10 +341,10 @@ export function assert(envelope: PartialBy<SignatureEnvelope, 'type'>): void {
|
|
|
338
341
|
export declare namespace assert {
|
|
339
342
|
type ErrorType =
|
|
340
343
|
| CoercionError
|
|
341
|
-
|
|
|
344
|
+
| InvalidApprovalError
|
|
342
345
|
| MissingPropertiesError
|
|
343
|
-
|
|
|
344
|
-
|
|
|
346
|
+
| AccountConfig.assert.ErrorType
|
|
347
|
+
| AccountConfig.getAddress.ErrorType
|
|
345
348
|
| Signature.assert.ErrorType
|
|
346
349
|
| Errors.GlobalErrorType
|
|
347
350
|
}
|
|
@@ -350,13 +353,13 @@ function assertKeychainVersion(keychain: Keychain): void {
|
|
|
350
353
|
if (keychain.version !== 'v1') return
|
|
351
354
|
let inner = keychain.inner
|
|
352
355
|
while (getType(inner) === 'keychain') inner = (inner as Keychain).inner
|
|
353
|
-
if (getType(inner) === '
|
|
354
|
-
throw new
|
|
355
|
-
reason: '
|
|
356
|
+
if (getType(inner) === 'configurable')
|
|
357
|
+
throw new InvalidApprovalError({
|
|
358
|
+
reason: 'configurable access keys require keychain V2',
|
|
356
359
|
})
|
|
357
360
|
}
|
|
358
361
|
|
|
359
|
-
function
|
|
362
|
+
function assertConfigurable(envelope: Configurable): void {
|
|
360
363
|
const missing: string[] = []
|
|
361
364
|
if (!envelope.account) missing.push('account')
|
|
362
365
|
if (!envelope.config) missing.push('config')
|
|
@@ -365,47 +368,47 @@ function assertMultisig(envelope: Multisig): void {
|
|
|
365
368
|
throw new MissingPropertiesError({
|
|
366
369
|
envelope,
|
|
367
370
|
missing,
|
|
368
|
-
type: '
|
|
371
|
+
type: 'configurable',
|
|
369
372
|
})
|
|
370
373
|
if (!Address.validate(envelope.account))
|
|
371
|
-
throw new
|
|
372
|
-
reason: '
|
|
374
|
+
throw new InvalidApprovalError({
|
|
375
|
+
reason: 'configurable account is invalid',
|
|
373
376
|
})
|
|
374
377
|
if (Hex.toBigInt(envelope.account) === 0n)
|
|
375
|
-
throw new
|
|
376
|
-
reason: '
|
|
378
|
+
throw new InvalidApprovalError({
|
|
379
|
+
reason: 'configurable account cannot be zero',
|
|
377
380
|
})
|
|
378
381
|
if (envelope.signatures.length === 0)
|
|
379
|
-
throw new
|
|
380
|
-
reason: '
|
|
382
|
+
throw new InvalidApprovalError({
|
|
383
|
+
reason: 'configurable signatures cannot be empty',
|
|
381
384
|
})
|
|
382
|
-
if (envelope.signatures.length >
|
|
383
|
-
throw new
|
|
384
|
-
reason: `
|
|
385
|
+
if (envelope.signatures.length > AccountConfig.maxSignatures)
|
|
386
|
+
throw new InvalidApprovalError({
|
|
387
|
+
reason: `configurable signatures exceed ${AccountConfig.maxSignatures}`,
|
|
385
388
|
})
|
|
386
389
|
|
|
387
|
-
|
|
390
|
+
AccountConfig.assert(envelope.config)
|
|
388
391
|
if (
|
|
389
392
|
(envelope.config.version ?? 0n) === 0n &&
|
|
390
393
|
envelope.config.owners.some((owner) =>
|
|
391
394
|
Address.isEqual(owner.owner, envelope.account),
|
|
392
395
|
)
|
|
393
396
|
)
|
|
394
|
-
throw new
|
|
397
|
+
throw new InvalidApprovalError({
|
|
395
398
|
reason: 'initial account cannot be an owner',
|
|
396
399
|
})
|
|
397
400
|
|
|
398
401
|
for (const inner of envelope.signatures) {
|
|
399
402
|
const type = getType(inner as SignatureEnvelope)
|
|
400
|
-
if (type === 'keychain' || type === '
|
|
401
|
-
throw new
|
|
403
|
+
if (type === 'keychain' || type === 'configurable')
|
|
404
|
+
throw new InvalidApprovalError({
|
|
402
405
|
reason: 'owner approvals must be primitive signatures',
|
|
403
406
|
})
|
|
404
407
|
assert(inner)
|
|
405
408
|
|
|
406
|
-
if (Hex.size(serialize(inner)) >
|
|
407
|
-
throw new
|
|
408
|
-
reason: `
|
|
409
|
+
if (Hex.size(serialize(inner)) > AccountConfig.maxOwnerSignatureBytes)
|
|
410
|
+
throw new InvalidApprovalError({
|
|
411
|
+
reason: `configurable owner signature exceeds ${AccountConfig.maxOwnerSignatureBytes} bytes`,
|
|
409
412
|
})
|
|
410
413
|
}
|
|
411
414
|
}
|
|
@@ -447,9 +450,9 @@ export function extractAddress(
|
|
|
447
450
|
if (root) return signature.userAddress
|
|
448
451
|
return extractAddress({ ...options, signature: signature.inner })
|
|
449
452
|
}
|
|
450
|
-
//
|
|
451
|
-
// derived
|
|
452
|
-
if (signature.type === '
|
|
453
|
+
// Configurable signatures have no single signer; the recovered sender is the
|
|
454
|
+
// derived configurable account address.
|
|
455
|
+
if (signature.type === 'configurable') return signature.account
|
|
453
456
|
return Address.fromPublicKey(extractPublicKey(options))
|
|
454
457
|
}
|
|
455
458
|
|
|
@@ -516,9 +519,9 @@ export function extractPublicKey(
|
|
|
516
519
|
return signature.publicKey
|
|
517
520
|
case 'keychain':
|
|
518
521
|
return extractPublicKey({ payload, signature: signature.inner })
|
|
519
|
-
case '
|
|
520
|
-
// A
|
|
521
|
-
// single public key; recover the
|
|
522
|
+
case 'configurable':
|
|
523
|
+
// A configurable signature aggregates multiple owner approvals and has no
|
|
524
|
+
// single public key; recover the configurable account via `extractAddress`.
|
|
522
525
|
throw new CoercionError({ envelope: signature })
|
|
523
526
|
}
|
|
524
527
|
}
|
|
@@ -681,46 +684,45 @@ function deserialize_(value: Serialized): SignatureEnvelope {
|
|
|
681
684
|
return envelope
|
|
682
685
|
}
|
|
683
686
|
|
|
684
|
-
if (typeId ===
|
|
687
|
+
if (typeId === serializedConfigurableType) {
|
|
685
688
|
const decoded = Rlp.toHex(data)
|
|
686
689
|
if (!Array.isArray(decoded) || decoded.length !== 3)
|
|
687
690
|
throw new InvalidSerializedError({
|
|
688
|
-
reason:
|
|
691
|
+
reason:
|
|
692
|
+
'invalid configurable wire shape: expected exactly three fields',
|
|
689
693
|
serialized,
|
|
690
694
|
})
|
|
691
695
|
const [account, config, signatures] = decoded
|
|
692
696
|
if (typeof account !== 'string' || !Address.validate(account))
|
|
693
697
|
throw new InvalidSerializedError({
|
|
694
|
-
reason: 'invalid
|
|
698
|
+
reason: 'invalid configurable account',
|
|
695
699
|
serialized,
|
|
696
700
|
})
|
|
697
701
|
if (!Array.isArray(config))
|
|
698
702
|
throw new InvalidSerializedError({
|
|
699
|
-
reason: 'invalid
|
|
703
|
+
reason: 'invalid account config',
|
|
700
704
|
serialized,
|
|
701
705
|
})
|
|
702
706
|
if (
|
|
703
707
|
!Array.isArray(signatures) ||
|
|
704
708
|
signatures.length === 0 ||
|
|
705
|
-
signatures.length >
|
|
709
|
+
signatures.length > AccountConfig.maxSignatures
|
|
706
710
|
)
|
|
707
711
|
throw new InvalidSerializedError({
|
|
708
|
-
reason: 'invalid
|
|
712
|
+
reason: 'invalid configurable signatures list',
|
|
709
713
|
serialized,
|
|
710
714
|
})
|
|
711
715
|
const envelope = {
|
|
712
|
-
type: '
|
|
716
|
+
type: 'configurable',
|
|
713
717
|
account,
|
|
714
|
-
config:
|
|
715
|
-
config as unknown as MultisigConfig.Tuple,
|
|
716
|
-
),
|
|
718
|
+
config: AccountConfig.fromTuple(config as unknown as AccountConfig.Tuple),
|
|
717
719
|
signatures: signatures.map((signature) => {
|
|
718
720
|
if (
|
|
719
721
|
!Hex.validate(signature) ||
|
|
720
|
-
Hex.size(signature) >
|
|
722
|
+
Hex.size(signature) > AccountConfig.maxOwnerSignatureBytes
|
|
721
723
|
)
|
|
722
724
|
throw new InvalidSerializedError({
|
|
723
|
-
reason: 'invalid
|
|
725
|
+
reason: 'invalid configurable owner signature',
|
|
724
726
|
serialized,
|
|
725
727
|
})
|
|
726
728
|
// Reject recursive envelopes before decoding their contents.
|
|
@@ -734,13 +736,13 @@ function deserialize_(value: Serialized): SignatureEnvelope {
|
|
|
734
736
|
})
|
|
735
737
|
return deserialize(signature) as Primitive
|
|
736
738
|
}),
|
|
737
|
-
} satisfies
|
|
738
|
-
|
|
739
|
+
} satisfies Configurable
|
|
740
|
+
assertConfigurable(envelope)
|
|
739
741
|
return envelope
|
|
740
742
|
}
|
|
741
743
|
|
|
742
744
|
throw new InvalidSerializedError({
|
|
743
|
-
reason: `Unknown signature type identifier: ${typeId}. Expected ${serializedP256Type} (P256), ${serializedWebAuthnType} (WebAuthn), ${serializedKeychainType} (Keychain V1), ${serializedKeychainV2Type} (Keychain V2), or ${
|
|
745
|
+
reason: `Unknown signature type identifier: ${typeId}. Expected ${serializedP256Type} (P256), ${serializedWebAuthnType} (WebAuthn), ${serializedKeychainType} (Keychain V1), ${serializedKeychainV2Type} (Keychain V2), or ${serializedConfigurableType} (Configurable)`,
|
|
744
746
|
serialized,
|
|
745
747
|
})
|
|
746
748
|
}
|
|
@@ -871,7 +873,7 @@ function deserialize_(value: Serialized): SignatureEnvelope {
|
|
|
871
873
|
* ```
|
|
872
874
|
*
|
|
873
875
|
* @example
|
|
874
|
-
* ###
|
|
876
|
+
* ### Configurable
|
|
875
877
|
*
|
|
876
878
|
* Include the permanent account and current configuration on every transaction.
|
|
877
879
|
*
|
|
@@ -920,12 +922,12 @@ export function from<const value extends from.Value>(
|
|
|
920
922
|
|
|
921
923
|
const type = getType(value)
|
|
922
924
|
|
|
923
|
-
if (type === '
|
|
924
|
-
const
|
|
925
|
+
if (type === 'configurable') {
|
|
926
|
+
const configurable = value as Configurable
|
|
925
927
|
return {
|
|
926
|
-
...
|
|
927
|
-
config:
|
|
928
|
-
signatures:
|
|
928
|
+
...configurable,
|
|
929
|
+
config: AccountConfig.from(configurable.config),
|
|
930
|
+
signatures: configurable.signatures.map((signature) => from(signature)),
|
|
929
931
|
type,
|
|
930
932
|
} as never
|
|
931
933
|
}
|
|
@@ -972,14 +974,14 @@ export declare namespace from {
|
|
|
972
974
|
payload?: Hex.Hex | Bytes.Bytes | undefined
|
|
973
975
|
}
|
|
974
976
|
|
|
975
|
-
/**
|
|
976
|
-
type
|
|
977
|
-
config:
|
|
978
|
-
type?: '
|
|
977
|
+
/** Configurable input with an explicit account and a normalizable configuration. */
|
|
978
|
+
type ConfigurableFromConfig = Omit<Configurable, 'config' | 'type'> & {
|
|
979
|
+
config: AccountConfig.Input
|
|
980
|
+
type?: 'configurable' | undefined
|
|
979
981
|
}
|
|
980
982
|
|
|
981
983
|
type Value =
|
|
982
|
-
|
|
|
984
|
+
| ConfigurableFromConfig
|
|
983
985
|
| UnionPartialBy<SignatureEnvelope, 'prehash' | 'type'>
|
|
984
986
|
| Secp256k1Flat
|
|
985
987
|
| Serialized
|
|
@@ -990,8 +992,8 @@ export declare namespace from {
|
|
|
990
992
|
? SignatureEnvelope
|
|
991
993
|
: value extends Secp256k1Flat
|
|
992
994
|
? Secp256k1
|
|
993
|
-
: value extends
|
|
994
|
-
? Extract<SignatureEnvelope, { type: '
|
|
995
|
+
: value extends ConfigurableFromConfig
|
|
996
|
+
? Extract<SignatureEnvelope, { type: 'configurable' }>
|
|
995
997
|
: IsNarrowable<value, SignatureEnvelope> extends true
|
|
996
998
|
? SignatureEnvelope
|
|
997
999
|
: Assign<
|
|
@@ -1026,8 +1028,10 @@ export declare namespace from {
|
|
|
1026
1028
|
*/
|
|
1027
1029
|
export function fromRpc(envelope: SignatureEnvelopeRpc): SignatureEnvelope {
|
|
1028
1030
|
if (typeof envelope === 'string') {
|
|
1029
|
-
const signature = deserialize(
|
|
1030
|
-
|
|
1031
|
+
const signature = deserialize(
|
|
1032
|
+
Hex.concat(serializedConfigurableType, envelope),
|
|
1033
|
+
)
|
|
1034
|
+
if (signature.type !== 'configurable') throw new CoercionError({ envelope })
|
|
1031
1035
|
return signature
|
|
1032
1036
|
}
|
|
1033
1037
|
if (envelope.type === 'secp256k1')
|
|
@@ -1122,7 +1126,7 @@ export declare namespace fromRpc {
|
|
|
1122
1126
|
| assert.ErrorType
|
|
1123
1127
|
| CoercionError
|
|
1124
1128
|
| InvalidSerializedError
|
|
1125
|
-
|
|
|
1129
|
+
| AccountConfig.getAddress.ErrorType
|
|
1126
1130
|
| Signature.fromRpc.ErrorType
|
|
1127
1131
|
| Errors.GlobalErrorType
|
|
1128
1132
|
}
|
|
@@ -1196,9 +1200,9 @@ export function getType<
|
|
|
1196
1200
|
if ('userAddress' in envelope && 'inner' in envelope)
|
|
1197
1201
|
return 'keychain' as never
|
|
1198
1202
|
|
|
1199
|
-
// Detect
|
|
1203
|
+
// Detect Configurable signature
|
|
1200
1204
|
if ('account' in envelope && 'config' in envelope && 'signatures' in envelope)
|
|
1201
|
-
return '
|
|
1205
|
+
return 'configurable' as never
|
|
1202
1206
|
|
|
1203
1207
|
throw new CoercionError({
|
|
1204
1208
|
envelope,
|
|
@@ -1214,7 +1218,7 @@ export function getType<
|
|
|
1214
1218
|
* - WebAuthn: `0x02` + webauthnData (variable) + r (32) + s (32) + pubKeyX (32) + pubKeyY (32)
|
|
1215
1219
|
* - Keychain V1: `0x03` + userAddress (20) + inner signature (recursive)
|
|
1216
1220
|
* - Keychain V2: `0x04` + userAddress (20) + inner signature (recursive)
|
|
1217
|
-
* -
|
|
1221
|
+
* - Configurable: `0x05` + RLP `[account, config, signatures]`
|
|
1218
1222
|
*
|
|
1219
1223
|
* [Signature Types](https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction#signature-types)
|
|
1220
1224
|
*
|
|
@@ -1299,15 +1303,15 @@ export function serialize(
|
|
|
1299
1303
|
)
|
|
1300
1304
|
}
|
|
1301
1305
|
|
|
1302
|
-
if (type === '
|
|
1303
|
-
const
|
|
1304
|
-
assert(
|
|
1306
|
+
if (type === 'configurable') {
|
|
1307
|
+
const configurable = envelope as Configurable
|
|
1308
|
+
assert(configurable)
|
|
1305
1309
|
return Hex.concat(
|
|
1306
|
-
|
|
1310
|
+
serializedConfigurableType,
|
|
1307
1311
|
Rlp.fromHex([
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1312
|
+
configurable.account,
|
|
1313
|
+
AccountConfig.toTuple(configurable.config),
|
|
1314
|
+
configurable.signatures.map((signature) => serialize(signature)),
|
|
1311
1315
|
]),
|
|
1312
1316
|
options.magic ? magicBytes : '0x',
|
|
1313
1317
|
)
|
|
@@ -1339,14 +1343,14 @@ export declare namespace serialize {
|
|
|
1339
1343
|
/**
|
|
1340
1344
|
* Sorts primitive owner approvals by recovered address.
|
|
1341
1345
|
*
|
|
1342
|
-
* Recovery uses the account and current config version's
|
|
1346
|
+
* Recovery uses the account and current config version's owner approval digest.
|
|
1343
1347
|
* Duplicate or non-owner approvals must still be rejected by the node.
|
|
1344
1348
|
*
|
|
1345
1349
|
* @example
|
|
1346
1350
|
* ```ts twoslash
|
|
1347
1351
|
* import { SignatureEnvelope } from 'ox/tempo'
|
|
1348
1352
|
*
|
|
1349
|
-
* const ordered = SignatureEnvelope.
|
|
1353
|
+
* const ordered = SignatureEnvelope.sortApprovals({
|
|
1350
1354
|
* account: '0x2222222222222222222222222222222222222222',
|
|
1351
1355
|
* config: { version: 0n },
|
|
1352
1356
|
* payload: `0x${'ab'.repeat(32)}`,
|
|
@@ -1357,11 +1361,11 @@ export declare namespace serialize {
|
|
|
1357
1361
|
* @param value - Account, version, payload, and primitive approvals.
|
|
1358
1362
|
* @returns The approvals in ascending recovered-address order.
|
|
1359
1363
|
*/
|
|
1360
|
-
export function
|
|
1361
|
-
value:
|
|
1364
|
+
export function sortApprovals(
|
|
1365
|
+
value: sortApprovals.Value,
|
|
1362
1366
|
): readonly Primitive[] {
|
|
1363
1367
|
const { signatures } = value
|
|
1364
|
-
const digest =
|
|
1368
|
+
const digest = AccountConfig.getSignPayload(value)
|
|
1365
1369
|
// Recover each signer once (decorate–sort–undecorate) rather than inside the
|
|
1366
1370
|
// comparator.
|
|
1367
1371
|
return signatures
|
|
@@ -1373,14 +1377,14 @@ export function sortMultisigApprovals(
|
|
|
1373
1377
|
.map((entry) => entry.signature)
|
|
1374
1378
|
}
|
|
1375
1379
|
|
|
1376
|
-
export declare namespace
|
|
1377
|
-
type Value =
|
|
1380
|
+
export declare namespace sortApprovals {
|
|
1381
|
+
type Value = AccountConfig.getSignPayload.Value & {
|
|
1378
1382
|
/** Primitive owner approvals to order. */
|
|
1379
1383
|
signatures: readonly Primitive[]
|
|
1380
1384
|
}
|
|
1381
1385
|
|
|
1382
1386
|
type ErrorType =
|
|
1383
|
-
|
|
|
1387
|
+
| AccountConfig.getSignPayload.ErrorType
|
|
1384
1388
|
| extractAddress.ErrorType
|
|
1385
1389
|
| Errors.GlobalErrorType
|
|
1386
1390
|
}
|
|
@@ -1459,20 +1463,20 @@ export function toRpc<const envelope extends toRpc.Input>(
|
|
|
1459
1463
|
} as never
|
|
1460
1464
|
}
|
|
1461
1465
|
|
|
1462
|
-
if (type === '
|
|
1463
|
-
const
|
|
1466
|
+
if (type === 'configurable') {
|
|
1467
|
+
const configurable = envelope as Configurable<Hex.Hex | number>
|
|
1464
1468
|
return Hex.slice(
|
|
1465
1469
|
serialize({
|
|
1466
|
-
...
|
|
1470
|
+
...configurable,
|
|
1467
1471
|
config: {
|
|
1468
|
-
...
|
|
1469
|
-
threshold: Number(
|
|
1470
|
-
owners:
|
|
1472
|
+
...configurable.config,
|
|
1473
|
+
threshold: Number(configurable.config.threshold),
|
|
1474
|
+
owners: configurable.config.owners.map((owner) => ({
|
|
1471
1475
|
...owner,
|
|
1472
1476
|
weight: Number(owner.weight),
|
|
1473
1477
|
})),
|
|
1474
1478
|
},
|
|
1475
|
-
signatures:
|
|
1479
|
+
signatures: configurable.signatures.map((signature) =>
|
|
1476
1480
|
signature.type === 'secp256k1'
|
|
1477
1481
|
? {
|
|
1478
1482
|
...signature,
|
|
@@ -1511,8 +1515,8 @@ export declare namespace toRpc {
|
|
|
1511
1515
|
? WebAuthnRpc
|
|
1512
1516
|
: GetType<envelope> extends 'keychain'
|
|
1513
1517
|
? KeychainRpc
|
|
1514
|
-
: GetType<envelope> extends '
|
|
1515
|
-
?
|
|
1518
|
+
: GetType<envelope> extends 'configurable'
|
|
1519
|
+
? ConfigurableRpc
|
|
1516
1520
|
: SignatureEnvelopeRpc
|
|
1517
1521
|
|
|
1518
1522
|
type ErrorType =
|
|
@@ -1769,7 +1773,7 @@ export class MissingPropertiesError extends Errors.BaseError {
|
|
|
1769
1773
|
}: {
|
|
1770
1774
|
envelope: unknown
|
|
1771
1775
|
missing: string[]
|
|
1772
|
-
type: Type | 'keychain' | '
|
|
1776
|
+
type: Type | 'keychain' | 'configurable'
|
|
1773
1777
|
}) {
|
|
1774
1778
|
super(
|
|
1775
1779
|
`Signature envelope of type "${type}" is missing required properties: ${missing.map((m) => `\`${m}\``).join(', ')}.\n\nProvided: ${Json.stringify(envelope)}`,
|
|
@@ -1790,12 +1794,12 @@ export class InvalidSerializedError extends Errors.BaseError {
|
|
|
1790
1794
|
}
|
|
1791
1795
|
|
|
1792
1796
|
/**
|
|
1793
|
-
* Error thrown when a
|
|
1797
|
+
* Error thrown when a configurable account owner approval is invalid.
|
|
1794
1798
|
*/
|
|
1795
|
-
export class
|
|
1796
|
-
override readonly name = 'SignatureEnvelope.
|
|
1799
|
+
export class InvalidApprovalError extends Errors.BaseError {
|
|
1800
|
+
override readonly name = 'SignatureEnvelope.InvalidApprovalError'
|
|
1797
1801
|
constructor({ reason }: { reason: string }) {
|
|
1798
|
-
super(`Invalid
|
|
1802
|
+
super(`Invalid owner approval: ${reason}.`)
|
|
1799
1803
|
}
|
|
1800
1804
|
}
|
|
1801
1805
|
|
package/src/tempo/TokenRole.ts
CHANGED
|
@@ -7,6 +7,7 @@ export const roles = [
|
|
|
7
7
|
'unpause',
|
|
8
8
|
'issuer',
|
|
9
9
|
'burnBlocked',
|
|
10
|
+
'burnAt',
|
|
10
11
|
] as const
|
|
11
12
|
export type TokenRole = (typeof roles)[number]
|
|
12
13
|
|
|
@@ -16,13 +17,15 @@ export const toPreHashed = {
|
|
|
16
17
|
unpause: 'UNPAUSE_ROLE',
|
|
17
18
|
issuer: 'ISSUER_ROLE',
|
|
18
19
|
burnBlocked: 'BURN_BLOCKED_ROLE',
|
|
20
|
+
burnAt: 'BURN_AT_ROLE',
|
|
19
21
|
} as const satisfies Record<TokenRole, string>
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Serializes a token role to its keccak256 hash representation.
|
|
23
25
|
*
|
|
24
26
|
* TIP-20 includes a built-in RBAC system with roles like `ISSUER_ROLE` (mint/burn),
|
|
25
|
-
* `PAUSE_ROLE`/`UNPAUSE_ROLE` (emergency controls),
|
|
27
|
+
* `PAUSE_ROLE`/`UNPAUSE_ROLE` (emergency controls), `BURN_BLOCKED_ROLE` (compliance), and
|
|
28
|
+
* `BURN_AT_ROLE` (burn from any unprotected account, T12+).
|
|
26
29
|
*
|
|
27
30
|
* [TIP-20 RBAC](https://docs.tempo.xyz/protocol/tip20/overview#role-based-access-control-rbac)
|
|
28
31
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Transaction } from 'ox/tempo'
|
|
2
2
|
import { describe, expect, test } from 'vp/test'
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const configurableRpcSignature =
|
|
5
5
|
'0xf89794c4a590afa7337e5cd5eb3aa60cacf91c5400044bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b' as const
|
|
6
6
|
|
|
7
7
|
describe('fromRpc', () => {
|
|
@@ -517,7 +517,7 @@ describe('toRpc', () => {
|
|
|
517
517
|
})
|
|
518
518
|
|
|
519
519
|
describe('roundtrip', () => {
|
|
520
|
-
test('behavior:
|
|
520
|
+
test('behavior: configurable RPC signature', () => {
|
|
521
521
|
const rpc = {
|
|
522
522
|
accessList: [],
|
|
523
523
|
blockHash:
|
|
@@ -532,7 +532,7 @@ describe('roundtrip', () => {
|
|
|
532
532
|
maxFeePerGas: '0x2',
|
|
533
533
|
maxPriorityFeePerGas: '0x1',
|
|
534
534
|
nonce: '0x357',
|
|
535
|
-
signature:
|
|
535
|
+
signature: configurableRpcSignature,
|
|
536
536
|
transactionIndex: '0x2',
|
|
537
537
|
type: '0x76',
|
|
538
538
|
} as const satisfies Transaction.TempoRpc
|
|
@@ -567,7 +567,7 @@ describe('roundtrip', () => {
|
|
|
567
567
|
"type": "secp256k1",
|
|
568
568
|
},
|
|
569
569
|
],
|
|
570
|
-
"type": "
|
|
570
|
+
"type": "configurable",
|
|
571
571
|
},
|
|
572
572
|
}
|
|
573
573
|
`)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type * as ox_TransactionRequest from '../core/TransactionRequest.js'
|
|
2
2
|
import * as Signature from '../core/Signature.js'
|
|
3
3
|
import { describe, expectTypeOf, test } from 'vp/test'
|
|
4
|
-
import * as
|
|
4
|
+
import * as AccountSimulation from './AccountSimulation.js'
|
|
5
5
|
import * as SignatureEnvelope from './SignatureEnvelope.js'
|
|
6
6
|
import * as TransactionRequest from './TransactionRequest.js'
|
|
7
7
|
import * as TxEnvelopeTempo from './TxEnvelopeTempo.js'
|
|
@@ -49,12 +49,23 @@ describe('TransactionRequest type', () => {
|
|
|
49
49
|
>()
|
|
50
50
|
})
|
|
51
51
|
|
|
52
|
-
test('carries domain and RPC
|
|
52
|
+
test('carries domain and RPC account simulations', () => {
|
|
53
53
|
expectTypeOf<
|
|
54
|
-
TransactionRequest.TransactionRequest['
|
|
55
|
-
>().toEqualTypeOf<
|
|
54
|
+
TransactionRequest.TransactionRequest['accountSimulation']
|
|
55
|
+
>().toEqualTypeOf<AccountSimulation.Spec | undefined>()
|
|
56
56
|
expectTypeOf<TransactionRequest.Rpc['multisigSimulation']>().toEqualTypeOf<
|
|
57
|
-
|
|
57
|
+
AccountSimulation.Rpc | undefined
|
|
58
|
+
>()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('maps the configurable key type to the RPC multisig key type', () => {
|
|
62
|
+
expectTypeOf<
|
|
63
|
+
TransactionRequest.TransactionRequest['keyType']
|
|
64
|
+
>().toEqualTypeOf<
|
|
65
|
+
'configurable' | 'p256' | 'secp256k1' | 'webAuthn' | undefined
|
|
66
|
+
>()
|
|
67
|
+
expectTypeOf<TransactionRequest.Rpc['keyType']>().toEqualTypeOf<
|
|
68
|
+
'multisig' | 'p256' | 'secp256k1' | 'webAuthn' | undefined
|
|
58
69
|
>()
|
|
59
70
|
})
|
|
60
71
|
})
|
|
@@ -73,6 +84,7 @@ describe('toEnvelope', () => {
|
|
|
73
84
|
// Forces us to update toEnvelope when fields are added to the request.
|
|
74
85
|
TransactionRequest.toEnvelope({
|
|
75
86
|
accessList: [],
|
|
87
|
+
accountSimulation: undefined,
|
|
76
88
|
authorizationList: undefined,
|
|
77
89
|
calls: undefined,
|
|
78
90
|
chainId: 1,
|
|
@@ -87,7 +99,6 @@ describe('toEnvelope', () => {
|
|
|
87
99
|
keyType: 'secp256k1',
|
|
88
100
|
maxFeePerGas: 1n,
|
|
89
101
|
maxPriorityFeePerGas: 1n,
|
|
90
|
-
multisigSimulation: undefined,
|
|
91
102
|
nonce: 0n,
|
|
92
103
|
nonceKey: 'random',
|
|
93
104
|
signature: undefined,
|