ox 0.12.4 → 0.13.1
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 +21 -0
- package/_cjs/core/P256.js +1 -1
- package/_cjs/core/P256.js.map +1 -1
- package/_cjs/core/WebAuthnP256.js +15 -256
- package/_cjs/core/WebAuthnP256.js.map +1 -1
- package/_cjs/core/WebCryptoP256.js +3 -1
- package/_cjs/core/WebCryptoP256.js.map +1 -1
- package/_cjs/core/internal/webauthn.js +5 -13
- package/_cjs/core/internal/webauthn.js.map +1 -1
- package/_cjs/index.docs.js +1 -0
- package/_cjs/index.docs.js.map +1 -1
- package/_cjs/tempo/KeyAuthorization.js +18 -3
- package/_cjs/tempo/KeyAuthorization.js.map +1 -1
- package/_cjs/tempo/SignatureEnvelope.js +26 -0
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/tempo/TxEnvelopeTempo.js +5 -10
- package/_cjs/tempo/TxEnvelopeTempo.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_cjs/webauthn/Authentication.js +246 -0
- package/_cjs/webauthn/Authentication.js.map +1 -0
- package/_cjs/webauthn/Authenticator.js +55 -0
- package/_cjs/webauthn/Authenticator.js.map +1 -0
- package/_cjs/webauthn/Credential.js +53 -0
- package/_cjs/webauthn/Credential.js.map +1 -0
- package/_cjs/webauthn/Registration.js +349 -0
- package/_cjs/webauthn/Registration.js.map +1 -0
- package/_cjs/webauthn/Types.js +3 -0
- package/_cjs/webauthn/Types.js.map +1 -0
- package/_cjs/webauthn/index.js +9 -0
- package/_cjs/webauthn/index.js.map +1 -0
- package/_cjs/webauthn/internal/utils.js +53 -0
- package/_cjs/webauthn/internal/utils.js.map +1 -0
- package/_esm/core/P256.js +1 -1
- package/_esm/core/P256.js.map +1 -1
- package/_esm/core/WebAuthnP256.js +13 -261
- package/_esm/core/WebAuthnP256.js.map +1 -1
- package/_esm/core/WebCryptoP256.js +4 -1
- package/_esm/core/WebCryptoP256.js.map +1 -1
- package/_esm/core/internal/webauthn.js +5 -13
- package/_esm/core/internal/webauthn.js.map +1 -1
- package/_esm/erc8021/index.js +2 -2
- package/_esm/index.docs.js +1 -0
- package/_esm/index.docs.js.map +1 -1
- package/_esm/tempo/KeyAuthorization.js +66 -3
- package/_esm/tempo/KeyAuthorization.js.map +1 -1
- package/_esm/tempo/SignatureEnvelope.js +74 -0
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/tempo/TransactionReceipt.js +1 -1
- package/_esm/tempo/TransactionRequest.js +1 -1
- package/_esm/tempo/TxEnvelopeTempo.js +5 -10
- package/_esm/tempo/TxEnvelopeTempo.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_esm/webauthn/Authentication.js +453 -0
- package/_esm/webauthn/Authentication.js.map +1 -0
- package/_esm/webauthn/Authenticator.js +176 -0
- package/_esm/webauthn/Authenticator.js.map +1 -0
- package/_esm/webauthn/Credential.js +95 -0
- package/_esm/webauthn/Credential.js.map +1 -0
- package/_esm/webauthn/Registration.js +512 -0
- package/_esm/webauthn/Registration.js.map +1 -0
- package/_esm/webauthn/Types.js +2 -0
- package/_esm/webauthn/Types.js.map +1 -0
- package/_esm/webauthn/index.js +31 -0
- package/_esm/webauthn/index.js.map +1 -0
- package/_esm/webauthn/internal/utils.js +52 -0
- package/_esm/webauthn/internal/utils.js.map +1 -0
- package/_types/core/WebAuthnP256.d.ts +33 -208
- package/_types/core/WebAuthnP256.d.ts.map +1 -1
- package/_types/core/WebCryptoP256.d.ts +2 -0
- package/_types/core/WebCryptoP256.d.ts.map +1 -1
- package/_types/core/internal/webauthn.d.ts +2 -110
- package/_types/core/internal/webauthn.d.ts.map +1 -1
- package/_types/erc8021/index.d.ts +2 -2
- package/_types/index.docs.d.ts +1 -0
- package/_types/index.docs.d.ts.map +1 -1
- package/_types/tempo/KeyAuthorization.d.ts +57 -0
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/SignatureEnvelope.d.ts +75 -0
- package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/_types/tempo/Transaction.d.ts +2 -2
- package/_types/tempo/TransactionReceipt.d.ts +2 -2
- package/_types/tempo/TransactionRequest.d.ts +2 -2
- package/_types/tempo/TxEnvelopeTempo.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/_types/webauthn/Authentication.d.ts +324 -0
- package/_types/webauthn/Authentication.d.ts.map +1 -0
- package/_types/webauthn/Authenticator.d.ts +182 -0
- package/_types/webauthn/Authenticator.d.ts.map +1 -0
- package/_types/webauthn/Credential.d.ts +77 -0
- package/_types/webauthn/Credential.d.ts.map +1 -0
- package/_types/webauthn/Registration.d.ts +308 -0
- package/_types/webauthn/Registration.d.ts.map +1 -0
- package/_types/webauthn/Types.d.ts +106 -0
- package/_types/webauthn/Types.d.ts.map +1 -0
- package/_types/webauthn/index.d.ts +33 -0
- package/_types/webauthn/index.d.ts.map +1 -0
- package/_types/webauthn/internal/utils.d.ts +17 -0
- package/_types/webauthn/internal/utils.d.ts.map +1 -0
- package/core/P256.ts +1 -1
- package/core/WebAuthnP256.ts +37 -582
- package/core/WebCryptoP256.ts +6 -1
- package/core/internal/webauthn.ts +6 -165
- package/erc8021/index.ts +2 -2
- package/index.docs.ts +1 -0
- package/package.json +31 -1
- package/tempo/KeyAuthorization.test.ts +139 -0
- package/tempo/KeyAuthorization.ts +82 -3
- package/tempo/SignatureEnvelope.test.ts +147 -0
- package/tempo/SignatureEnvelope.ts +113 -0
- package/tempo/Transaction.ts +2 -2
- package/tempo/TransactionReceipt.ts +2 -2
- package/tempo/TransactionRequest.ts +2 -2
- package/tempo/TxEnvelopeTempo.ts +5 -12
- package/tempo/e2e.test.ts +265 -0
- package/version.ts +1 -1
- package/webauthn/Authentication/package.json +6 -0
- package/webauthn/Authentication.ts +673 -0
- package/webauthn/Authenticator/package.json +6 -0
- package/webauthn/Authenticator.ts +259 -0
- package/webauthn/Credential/package.json +6 -0
- package/webauthn/Credential.ts +146 -0
- package/webauthn/Registration/package.json +6 -0
- package/webauthn/Registration.ts +805 -0
- package/webauthn/Types/package.json +6 -0
- package/webauthn/Types.ts +158 -0
- package/webauthn/index.ts +38 -0
- package/webauthn/internal/utils.ts +63 -0
- package/webauthn/package.json +6 -0
|
@@ -772,6 +772,153 @@ describe('deserialize', () => {
|
|
|
772
772
|
})
|
|
773
773
|
})
|
|
774
774
|
|
|
775
|
+
describe('extractAddress', () => {
|
|
776
|
+
describe('secp256k1', () => {
|
|
777
|
+
test('default', () => {
|
|
778
|
+
const privateKey = Secp256k1.randomPrivateKey()
|
|
779
|
+
const address = Address.fromPublicKey(
|
|
780
|
+
Secp256k1.getPublicKey({ privateKey }),
|
|
781
|
+
)
|
|
782
|
+
const payload = '0xdeadbeef' as const
|
|
783
|
+
|
|
784
|
+
const signature = Secp256k1.sign({ payload, privateKey })
|
|
785
|
+
const envelope = SignatureEnvelope.from(signature)
|
|
786
|
+
|
|
787
|
+
expect(
|
|
788
|
+
SignatureEnvelope.extractAddress({ payload, signature: envelope }),
|
|
789
|
+
).toBe(address)
|
|
790
|
+
})
|
|
791
|
+
})
|
|
792
|
+
|
|
793
|
+
describe('p256', () => {
|
|
794
|
+
test('default', () => {
|
|
795
|
+
const privateKey = P256.randomPrivateKey()
|
|
796
|
+
const pk = P256.getPublicKey({ privateKey })
|
|
797
|
+
const address = Address.fromPublicKey(pk)
|
|
798
|
+
const payload = '0xdeadbeef' as const
|
|
799
|
+
|
|
800
|
+
const signature = P256.sign({ payload, privateKey })
|
|
801
|
+
const envelope = SignatureEnvelope.from({
|
|
802
|
+
prehash: false,
|
|
803
|
+
publicKey: pk,
|
|
804
|
+
signature,
|
|
805
|
+
})
|
|
806
|
+
|
|
807
|
+
expect(
|
|
808
|
+
SignatureEnvelope.extractAddress({ payload, signature: envelope }),
|
|
809
|
+
).toBe(address)
|
|
810
|
+
})
|
|
811
|
+
})
|
|
812
|
+
|
|
813
|
+
describe('webAuthn', () => {
|
|
814
|
+
test('default', () => {
|
|
815
|
+
const address = Address.fromPublicKey(publicKey)
|
|
816
|
+
|
|
817
|
+
expect(
|
|
818
|
+
SignatureEnvelope.extractAddress({
|
|
819
|
+
payload: '0xdeadbeef',
|
|
820
|
+
signature: signature_webauthn,
|
|
821
|
+
}),
|
|
822
|
+
).toBe(address)
|
|
823
|
+
})
|
|
824
|
+
})
|
|
825
|
+
|
|
826
|
+
describe('keychain', () => {
|
|
827
|
+
test('default', () => {
|
|
828
|
+
const privateKey = Secp256k1.randomPrivateKey()
|
|
829
|
+
const address = Address.fromPublicKey(
|
|
830
|
+
Secp256k1.getPublicKey({ privateKey }),
|
|
831
|
+
)
|
|
832
|
+
const payload = '0xdeadbeef' as const
|
|
833
|
+
|
|
834
|
+
const signature = Secp256k1.sign({ payload, privateKey })
|
|
835
|
+
const envelope = SignatureEnvelope.from({
|
|
836
|
+
userAddress: '0x1234567890123456789012345678901234567890',
|
|
837
|
+
inner: SignatureEnvelope.from(signature),
|
|
838
|
+
})
|
|
839
|
+
|
|
840
|
+
expect(
|
|
841
|
+
SignatureEnvelope.extractAddress({ payload, signature: envelope }),
|
|
842
|
+
).toBe(address)
|
|
843
|
+
})
|
|
844
|
+
|
|
845
|
+
test('behavior: root = true returns userAddress', () => {
|
|
846
|
+
expect(
|
|
847
|
+
SignatureEnvelope.extractAddress({
|
|
848
|
+
payload: '0xdeadbeef',
|
|
849
|
+
signature: signature_keychain_secp256k1,
|
|
850
|
+
root: true,
|
|
851
|
+
}),
|
|
852
|
+
).toBe('0x1234567890123456789012345678901234567890')
|
|
853
|
+
})
|
|
854
|
+
})
|
|
855
|
+
})
|
|
856
|
+
|
|
857
|
+
describe('extractPublicKey', () => {
|
|
858
|
+
describe('secp256k1', () => {
|
|
859
|
+
test('default', () => {
|
|
860
|
+
const privateKey = Secp256k1.randomPrivateKey()
|
|
861
|
+
const pk = Secp256k1.getPublicKey({ privateKey })
|
|
862
|
+
const payload = '0xdeadbeef' as const
|
|
863
|
+
|
|
864
|
+
const signature = Secp256k1.sign({ payload, privateKey })
|
|
865
|
+
const envelope = SignatureEnvelope.from(signature)
|
|
866
|
+
|
|
867
|
+
expect(
|
|
868
|
+
SignatureEnvelope.extractPublicKey({ payload, signature: envelope }),
|
|
869
|
+
).toEqual(pk)
|
|
870
|
+
})
|
|
871
|
+
})
|
|
872
|
+
|
|
873
|
+
describe('p256', () => {
|
|
874
|
+
test('default', () => {
|
|
875
|
+
const privateKey = P256.randomPrivateKey()
|
|
876
|
+
const pk = P256.getPublicKey({ privateKey })
|
|
877
|
+
const payload = '0xdeadbeef' as const
|
|
878
|
+
|
|
879
|
+
const signature = P256.sign({ payload, privateKey })
|
|
880
|
+
const envelope = SignatureEnvelope.from({
|
|
881
|
+
prehash: false,
|
|
882
|
+
publicKey: pk,
|
|
883
|
+
signature,
|
|
884
|
+
})
|
|
885
|
+
|
|
886
|
+
expect(
|
|
887
|
+
SignatureEnvelope.extractPublicKey({ payload, signature: envelope }),
|
|
888
|
+
).toEqual(pk)
|
|
889
|
+
})
|
|
890
|
+
})
|
|
891
|
+
|
|
892
|
+
describe('webAuthn', () => {
|
|
893
|
+
test('default', () => {
|
|
894
|
+
expect(
|
|
895
|
+
SignatureEnvelope.extractPublicKey({
|
|
896
|
+
payload: '0xdeadbeef',
|
|
897
|
+
signature: signature_webauthn,
|
|
898
|
+
}),
|
|
899
|
+
).toEqual(publicKey)
|
|
900
|
+
})
|
|
901
|
+
})
|
|
902
|
+
|
|
903
|
+
describe('keychain', () => {
|
|
904
|
+
test('default', () => {
|
|
905
|
+
const privateKey = Secp256k1.randomPrivateKey()
|
|
906
|
+
const pk = Secp256k1.getPublicKey({ privateKey })
|
|
907
|
+
const payload = '0xdeadbeef' as const
|
|
908
|
+
|
|
909
|
+
const signature = Secp256k1.sign({ payload, privateKey })
|
|
910
|
+
const envelope = SignatureEnvelope.from({
|
|
911
|
+
userAddress: '0x1234567890123456789012345678901234567890',
|
|
912
|
+
inner: SignatureEnvelope.from(signature),
|
|
913
|
+
})
|
|
914
|
+
|
|
915
|
+
expect(
|
|
916
|
+
SignatureEnvelope.extractPublicKey({ payload, signature: envelope }),
|
|
917
|
+
).toEqual(pk)
|
|
918
|
+
})
|
|
919
|
+
})
|
|
920
|
+
})
|
|
921
|
+
|
|
775
922
|
describe('from', () => {
|
|
776
923
|
describe('secp256k1', () => {
|
|
777
924
|
test('behavior: coerces from hex string', () => {
|
|
@@ -269,6 +269,119 @@ export declare namespace assert {
|
|
|
269
269
|
| Errors.GlobalErrorType
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Extracts the address of the signer from a {@link ox#SignatureEnvelope.SignatureEnvelope}.
|
|
274
|
+
*
|
|
275
|
+
* - **secp256k1**: Recovers the address from the payload via `ecrecover`.
|
|
276
|
+
* - **p256** / **webAuthn**: Derives the address from the embedded public key.
|
|
277
|
+
* - **keychain**: Extracts from the inner signature (or returns `userAddress` if `user` is `true`).
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```ts twoslash
|
|
281
|
+
* import { Secp256k1 } from 'ox'
|
|
282
|
+
* import { SignatureEnvelope } from 'ox/tempo'
|
|
283
|
+
*
|
|
284
|
+
* const payload = '0xdeadbeef'
|
|
285
|
+
* const signature = Secp256k1.sign({ payload, privateKey: '0x...' })
|
|
286
|
+
* const envelope = SignatureEnvelope.from(signature)
|
|
287
|
+
*
|
|
288
|
+
* const address = SignatureEnvelope.extractAddress({ // [!code focus]
|
|
289
|
+
* payload, // [!code focus]
|
|
290
|
+
* signature: envelope, // [!code focus]
|
|
291
|
+
* }) // [!code focus]
|
|
292
|
+
* ```
|
|
293
|
+
*
|
|
294
|
+
* @param options - The extraction options.
|
|
295
|
+
* @returns The signer address.
|
|
296
|
+
*/
|
|
297
|
+
export function extractAddress(
|
|
298
|
+
options: extractAddress.Options,
|
|
299
|
+
): extractAddress.ReturnType {
|
|
300
|
+
const { signature, root } = options
|
|
301
|
+
if (signature.type === 'keychain') {
|
|
302
|
+
if (root) return signature.userAddress
|
|
303
|
+
return extractAddress({ ...options, signature: signature.inner })
|
|
304
|
+
}
|
|
305
|
+
return Address.fromPublicKey(extractPublicKey(options))
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export declare namespace extractAddress {
|
|
309
|
+
type Options = {
|
|
310
|
+
/** The sign payload that was signed (only required for secp256k1 signatures). */
|
|
311
|
+
payload: Hex.Hex | Bytes.Bytes
|
|
312
|
+
/** The signature envelope. */
|
|
313
|
+
signature: SignatureEnvelope
|
|
314
|
+
/** Whether to return the root `userAddress` for keychain signatures instead of extracting from the inner signature. */
|
|
315
|
+
root?: boolean | undefined
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
type ReturnType = Address.Address
|
|
319
|
+
|
|
320
|
+
type ErrorType =
|
|
321
|
+
| Address.fromPublicKey.ErrorType
|
|
322
|
+
| extractPublicKey.ErrorType
|
|
323
|
+
| Errors.GlobalErrorType
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Extracts the public key of the signer from a {@link ox#SignatureEnvelope.SignatureEnvelope}.
|
|
328
|
+
*
|
|
329
|
+
* - **secp256k1**: Recovers the public key from the payload via `ecrecover`.
|
|
330
|
+
* - **p256** / **webAuthn**: Returns the embedded public key.
|
|
331
|
+
* - **keychain**: Extracts from the inner signature.
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```ts twoslash
|
|
335
|
+
* import { Secp256k1 } from 'ox'
|
|
336
|
+
* import { SignatureEnvelope } from 'ox/tempo'
|
|
337
|
+
*
|
|
338
|
+
* const payload = '0xdeadbeef'
|
|
339
|
+
* const signature = Secp256k1.sign({ payload, privateKey: '0x...' })
|
|
340
|
+
* const envelope = SignatureEnvelope.from(signature)
|
|
341
|
+
*
|
|
342
|
+
* const publicKey = SignatureEnvelope.extractPublicKey({ // [!code focus]
|
|
343
|
+
* payload, // [!code focus]
|
|
344
|
+
* signature: envelope, // [!code focus]
|
|
345
|
+
* }) // [!code focus]
|
|
346
|
+
* ```
|
|
347
|
+
*
|
|
348
|
+
* @param options - The extraction options.
|
|
349
|
+
* @returns The signer's public key.
|
|
350
|
+
*/
|
|
351
|
+
export function extractPublicKey(
|
|
352
|
+
options: extractPublicKey.Options,
|
|
353
|
+
): extractPublicKey.ReturnType {
|
|
354
|
+
const { payload, signature } = options
|
|
355
|
+
|
|
356
|
+
switch (signature.type) {
|
|
357
|
+
case 'secp256k1':
|
|
358
|
+
return ox_Secp256k1.recoverPublicKey({
|
|
359
|
+
payload,
|
|
360
|
+
signature: signature.signature,
|
|
361
|
+
})
|
|
362
|
+
case 'p256':
|
|
363
|
+
case 'webAuthn':
|
|
364
|
+
return signature.publicKey
|
|
365
|
+
case 'keychain':
|
|
366
|
+
return extractPublicKey({ payload, signature: signature.inner })
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export declare namespace extractPublicKey {
|
|
371
|
+
type Options = {
|
|
372
|
+
/** The sign payload that was signed (only required for secp256k1 signatures). */
|
|
373
|
+
payload: Hex.Hex | Bytes.Bytes
|
|
374
|
+
/** The signature envelope. */
|
|
375
|
+
signature: SignatureEnvelope
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
type ReturnType = PublicKey.PublicKey
|
|
379
|
+
|
|
380
|
+
type ErrorType =
|
|
381
|
+
| ox_Secp256k1.recoverPublicKey.ErrorType
|
|
382
|
+
| Errors.GlobalErrorType
|
|
383
|
+
}
|
|
384
|
+
|
|
272
385
|
/**
|
|
273
386
|
* Deserializes a hex-encoded signature envelope into a typed signature object.
|
|
274
387
|
*
|
package/tempo/Transaction.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type { Call } from './TxEnvelopeTempo.js'
|
|
|
13
13
|
/**
|
|
14
14
|
* A Transaction as defined in the [Execution API specification](https://github.com/ethereum/execution-apis/blob/main/src/schemas/transaction.yaml).
|
|
15
15
|
*
|
|
16
|
-
* @see {@link https://docs.tempo.xyz/protocol/transactions
|
|
16
|
+
* @see {@link https://docs.tempo.xyz/protocol/transactions}
|
|
17
17
|
*/
|
|
18
18
|
export type Transaction<
|
|
19
19
|
pending extends boolean = false,
|
|
@@ -39,7 +39,7 @@ export type Rpc<pending extends boolean = false> = UnionCompute<
|
|
|
39
39
|
* Features configurable fee tokens, call batching, fee sponsorship, access keys,
|
|
40
40
|
* parallelizable nonces, and scheduled execution via `validAfter`/`validBefore`.
|
|
41
41
|
*
|
|
42
|
-
* @see {@link https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction
|
|
42
|
+
* @see {@link https://docs.tempo.xyz/protocol/transactions/spec-tempo-transaction}
|
|
43
43
|
*/
|
|
44
44
|
export type Tempo<
|
|
45
45
|
pending extends boolean = false,
|
|
@@ -9,7 +9,7 @@ import * as ox_TransactionReceipt from '../core/TransactionReceipt.js'
|
|
|
9
9
|
* Extends standard receipts with `feePayer` (the address that paid fees) and
|
|
10
10
|
* `feeToken` (the TIP-20 token used for fee payment).
|
|
11
11
|
*
|
|
12
|
-
* @see {@link https://docs.tempo.xyz/protocol/transactions
|
|
12
|
+
* @see {@link https://docs.tempo.xyz/protocol/transactions}
|
|
13
13
|
*/
|
|
14
14
|
export type TransactionReceipt<
|
|
15
15
|
status = ox_TransactionReceipt.Status,
|
|
@@ -60,7 +60,7 @@ export const toRpcType = {
|
|
|
60
60
|
/**
|
|
61
61
|
* Converts an RPC receipt to a TransactionReceipt.
|
|
62
62
|
*
|
|
63
|
-
* @see {@link https://docs.tempo.xyz/protocol/transactions
|
|
63
|
+
* @see {@link https://docs.tempo.xyz/protocol/transactions}
|
|
64
64
|
*
|
|
65
65
|
* @example
|
|
66
66
|
* ```ts twoslash
|
|
@@ -16,7 +16,7 @@ type KeyType = 'secp256k1' | 'p256' | 'webAuthn'
|
|
|
16
16
|
* Extends the [Execution API specification](https://github.com/ethereum/execution-apis/blob/4aca1d7a3e5aab24c8f6437131289ad386944eaa/src/schemas/transaction.yaml#L358-L423)
|
|
17
17
|
* with Tempo-specific fields for batched calls, fee tokens, access keys, and scheduled execution.
|
|
18
18
|
*
|
|
19
|
-
* @see {@link https://docs.tempo.xyz/protocol/transactions
|
|
19
|
+
* @see {@link https://docs.tempo.xyz/protocol/transactions}
|
|
20
20
|
*/
|
|
21
21
|
export type TransactionRequest<
|
|
22
22
|
bigintType = bigint,
|
|
@@ -54,7 +54,7 @@ export type Rpc = Omit<
|
|
|
54
54
|
/**
|
|
55
55
|
* Converts a {@link ox#TransactionRequest.TransactionRequest} to a {@link ox#TransactionRequest.Rpc}.
|
|
56
56
|
*
|
|
57
|
-
* @see {@link https://docs.tempo.xyz/protocol/transactions
|
|
57
|
+
* @see {@link https://docs.tempo.xyz/protocol/transactions}
|
|
58
58
|
*
|
|
59
59
|
* @example
|
|
60
60
|
* ```ts twoslash
|
package/tempo/TxEnvelopeTempo.ts
CHANGED
|
@@ -378,18 +378,11 @@ export function deserialize(serialized: Serialized): Compute<TxEnvelopeTempo> {
|
|
|
378
378
|
// Recover sender address from the signature if not already set.
|
|
379
379
|
if (!transaction.from && signatureEnvelope) {
|
|
380
380
|
try {
|
|
381
|
-
|
|
382
|
-
transaction
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
)
|
|
387
|
-
transaction.from = Address.fromPublicKey(signatureEnvelope.publicKey)
|
|
388
|
-
else if (signatureEnvelope.type === 'secp256k1')
|
|
389
|
-
transaction.from = Secp256k1.recoverAddress({
|
|
390
|
-
payload: getSignPayload(from(transaction)),
|
|
391
|
-
signature: signatureEnvelope.signature,
|
|
392
|
-
})
|
|
381
|
+
transaction.from = SignatureEnvelope.extractAddress({
|
|
382
|
+
payload: getSignPayload(from(transaction)),
|
|
383
|
+
signature: signatureEnvelope,
|
|
384
|
+
root: true,
|
|
385
|
+
})
|
|
393
386
|
} catch {}
|
|
394
387
|
}
|
|
395
388
|
|
package/tempo/e2e.test.ts
CHANGED
|
@@ -1391,4 +1391,269 @@ describe('behavior: keyAuthorization', () => {
|
|
|
1391
1391
|
expect(receipt).toBeDefined()
|
|
1392
1392
|
}
|
|
1393
1393
|
})
|
|
1394
|
+
|
|
1395
|
+
test('behavior: access key with limits + expiry', async () => {
|
|
1396
|
+
const privateKey = P256.randomPrivateKey()
|
|
1397
|
+
const publicKey = P256.getPublicKey({ privateKey })
|
|
1398
|
+
const address = Address.fromPublicKey(publicKey)
|
|
1399
|
+
const access = {
|
|
1400
|
+
address,
|
|
1401
|
+
publicKey,
|
|
1402
|
+
privateKey,
|
|
1403
|
+
} as const
|
|
1404
|
+
|
|
1405
|
+
const keyAuth = KeyAuthorization.from({
|
|
1406
|
+
address: access.address,
|
|
1407
|
+
type: 'p256',
|
|
1408
|
+
expiry: Math.floor(Date.now() / 1000) + 60 * 60,
|
|
1409
|
+
limits: [
|
|
1410
|
+
{
|
|
1411
|
+
token: '0x20c0000000000000000000000000000000000001',
|
|
1412
|
+
limit: Value.from('1000', 6),
|
|
1413
|
+
},
|
|
1414
|
+
],
|
|
1415
|
+
})
|
|
1416
|
+
|
|
1417
|
+
const keyAuth_signature = Secp256k1.sign({
|
|
1418
|
+
payload: KeyAuthorization.getSignPayload(keyAuth),
|
|
1419
|
+
privateKey: root.privateKey,
|
|
1420
|
+
})
|
|
1421
|
+
|
|
1422
|
+
const keyAuth_signed = KeyAuthorization.from(keyAuth, {
|
|
1423
|
+
signature: SignatureEnvelope.from(keyAuth_signature),
|
|
1424
|
+
})
|
|
1425
|
+
|
|
1426
|
+
const nonce = await getTransactionCount(client, {
|
|
1427
|
+
address: root.address,
|
|
1428
|
+
blockTag: 'pending',
|
|
1429
|
+
})
|
|
1430
|
+
|
|
1431
|
+
const transaction = TxEnvelopeTempo.from({
|
|
1432
|
+
calls: [
|
|
1433
|
+
{
|
|
1434
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
1435
|
+
},
|
|
1436
|
+
],
|
|
1437
|
+
chainId,
|
|
1438
|
+
feeToken: '0x20c0000000000000000000000000000000000001',
|
|
1439
|
+
keyAuthorization: keyAuth_signed,
|
|
1440
|
+
nonce: BigInt(nonce),
|
|
1441
|
+
gas: 1_000_000n,
|
|
1442
|
+
maxFeePerGas: Value.fromGwei('20'),
|
|
1443
|
+
maxPriorityFeePerGas: Value.fromGwei('10'),
|
|
1444
|
+
})
|
|
1445
|
+
|
|
1446
|
+
const signature = P256.sign({
|
|
1447
|
+
payload: TxEnvelopeTempo.getSignPayload(transaction),
|
|
1448
|
+
privateKey: access.privateKey,
|
|
1449
|
+
})
|
|
1450
|
+
|
|
1451
|
+
const serialized_signed = TxEnvelopeTempo.serialize(transaction, {
|
|
1452
|
+
signature: SignatureEnvelope.from({
|
|
1453
|
+
userAddress: root.address,
|
|
1454
|
+
inner: SignatureEnvelope.from({
|
|
1455
|
+
prehash: false,
|
|
1456
|
+
publicKey: access.publicKey,
|
|
1457
|
+
signature,
|
|
1458
|
+
type: 'p256',
|
|
1459
|
+
}),
|
|
1460
|
+
type: 'keychain',
|
|
1461
|
+
}),
|
|
1462
|
+
})
|
|
1463
|
+
|
|
1464
|
+
const receipt = (await client
|
|
1465
|
+
.request({
|
|
1466
|
+
method: 'eth_sendRawTransactionSync',
|
|
1467
|
+
params: [serialized_signed],
|
|
1468
|
+
})
|
|
1469
|
+
.then((tx) => TransactionReceipt.fromRpc(tx as any)))!
|
|
1470
|
+
expect(receipt).toBeDefined()
|
|
1471
|
+
expect(receipt.status).toBe('success')
|
|
1472
|
+
|
|
1473
|
+
{
|
|
1474
|
+
const response = await client
|
|
1475
|
+
.request({
|
|
1476
|
+
method: 'eth_getTransactionByHash',
|
|
1477
|
+
params: [receipt.transactionHash],
|
|
1478
|
+
})
|
|
1479
|
+
.then((tx) => Transaction.fromRpc(tx as any))
|
|
1480
|
+
if (!response) throw new Error()
|
|
1481
|
+
|
|
1482
|
+
expect(response.from).toBe(root.address)
|
|
1483
|
+
expect(response.keyAuthorization).toBeDefined()
|
|
1484
|
+
expect(response.keyAuthorization?.expiry).toBe(keyAuth.expiry)
|
|
1485
|
+
expect(response.keyAuthorization?.limits).toEqual(keyAuth.limits)
|
|
1486
|
+
}
|
|
1487
|
+
})
|
|
1488
|
+
|
|
1489
|
+
test('behavior: access key with limits (no expiry)', async () => {
|
|
1490
|
+
const privateKey = Secp256k1.randomPrivateKey()
|
|
1491
|
+
const publicKey = Secp256k1.getPublicKey({ privateKey })
|
|
1492
|
+
const address = Address.fromPublicKey(publicKey)
|
|
1493
|
+
const access = {
|
|
1494
|
+
address,
|
|
1495
|
+
publicKey,
|
|
1496
|
+
privateKey,
|
|
1497
|
+
} as const
|
|
1498
|
+
|
|
1499
|
+
const keyAuth = KeyAuthorization.from({
|
|
1500
|
+
address: access.address,
|
|
1501
|
+
type: 'secp256k1',
|
|
1502
|
+
limits: [
|
|
1503
|
+
{
|
|
1504
|
+
token: '0x20c0000000000000000000000000000000000001',
|
|
1505
|
+
limit: Value.from('1000', 6),
|
|
1506
|
+
},
|
|
1507
|
+
],
|
|
1508
|
+
})
|
|
1509
|
+
|
|
1510
|
+
const keyAuth_signature = Secp256k1.sign({
|
|
1511
|
+
payload: KeyAuthorization.getSignPayload(keyAuth),
|
|
1512
|
+
privateKey: root.privateKey,
|
|
1513
|
+
})
|
|
1514
|
+
|
|
1515
|
+
const keyAuth_signed = KeyAuthorization.from(keyAuth, {
|
|
1516
|
+
signature: SignatureEnvelope.from(keyAuth_signature),
|
|
1517
|
+
})
|
|
1518
|
+
|
|
1519
|
+
const nonce = await getTransactionCount(client, {
|
|
1520
|
+
address: root.address,
|
|
1521
|
+
blockTag: 'pending',
|
|
1522
|
+
})
|
|
1523
|
+
|
|
1524
|
+
const transaction = TxEnvelopeTempo.from({
|
|
1525
|
+
calls: [
|
|
1526
|
+
{
|
|
1527
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
1528
|
+
},
|
|
1529
|
+
],
|
|
1530
|
+
chainId,
|
|
1531
|
+
feeToken: '0x20c0000000000000000000000000000000000001',
|
|
1532
|
+
keyAuthorization: keyAuth_signed,
|
|
1533
|
+
nonce: BigInt(nonce),
|
|
1534
|
+
gas: 1_000_000n,
|
|
1535
|
+
maxFeePerGas: Value.fromGwei('20'),
|
|
1536
|
+
maxPriorityFeePerGas: Value.fromGwei('10'),
|
|
1537
|
+
})
|
|
1538
|
+
|
|
1539
|
+
const signature = Secp256k1.sign({
|
|
1540
|
+
payload: TxEnvelopeTempo.getSignPayload(transaction),
|
|
1541
|
+
privateKey: access.privateKey,
|
|
1542
|
+
})
|
|
1543
|
+
|
|
1544
|
+
const serialized_signed = TxEnvelopeTempo.serialize(transaction, {
|
|
1545
|
+
signature: SignatureEnvelope.from({
|
|
1546
|
+
userAddress: root.address,
|
|
1547
|
+
inner: SignatureEnvelope.from(signature),
|
|
1548
|
+
type: 'keychain',
|
|
1549
|
+
}),
|
|
1550
|
+
})
|
|
1551
|
+
|
|
1552
|
+
const receipt = (await client
|
|
1553
|
+
.request({
|
|
1554
|
+
method: 'eth_sendRawTransactionSync',
|
|
1555
|
+
params: [serialized_signed],
|
|
1556
|
+
})
|
|
1557
|
+
.then((tx) => TransactionReceipt.fromRpc(tx as any)))!
|
|
1558
|
+
expect(receipt).toBeDefined()
|
|
1559
|
+
expect(receipt.status).toBe('success')
|
|
1560
|
+
|
|
1561
|
+
{
|
|
1562
|
+
const response = await client
|
|
1563
|
+
.request({
|
|
1564
|
+
method: 'eth_getTransactionByHash',
|
|
1565
|
+
params: [receipt.transactionHash],
|
|
1566
|
+
})
|
|
1567
|
+
.then((tx) => Transaction.fromRpc(tx as any))
|
|
1568
|
+
if (!response) throw new Error()
|
|
1569
|
+
|
|
1570
|
+
expect(response.from).toBe(root.address)
|
|
1571
|
+
expect(response.keyAuthorization).toBeDefined()
|
|
1572
|
+
expect(response.keyAuthorization?.expiry).toBe(0)
|
|
1573
|
+
expect(response.keyAuthorization?.limits).toEqual(keyAuth.limits)
|
|
1574
|
+
}
|
|
1575
|
+
})
|
|
1576
|
+
|
|
1577
|
+
test('behavior: access key with expiry (no limits)', async () => {
|
|
1578
|
+
const privateKey = Secp256k1.randomPrivateKey()
|
|
1579
|
+
const publicKey = Secp256k1.getPublicKey({ privateKey })
|
|
1580
|
+
const address = Address.fromPublicKey(publicKey)
|
|
1581
|
+
const access = {
|
|
1582
|
+
address,
|
|
1583
|
+
publicKey,
|
|
1584
|
+
privateKey,
|
|
1585
|
+
} as const
|
|
1586
|
+
|
|
1587
|
+
const keyAuth = KeyAuthorization.from({
|
|
1588
|
+
address: access.address,
|
|
1589
|
+
type: 'secp256k1',
|
|
1590
|
+
expiry: Math.floor(Date.now() / 1000) + 60 * 60,
|
|
1591
|
+
})
|
|
1592
|
+
|
|
1593
|
+
const keyAuth_signature = Secp256k1.sign({
|
|
1594
|
+
payload: KeyAuthorization.getSignPayload(keyAuth),
|
|
1595
|
+
privateKey: root.privateKey,
|
|
1596
|
+
})
|
|
1597
|
+
|
|
1598
|
+
const keyAuth_signed = KeyAuthorization.from(keyAuth, {
|
|
1599
|
+
signature: SignatureEnvelope.from(keyAuth_signature),
|
|
1600
|
+
})
|
|
1601
|
+
|
|
1602
|
+
const nonce = await getTransactionCount(client, {
|
|
1603
|
+
address: root.address,
|
|
1604
|
+
blockTag: 'pending',
|
|
1605
|
+
})
|
|
1606
|
+
|
|
1607
|
+
const transaction = TxEnvelopeTempo.from({
|
|
1608
|
+
calls: [
|
|
1609
|
+
{
|
|
1610
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
1611
|
+
},
|
|
1612
|
+
],
|
|
1613
|
+
chainId,
|
|
1614
|
+
feeToken: '0x20c0000000000000000000000000000000000001',
|
|
1615
|
+
keyAuthorization: keyAuth_signed,
|
|
1616
|
+
nonce: BigInt(nonce),
|
|
1617
|
+
gas: 1_000_000n,
|
|
1618
|
+
maxFeePerGas: Value.fromGwei('20'),
|
|
1619
|
+
maxPriorityFeePerGas: Value.fromGwei('10'),
|
|
1620
|
+
})
|
|
1621
|
+
|
|
1622
|
+
const signature = Secp256k1.sign({
|
|
1623
|
+
payload: TxEnvelopeTempo.getSignPayload(transaction),
|
|
1624
|
+
privateKey: access.privateKey,
|
|
1625
|
+
})
|
|
1626
|
+
|
|
1627
|
+
const serialized_signed = TxEnvelopeTempo.serialize(transaction, {
|
|
1628
|
+
signature: SignatureEnvelope.from({
|
|
1629
|
+
userAddress: root.address,
|
|
1630
|
+
inner: SignatureEnvelope.from(signature),
|
|
1631
|
+
type: 'keychain',
|
|
1632
|
+
}),
|
|
1633
|
+
})
|
|
1634
|
+
|
|
1635
|
+
const receipt = (await client
|
|
1636
|
+
.request({
|
|
1637
|
+
method: 'eth_sendRawTransactionSync',
|
|
1638
|
+
params: [serialized_signed],
|
|
1639
|
+
})
|
|
1640
|
+
.then((tx) => TransactionReceipt.fromRpc(tx as any)))!
|
|
1641
|
+
expect(receipt).toBeDefined()
|
|
1642
|
+
expect(receipt.status).toBe('success')
|
|
1643
|
+
|
|
1644
|
+
{
|
|
1645
|
+
const response = await client
|
|
1646
|
+
.request({
|
|
1647
|
+
method: 'eth_getTransactionByHash',
|
|
1648
|
+
params: [receipt.transactionHash],
|
|
1649
|
+
})
|
|
1650
|
+
.then((tx) => Transaction.fromRpc(tx as any))
|
|
1651
|
+
if (!response) throw new Error()
|
|
1652
|
+
|
|
1653
|
+
expect(response.from).toBe(root.address)
|
|
1654
|
+
expect(response.keyAuthorization).toBeDefined()
|
|
1655
|
+
expect(response.keyAuthorization?.expiry).toBe(keyAuth.expiry)
|
|
1656
|
+
expect(response.keyAuthorization?.limits).toBeUndefined()
|
|
1657
|
+
}
|
|
1658
|
+
})
|
|
1394
1659
|
})
|
package/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** @internal */
|
|
2
|
-
export const version = '0.
|
|
2
|
+
export const version = '0.13.1'
|