mppx 0.7.0 → 0.8.0
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 +33 -0
- package/README.md +20 -11
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +18 -6
- package/dist/Challenge.js.map +1 -1
- package/dist/Mcp.d.ts +3 -0
- package/dist/Mcp.d.ts.map +1 -1
- package/dist/Mcp.js +2 -0
- package/dist/Mcp.js.map +1 -1
- package/dist/PaymentRequest.d.ts +10 -10
- package/dist/PaymentRequest.js +8 -8
- package/dist/client/Mppx.js +2 -2
- package/dist/client/Mppx.js.map +1 -1
- package/dist/client/Transport.d.ts +11 -16
- package/dist/client/Transport.d.ts.map +1 -1
- package/dist/client/Transport.js +55 -75
- package/dist/client/Transport.js.map +1 -1
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +46 -7
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/client/internal/protocols/Mcp.d.ts +7 -0
- package/dist/client/internal/protocols/Mcp.d.ts.map +1 -0
- package/dist/client/internal/protocols/Mcp.js +159 -0
- package/dist/client/internal/protocols/Mcp.js.map +1 -0
- package/dist/client/internal/protocols/Mpp.d.ts +4 -0
- package/dist/client/internal/protocols/Mpp.d.ts.map +1 -0
- package/dist/client/internal/protocols/Mpp.js +18 -0
- package/dist/client/internal/protocols/Mpp.js.map +1 -0
- package/dist/client/internal/protocols/Protocol.d.ts +10 -0
- package/dist/client/internal/protocols/Protocol.d.ts.map +1 -0
- package/dist/client/internal/protocols/Protocol.js +2 -0
- package/dist/client/internal/protocols/Protocol.js.map +1 -0
- package/dist/client/internal/protocols/Shared.d.ts +5 -0
- package/dist/client/internal/protocols/Shared.d.ts.map +1 -0
- package/dist/client/internal/protocols/Shared.js +20 -0
- package/dist/client/internal/protocols/Shared.js.map +1 -0
- package/dist/client/internal/protocols/X402.d.ts +8 -0
- package/dist/client/internal/protocols/X402.d.ts.map +1 -0
- package/dist/client/internal/protocols/X402.js +39 -0
- package/dist/client/internal/protocols/X402.js.map +1 -0
- package/dist/evm/client/index.d.ts +1 -0
- package/dist/evm/client/index.d.ts.map +1 -1
- package/dist/evm/client/index.js +1 -0
- package/dist/evm/client/index.js.map +1 -1
- package/dist/evm/index.d.ts +2 -0
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +2 -0
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/server/index.d.ts +1 -0
- package/dist/evm/server/index.d.ts.map +1 -1
- package/dist/evm/server/index.js +1 -0
- package/dist/evm/server/index.js.map +1 -1
- package/dist/mcp/client/McpClient.d.ts +101 -0
- package/dist/mcp/client/McpClient.d.ts.map +1 -0
- package/dist/mcp/client/McpClient.js +162 -0
- package/dist/mcp/client/McpClient.js.map +1 -0
- package/dist/mcp/client/index.d.ts.map +1 -0
- package/dist/mcp/client/index.js.map +1 -0
- package/dist/mcp/server/Transport.d.ts.map +1 -0
- package/dist/mcp/server/Transport.js.map +1 -0
- package/dist/mcp/server/index.d.ts.map +1 -0
- package/dist/mcp/server/index.js.map +1 -0
- package/dist/server/Mppx.d.ts +1 -1
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +9 -0
- package/dist/server/Mppx.js.map +1 -1
- package/dist/server/Transport.d.ts +1 -1
- package/dist/server/Transport.d.ts.map +1 -1
- package/dist/server/Transport.js +1 -1
- package/dist/server/Transport.js.map +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
- package/dist/stripe/server/internal/html.gen.js +1 -1
- package/dist/stripe/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/Proof.d.ts +85 -1
- package/dist/tempo/Proof.d.ts.map +1 -1
- package/dist/tempo/Proof.js +35 -0
- package/dist/tempo/Proof.js.map +1 -1
- package/dist/tempo/client/Charge.d.ts +13 -1
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +38 -25
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +5 -3
- package/dist/tempo/client/Methods.d.ts.map +1 -1
- package/dist/tempo/client/Methods.js +4 -2
- package/dist/tempo/client/Methods.js.map +1 -1
- package/dist/tempo/client/ResolveAccount.d.ts +40 -0
- package/dist/tempo/client/ResolveAccount.d.ts.map +1 -0
- package/dist/tempo/client/ResolveAccount.js +2 -0
- package/dist/tempo/client/ResolveAccount.js.map +1 -0
- package/dist/tempo/internal/fee-payer.d.ts +9 -1
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js +35 -6
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/internal/proof.d.ts +71 -5
- package/dist/tempo/internal/proof.d.ts.map +1 -1
- package/dist/tempo/internal/proof.js +42 -6
- package/dist/tempo/internal/proof.js.map +1 -1
- package/dist/tempo/legacy/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/legacy/client/SessionManager.js +10 -3
- package/dist/tempo/legacy/client/SessionManager.js.map +1 -1
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +42 -18
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +4 -2
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Methods.js +4 -2
- package/dist/tempo/server/Methods.js.map +1 -1
- package/dist/tempo/server/Subscription.d.ts +10 -0
- package/dist/tempo/server/Subscription.d.ts.map +1 -1
- package/dist/tempo/server/Subscription.js +135 -23
- package/dist/tempo/server/Subscription.js.map +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/session/client/ChannelOps.d.ts +2 -3
- package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -1
- package/dist/tempo/session/client/ChannelOps.js +7 -10
- package/dist/tempo/session/client/ChannelOps.js.map +1 -1
- package/dist/tempo/session/client/ChannelStore.d.ts +51 -0
- package/dist/tempo/session/client/ChannelStore.d.ts.map +1 -0
- package/dist/tempo/session/client/ChannelStore.js +63 -0
- package/dist/tempo/session/client/ChannelStore.js.map +1 -0
- package/dist/tempo/session/client/CredentialState.d.ts +7 -24
- package/dist/tempo/session/client/CredentialState.d.ts.map +1 -1
- package/dist/tempo/session/client/CredentialState.js +51 -49
- package/dist/tempo/session/client/CredentialState.js.map +1 -1
- package/dist/tempo/session/client/Session.d.ts +8 -2
- package/dist/tempo/session/client/Session.d.ts.map +1 -1
- package/dist/tempo/session/client/Session.js +22 -8
- package/dist/tempo/session/client/Session.js.map +1 -1
- package/dist/tempo/session/client/SessionManager.d.ts +4 -40
- package/dist/tempo/session/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/session/client/SessionManager.js +124 -174
- package/dist/tempo/session/client/SessionManager.js.map +1 -1
- package/dist/tempo/session/client/index.d.ts +3 -4
- package/dist/tempo/session/client/index.d.ts.map +1 -1
- package/dist/tempo/session/client/index.js +1 -0
- package/dist/tempo/session/client/index.js.map +1 -1
- package/dist/tempo/session/precompile/Voucher.d.ts +3 -3
- package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -1
- package/dist/tempo/session/precompile/Voucher.js +24 -25
- package/dist/tempo/session/precompile/Voucher.js.map +1 -1
- package/dist/tempo/session/server/Settlement.d.ts.map +1 -1
- package/dist/tempo/session/server/Settlement.js +4 -2
- package/dist/tempo/session/server/Settlement.js.map +1 -1
- package/dist/tempo/session/server/Sse.d.ts.map +1 -1
- package/dist/tempo/session/server/Sse.js.map +1 -1
- package/dist/tempo/session/server/Ws.d.ts.map +1 -1
- package/dist/tempo/session/server/Ws.js.map +1 -1
- package/dist/tempo/subscription/KeyAuthorization.d.ts +712 -1
- package/dist/tempo/subscription/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/subscription/Store.d.ts +2 -0
- package/dist/tempo/subscription/Store.d.ts.map +1 -1
- package/dist/tempo/subscription/Store.js +16 -1
- package/dist/tempo/subscription/Store.js.map +1 -1
- package/dist/x402/index.d.ts +1 -0
- package/dist/x402/index.d.ts.map +1 -1
- package/dist/x402/index.js +1 -0
- package/dist/x402/index.js.map +1 -1
- package/package.json +21 -10
- package/src/Challenge.test.ts +40 -0
- package/src/Challenge.ts +19 -6
- package/src/Mcp.ts +4 -0
- package/src/PaymentRequest.ts +10 -10
- package/src/cli/cli.test.ts +15 -15
- package/src/client/Mppx.test-d.ts +21 -1
- package/src/client/Mppx.test.ts +1 -1
- package/src/client/Mppx.ts +2 -2
- package/src/client/Transport.test.ts +225 -178
- package/src/client/Transport.ts +77 -83
- package/src/client/index.ts +14 -0
- package/src/client/internal/Fetch.test.ts +207 -2
- package/src/client/internal/Fetch.ts +52 -6
- package/src/client/internal/protocols/Mcp.test.ts +220 -0
- package/src/client/internal/protocols/Mcp.ts +162 -0
- package/src/client/internal/protocols/Mpp.ts +21 -0
- package/src/client/internal/protocols/Protocol.ts +10 -0
- package/src/client/internal/protocols/Shared.ts +25 -0
- package/src/client/internal/protocols/X402.ts +42 -0
- package/src/discovery/OpenApi.test.ts +1 -1
- package/src/evm/PublicInterface.test-d.ts +1 -1
- package/src/evm/client/index.ts +1 -0
- package/src/evm/index.ts +2 -0
- package/src/evm/server/Charge.test.ts +1 -1
- package/src/evm/server/index.ts +1 -0
- package/src/{mcp-sdk → mcp}/client/McpClient.integration.test.ts +10 -4
- package/src/{mcp-sdk → mcp}/client/McpClient.test-d.ts +45 -18
- package/src/{mcp-sdk → mcp}/client/McpClient.test.ts +211 -5
- package/src/mcp/client/McpClient.ts +307 -0
- package/src/{mcp-sdk → mcp}/client/McpClient.unit.test.ts +9 -5
- package/src/middlewares/elysia.test.ts +1 -1
- package/src/middlewares/express.test.ts +1 -1
- package/src/middlewares/hono.test.ts +1 -1
- package/src/middlewares/internal/mppx.test.ts +1 -1
- package/src/middlewares/nextjs.test.ts +1 -1
- package/src/proxy/Proxy.test.ts +1 -1
- package/src/proxy/services/anthropic.test.ts +1 -1
- package/src/proxy/services/openai.test.ts +1 -1
- package/src/proxy/services/stripe.test.ts +1 -1
- package/src/server/Mppx.authorize.test.ts +1 -1
- package/src/server/Mppx.test-d.ts +1 -1
- package/src/server/Mppx.test.ts +20 -2
- package/src/server/Mppx.ts +14 -1
- package/src/server/Transport.test.ts +6 -6
- package/src/server/Transport.ts +1 -1
- package/src/stripe/Charge.integration.test.ts +1 -1
- package/src/stripe/client/Charge.test.ts +1 -1
- package/src/stripe/server/Charge.test.ts +1 -1
- package/src/stripe/server/internal/html/package.json +1 -1
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/tempo/Proof.conformance.test.ts +146 -0
- package/src/tempo/Proof.test-d.ts +15 -0
- package/src/tempo/Proof.ts +52 -1
- package/src/tempo/Subscription.integration.test.ts +1 -1
- package/src/tempo/client/Charge.test.ts +173 -0
- package/src/tempo/client/Charge.ts +65 -36
- package/src/tempo/client/Methods.ts +4 -2
- package/src/tempo/client/ResolveAccount.ts +46 -0
- package/src/tempo/internal/fee-payer.test.ts +65 -10
- package/src/tempo/internal/fee-payer.ts +42 -6
- package/src/tempo/internal/proof.test.ts +12 -4
- package/src/tempo/internal/proof.ts +55 -6
- package/src/tempo/legacy/client/SessionManager.ts +11 -3
- package/src/tempo/legacy/server/Session.test.ts +91 -26
- package/src/tempo/server/Charge.test.ts +388 -17
- package/src/tempo/server/Charge.ts +46 -24
- package/src/tempo/server/Methods.ts +4 -2
- package/src/tempo/server/Subscription.test.ts +465 -3
- package/src/tempo/server/Subscription.ts +174 -19
- package/src/tempo/server/internal/html/package.json +2 -2
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/session/client/ChannelOps.ts +5 -19
- package/src/tempo/session/client/ChannelStore.ts +111 -0
- package/src/tempo/session/client/CredentialState.test.ts +206 -62
- package/src/tempo/session/client/CredentialState.ts +58 -73
- package/src/tempo/session/client/Session.test.ts +41 -1
- package/src/tempo/session/client/Session.ts +36 -10
- package/src/tempo/session/client/SessionManager.test.ts +154 -65
- package/src/tempo/session/client/SessionManager.ts +141 -235
- package/src/tempo/session/client/index.ts +8 -5
- package/src/tempo/session/precompile/Voucher.test.ts +45 -7
- package/src/tempo/session/precompile/Voucher.ts +27 -25
- package/src/tempo/session/server/Session.test.ts +4 -4
- package/src/tempo/session/server/Settlement.test.ts +88 -1
- package/src/tempo/session/server/Settlement.ts +2 -1
- package/src/tempo/session/server/Sse.ts +0 -2
- package/src/tempo/session/server/Ws.ts +0 -4
- package/src/tempo/subscription/Store.ts +27 -9
- package/src/x402/Exact.e2e.test.ts +1 -1
- package/src/x402/PublicInterface.test-d.ts +1 -1
- package/src/x402/index.ts +1 -0
- package/dist/mcp-sdk/client/McpClient.d.ts +0 -85
- package/dist/mcp-sdk/client/McpClient.d.ts.map +0 -1
- package/dist/mcp-sdk/client/McpClient.js +0 -118
- package/dist/mcp-sdk/client/McpClient.js.map +0 -1
- package/dist/mcp-sdk/client/index.d.ts.map +0 -1
- package/dist/mcp-sdk/client/index.js.map +0 -1
- package/dist/mcp-sdk/server/Transport.d.ts.map +0 -1
- package/dist/mcp-sdk/server/Transport.js.map +0 -1
- package/dist/mcp-sdk/server/index.d.ts.map +0 -1
- package/dist/mcp-sdk/server/index.js.map +0 -1
- package/src/mcp-sdk/client/McpClient.ts +0 -228
- /package/dist/{mcp-sdk → mcp}/client/index.d.ts +0 -0
- /package/dist/{mcp-sdk → mcp}/client/index.js +0 -0
- /package/dist/{mcp-sdk → mcp}/server/Transport.d.ts +0 -0
- /package/dist/{mcp-sdk → mcp}/server/Transport.js +0 -0
- /package/dist/{mcp-sdk → mcp}/server/index.d.ts +0 -0
- /package/dist/{mcp-sdk → mcp}/server/index.js +0 -0
- /package/src/{mcp-sdk → mcp}/client/index.ts +0 -0
- /package/src/{mcp-sdk → mcp}/server/Transport.test.ts +0 -0
- /package/src/{mcp-sdk → mcp}/server/Transport.ts +0 -0
- /package/src/{mcp-sdk → mcp}/server/index.ts +0 -0
|
@@ -1,7 +1,22 @@
|
|
|
1
|
+
import type { Address, Hex } from 'viem'
|
|
1
2
|
import { expectTypeOf, test } from 'vp/test'
|
|
2
3
|
|
|
3
4
|
import { Proof } from './index.js'
|
|
4
5
|
|
|
6
|
+
test('Proof exports the wallet-bound typed-data contract helpers', () => {
|
|
7
|
+
expectTypeOf(Proof.message).toEqualTypeOf<
|
|
8
|
+
(parameters: { account: Address; challengeId: string; realm: string }) => {
|
|
9
|
+
readonly account: Address
|
|
10
|
+
readonly challengeId: string
|
|
11
|
+
readonly realm: string
|
|
12
|
+
}
|
|
13
|
+
>()
|
|
14
|
+
|
|
15
|
+
expectTypeOf(Proof.hash).toEqualTypeOf<
|
|
16
|
+
(parameters: { account: Address; chainId: number; challengeId: string; realm: string }) => Hex
|
|
17
|
+
>()
|
|
18
|
+
})
|
|
19
|
+
|
|
5
20
|
test('Proof exports public proof source helpers', () => {
|
|
6
21
|
expectTypeOf(Proof.proofSource).toEqualTypeOf<
|
|
7
22
|
(parameters: { address: string; chainId: number }) => string
|
package/src/tempo/Proof.ts
CHANGED
|
@@ -1,7 +1,58 @@
|
|
|
1
|
-
import type { Address } from 'viem'
|
|
1
|
+
import type { Address, Hex } from 'viem'
|
|
2
2
|
|
|
3
3
|
import * as Proof_internal from './internal/proof.js'
|
|
4
4
|
|
|
5
|
+
/** EIP-712 primary type for Tempo proof credentials. */
|
|
6
|
+
export const primaryType = Proof_internal.primaryType
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* EIP-712 typed-data field definitions for Tempo zero-amount proof credentials.
|
|
10
|
+
*
|
|
11
|
+
* The `account` field cryptographically binds the signature to the payer
|
|
12
|
+
* wallet, so a proof signed for one account cannot be replayed against another.
|
|
13
|
+
*/
|
|
14
|
+
export const types = Proof_internal.types
|
|
15
|
+
|
|
16
|
+
/** Constructs the EIP-712 domain for a Tempo proof credential. */
|
|
17
|
+
export function domain(chainId: number) {
|
|
18
|
+
return Proof_internal.domain(chainId)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Constructs the EIP-712 message for a Tempo proof credential.
|
|
23
|
+
*
|
|
24
|
+
* @param parameters - Proof message parameters.
|
|
25
|
+
* @param parameters.account - Payer wallet address the proof is bound to.
|
|
26
|
+
* @param parameters.challengeId - Challenge `id` being proven.
|
|
27
|
+
* @param parameters.realm - Challenge `realm` being proven.
|
|
28
|
+
*/
|
|
29
|
+
export function message(parameters: { account: Address; challengeId: string; realm: string }) {
|
|
30
|
+
return Proof_internal.message(parameters)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Constructs the complete EIP-712 typed-data payload for a Tempo proof
|
|
35
|
+
* credential — the canonical, wallet-bound proof contract.
|
|
36
|
+
*/
|
|
37
|
+
export function typedData(parameters: {
|
|
38
|
+
account: Address
|
|
39
|
+
chainId: number
|
|
40
|
+
challengeId: string
|
|
41
|
+
realm: string
|
|
42
|
+
}) {
|
|
43
|
+
return Proof_internal.typedData(parameters)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Computes the EIP-712 digest (signing payload) for a Tempo proof credential. */
|
|
47
|
+
export function hash(parameters: {
|
|
48
|
+
account: Address
|
|
49
|
+
chainId: number
|
|
50
|
+
challengeId: string
|
|
51
|
+
realm: string
|
|
52
|
+
}): Hex {
|
|
53
|
+
return Proof_internal.hash(parameters)
|
|
54
|
+
}
|
|
55
|
+
|
|
5
56
|
/** Constructs the canonical `did:pkh:eip155` source DID for Tempo proof credentials. */
|
|
6
57
|
export function proofSource(parameters: { address: string; chainId: number }): string {
|
|
7
58
|
return Proof_internal.proofSource(parameters)
|
|
@@ -10,7 +10,7 @@ import * as SubscriptionStore from './subscription/Store.js'
|
|
|
10
10
|
import type { SubscriptionAccessKey, SubscriptionRecord } from './subscription/Types.js'
|
|
11
11
|
|
|
12
12
|
const realm = 'news.example.com'
|
|
13
|
-
const secretKey = 'subscription-lifecycle-secret'
|
|
13
|
+
const secretKey = 'subscription-lifecycle-secret-key-32'
|
|
14
14
|
const currency = '0x20c0000000000000000000000000000000000001'
|
|
15
15
|
const recipient = '0x1234567890abcdef1234567890abcdef12345678'
|
|
16
16
|
const periodCount = '30'
|
|
@@ -2,6 +2,7 @@ import { Challenge, Credential } from 'mppx'
|
|
|
2
2
|
import { createClient, http } from 'viem'
|
|
3
3
|
import { privateKeyToAccount } from 'viem/accounts'
|
|
4
4
|
import { tempoLocalnet } from 'viem/chains'
|
|
5
|
+
import { Account, Secp256k1 } from 'viem/tempo'
|
|
5
6
|
import { describe, expect, test, vi } from 'vp/test'
|
|
6
7
|
|
|
7
8
|
import * as Methods from '../Methods.js'
|
|
@@ -83,6 +84,178 @@ describe('tempo.charge client', () => {
|
|
|
83
84
|
expect(credential.source).toBe(`did:pkh:eip155:${chainId}:${account.address}`)
|
|
84
85
|
})
|
|
85
86
|
|
|
87
|
+
test('resolveAccount selects the transaction account from executable calls', async () => {
|
|
88
|
+
vi.resetModules()
|
|
89
|
+
const selectedAccount = privateKeyToAccount(
|
|
90
|
+
'0x0000000000000000000000000000000000000000000000000000000000000002',
|
|
91
|
+
)
|
|
92
|
+
const chainId = 42431
|
|
93
|
+
const calls: charge.ResolveAccountInfo[] = []
|
|
94
|
+
const prepareTransactionRequest = vi.fn(async () => ({}))
|
|
95
|
+
const signTransaction = vi.fn(async () => '0xdeadbeef')
|
|
96
|
+
vi.doMock('viem/actions', () => ({
|
|
97
|
+
prepareTransactionRequest,
|
|
98
|
+
sendCallsSync: vi.fn(),
|
|
99
|
+
signTransaction,
|
|
100
|
+
signTypedData: vi.fn(),
|
|
101
|
+
}))
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const { charge: chargeWithMockedActions } = await import('./Charge.js')
|
|
105
|
+
const client = createClient({
|
|
106
|
+
account,
|
|
107
|
+
chain: tempoLocalnet,
|
|
108
|
+
transport: http('http://127.0.0.1'),
|
|
109
|
+
})
|
|
110
|
+
const method = chargeWithMockedActions({
|
|
111
|
+
account,
|
|
112
|
+
getClient: () => client,
|
|
113
|
+
resolveAccount(info) {
|
|
114
|
+
calls.push(info)
|
|
115
|
+
return selectedAccount
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const credential = Credential.deserialize(
|
|
120
|
+
await method.createCredential({
|
|
121
|
+
challenge: createChallenge({ amount: '1', chainId, supportedModes: ['pull'] }),
|
|
122
|
+
context: {},
|
|
123
|
+
}),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
expect(calls).toHaveLength(1)
|
|
127
|
+
expect(calls[0]!.account.address).toBe(account.address)
|
|
128
|
+
expect(calls[0]!.chainId).toBe(chainId)
|
|
129
|
+
expect(calls[0]!.operation.kind).toBe('executeCalls')
|
|
130
|
+
if (calls[0]!.operation.kind !== 'executeCalls') throw new Error('expected executeCalls')
|
|
131
|
+
expect(calls[0]!.operation.calls).toHaveLength(1)
|
|
132
|
+
expect(calls[0]!.operation.calls?.[0]?.to.toLowerCase()).toBe(currency.toLowerCase())
|
|
133
|
+
expect(prepareTransactionRequest).toHaveBeenCalledOnce()
|
|
134
|
+
expect(signTransaction).toHaveBeenCalledOnce()
|
|
135
|
+
expect(credential.payload).toEqual({ signature: '0xdeadbeef', type: 'transaction' })
|
|
136
|
+
expect(credential.source).toBe(`did:pkh:eip155:${chainId}:${selectedAccount.address}`)
|
|
137
|
+
} finally {
|
|
138
|
+
vi.doUnmock('viem/actions')
|
|
139
|
+
vi.resetModules()
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('resolveAccount omits executable calls when auto-swap routing is account-dependent', async () => {
|
|
144
|
+
vi.resetModules()
|
|
145
|
+
const selectedAccount = privateKeyToAccount(
|
|
146
|
+
'0x0000000000000000000000000000000000000000000000000000000000000002',
|
|
147
|
+
)
|
|
148
|
+
const chainId = 42431
|
|
149
|
+
const calls: charge.ResolveAccountInfo[] = []
|
|
150
|
+
const prepareTransactionRequest = vi.fn(async () => ({}))
|
|
151
|
+
const signTransaction = vi.fn(async () => '0xdeadbeef')
|
|
152
|
+
const findCalls = vi.fn(async (_client: unknown, _parameters: { account: string }) => undefined)
|
|
153
|
+
vi.doMock('viem/actions', () => ({
|
|
154
|
+
prepareTransactionRequest,
|
|
155
|
+
sendCallsSync: vi.fn(),
|
|
156
|
+
signTransaction,
|
|
157
|
+
signTypedData: vi.fn(),
|
|
158
|
+
}))
|
|
159
|
+
vi.doMock('../internal/auto-swap.js', () => ({
|
|
160
|
+
defaultCurrencies: [currency],
|
|
161
|
+
findCalls,
|
|
162
|
+
resolve: vi.fn(() => ({ tokenIn: [currency], slippage: 1 })),
|
|
163
|
+
}))
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const { charge: chargeWithMockedActions } = await import('./Charge.js')
|
|
167
|
+
const client = createClient({
|
|
168
|
+
account,
|
|
169
|
+
chain: tempoLocalnet,
|
|
170
|
+
transport: http('http://127.0.0.1'),
|
|
171
|
+
})
|
|
172
|
+
const method = chargeWithMockedActions({
|
|
173
|
+
account,
|
|
174
|
+
autoSwap: true,
|
|
175
|
+
getClient: () => client,
|
|
176
|
+
resolveAccount(info) {
|
|
177
|
+
calls.push(info)
|
|
178
|
+
return selectedAccount
|
|
179
|
+
},
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
const credential = Credential.deserialize(
|
|
183
|
+
await method.createCredential({
|
|
184
|
+
challenge: createChallenge({ amount: '1', chainId, supportedModes: ['pull'] }),
|
|
185
|
+
context: {},
|
|
186
|
+
}),
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
expect(calls).toHaveLength(1)
|
|
190
|
+
expect(calls[0]!.operation.kind).toBe('executeCalls')
|
|
191
|
+
if (calls[0]!.operation.kind !== 'executeCalls') throw new Error('expected executeCalls')
|
|
192
|
+
expect(calls[0]!.operation.calls).toBeUndefined()
|
|
193
|
+
expect(findCalls).toHaveBeenCalledOnce()
|
|
194
|
+
expect(findCalls.mock.calls[0]?.[1].account).toBe(selectedAccount.address)
|
|
195
|
+
expect(credential.payload).toEqual({ signature: '0xdeadbeef', type: 'transaction' })
|
|
196
|
+
} finally {
|
|
197
|
+
vi.doUnmock('viem/actions')
|
|
198
|
+
vi.doUnmock('../internal/auto-swap.js')
|
|
199
|
+
vi.resetModules()
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
test('zero-amount proof binds to the root payer for an access-key account', async () => {
|
|
204
|
+
vi.resetModules()
|
|
205
|
+
// Capture the typed data so we can assert what the proof commits to.
|
|
206
|
+
let signedTypedData: { message: { account: string } } | undefined
|
|
207
|
+
const signTypedData = vi.fn(async (_client: unknown, parameters: typeof signedTypedData) => {
|
|
208
|
+
signedTypedData = parameters
|
|
209
|
+
return '0xdeadbeef'
|
|
210
|
+
})
|
|
211
|
+
vi.doMock('viem/actions', () => ({
|
|
212
|
+
prepareTransactionRequest: vi.fn(),
|
|
213
|
+
sendCallsSync: vi.fn(),
|
|
214
|
+
signTransaction: vi.fn(),
|
|
215
|
+
signTypedData,
|
|
216
|
+
}))
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const { charge: chargeWithMockedActions } = await import('./Charge.js')
|
|
220
|
+
const chainId = 42431
|
|
221
|
+
// An access-key account signs with its own key but reports the root
|
|
222
|
+
// account as `address`; the proof must bind to that root payer.
|
|
223
|
+
const accessKey = Account.fromSecp256k1(Secp256k1.randomPrivateKey(), {
|
|
224
|
+
access: account,
|
|
225
|
+
})
|
|
226
|
+
expect(accessKey.address).toBe(account.address)
|
|
227
|
+
expect(accessKey.accessKeyAddress).not.toBe(account.address)
|
|
228
|
+
|
|
229
|
+
const client = createClient({
|
|
230
|
+
account: accessKey,
|
|
231
|
+
chain: tempoLocalnet,
|
|
232
|
+
transport: http('http://127.0.0.1'),
|
|
233
|
+
})
|
|
234
|
+
const resolveAccount = vi.fn()
|
|
235
|
+
const method = chargeWithMockedActions({
|
|
236
|
+
account: accessKey,
|
|
237
|
+
getClient: () => client,
|
|
238
|
+
resolveAccount,
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
const credential = Credential.deserialize(
|
|
242
|
+
await method.createCredential({
|
|
243
|
+
challenge: createChallenge({ chainId }),
|
|
244
|
+
context: {},
|
|
245
|
+
}),
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
expect(signTypedData).toHaveBeenCalledOnce()
|
|
249
|
+
expect(resolveAccount).not.toHaveBeenCalled()
|
|
250
|
+
expect(signedTypedData?.message.account).toBe(account.address)
|
|
251
|
+
expect(credential.payload).toEqual({ signature: '0xdeadbeef', type: 'proof' })
|
|
252
|
+
expect(credential.source).toBe(`did:pkh:eip155:${chainId}:${account.address}`)
|
|
253
|
+
} finally {
|
|
254
|
+
vi.doUnmock('viem/actions')
|
|
255
|
+
vi.resetModules()
|
|
256
|
+
}
|
|
257
|
+
})
|
|
258
|
+
|
|
86
259
|
test('uses challenge chainId for non-zero transaction source', async () => {
|
|
87
260
|
vi.resetModules()
|
|
88
261
|
const prepareTransactionRequest = vi.fn(async () => ({}))
|
|
@@ -20,6 +20,20 @@ import * as Charge_internal from '../internal/charge.js'
|
|
|
20
20
|
import * as defaults from '../internal/defaults.js'
|
|
21
21
|
import * as Proof from '../internal/proof.js'
|
|
22
22
|
import * as Methods from '../Methods.js'
|
|
23
|
+
import type * as AccountResolution from './ResolveAccount.js'
|
|
24
|
+
|
|
25
|
+
/** Runtime context accepted by the Tempo charge client method. */
|
|
26
|
+
export type ChargeContext = {
|
|
27
|
+
account?: Account.getResolver.Parameters['account'] | undefined
|
|
28
|
+
autoSwap?: AutoSwap.resolve.Value | undefined
|
|
29
|
+
mode?: Methods.ChargeMode | undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const chargeContextSchema = z.object({
|
|
33
|
+
account: z.optional(z.custom<ChargeContext['account']>()),
|
|
34
|
+
autoSwap: z.optional(z.custom<ChargeContext['autoSwap']>()),
|
|
35
|
+
mode: z.optional(z.enum(Methods.chargeModes)),
|
|
36
|
+
})
|
|
23
37
|
|
|
24
38
|
/**
|
|
25
39
|
* Creates a Tempo charge method intent for usage on the client.
|
|
@@ -44,11 +58,7 @@ export function charge(parameters: charge.Parameters = {}) {
|
|
|
44
58
|
const getAccount = Account.getResolver({ account: parameters.account })
|
|
45
59
|
|
|
46
60
|
return Method.toClient(Methods.charge, {
|
|
47
|
-
context:
|
|
48
|
-
account: z.optional(z.custom<Account.getResolver.Parameters['account']>()),
|
|
49
|
-
autoSwap: z.optional(z.custom<charge.AutoSwap>()),
|
|
50
|
-
mode: z.optional(z.enum(Methods.chargeModes)),
|
|
51
|
-
}),
|
|
61
|
+
context: chargeContextSchema,
|
|
52
62
|
|
|
53
63
|
async createCredential({ challenge, context }) {
|
|
54
64
|
// Chain pinning: reject a challenge whose chain ID conflicts with the
|
|
@@ -68,24 +78,30 @@ export function charge(parameters: charge.Parameters = {}) {
|
|
|
68
78
|
if (chainId === undefined)
|
|
69
79
|
throw new Error('No `chainId` provided. Pass a chain ID in the challenge or client.')
|
|
70
80
|
|
|
71
|
-
const account = getAccount(client, context)
|
|
72
|
-
|
|
73
81
|
const { request } = challenge
|
|
74
82
|
const { amount, methodDetails } = request
|
|
83
|
+
const supportedModes = (methodDetails?.supportedModes as
|
|
84
|
+
| readonly Methods.ChargeMode[]
|
|
85
|
+
| undefined) ?? ['pull', 'push']
|
|
86
|
+
const defaultAccount = getAccount(client, context)
|
|
75
87
|
|
|
76
88
|
// Zero-amount: sign EIP-712 typed data instead of creating a transaction.
|
|
77
89
|
if (BigInt(amount) === 0n) {
|
|
78
90
|
const signature = await signTypedData(client, {
|
|
79
|
-
account,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
account: defaultAccount,
|
|
92
|
+
// `account` here is the signing account; the proof's bound payer is
|
|
93
|
+
// `account.address` (echoed in the credential `source` below).
|
|
94
|
+
...Proof.typedData({
|
|
95
|
+
account: defaultAccount.address,
|
|
96
|
+
chainId,
|
|
97
|
+
challengeId: challenge.id,
|
|
98
|
+
realm: challenge.realm,
|
|
99
|
+
}),
|
|
84
100
|
})
|
|
85
101
|
return Credential.serialize({
|
|
86
102
|
challenge,
|
|
87
103
|
payload: { signature, type: 'proof' },
|
|
88
|
-
source: Proof.proofSource({ address:
|
|
104
|
+
source: Proof.proofSource({ address: defaultAccount.address, chainId }),
|
|
89
105
|
})
|
|
90
106
|
}
|
|
91
107
|
|
|
@@ -100,22 +116,6 @@ export function charge(parameters: charge.Parameters = {}) {
|
|
|
100
116
|
}
|
|
101
117
|
}
|
|
102
118
|
}
|
|
103
|
-
const supportedModes = (methodDetails?.supportedModes as
|
|
104
|
-
| readonly Methods.ChargeMode[]
|
|
105
|
-
| undefined) ?? ['pull', 'push']
|
|
106
|
-
const mode = (() => {
|
|
107
|
-
const explicitMode = context?.mode ?? parameters.mode
|
|
108
|
-
if (explicitMode) {
|
|
109
|
-
if (!supportedModes.includes(explicitMode))
|
|
110
|
-
throw new Error(`Challenge does not support ${explicitMode} mode.`)
|
|
111
|
-
return explicitMode
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const preferredMode = account.type === 'json-rpc' ? 'push' : 'pull'
|
|
115
|
-
if (supportedModes.includes(preferredMode)) return preferredMode
|
|
116
|
-
return supportedModes[0]!
|
|
117
|
-
})()
|
|
118
|
-
|
|
119
119
|
const memo = methodDetails?.memo
|
|
120
120
|
? (methodDetails.memo as Hex.Hex)
|
|
121
121
|
: Attribution.encode({ challengeId: challenge.id, clientId, serverId: challenge.realm })
|
|
@@ -127,13 +127,14 @@ export function charge(parameters: charge.Parameters = {}) {
|
|
|
127
127
|
},
|
|
128
128
|
recipient: request.recipient as Address,
|
|
129
129
|
})
|
|
130
|
-
const transferCalls = transfers.map(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
130
|
+
const transferCalls = transfers.map(
|
|
131
|
+
(transfer): AccountResolution.ResolveAccountCall =>
|
|
132
|
+
Actions.token.transfer.call({
|
|
133
|
+
amount: BigInt(transfer.amount),
|
|
134
|
+
...(transfer.memo && { memo: transfer.memo as Hex.Hex }),
|
|
135
|
+
to: transfer.recipient as Address,
|
|
136
|
+
token: currency,
|
|
137
|
+
}) as AccountResolution.ResolveAccountCall,
|
|
137
138
|
)
|
|
138
139
|
|
|
139
140
|
const autoSwap = AutoSwap.resolve(
|
|
@@ -141,6 +142,29 @@ export function charge(parameters: charge.Parameters = {}) {
|
|
|
141
142
|
AutoSwap.defaultCurrencies,
|
|
142
143
|
)
|
|
143
144
|
|
|
145
|
+
const account =
|
|
146
|
+
(await parameters.resolveAccount?.({
|
|
147
|
+
account: defaultAccount,
|
|
148
|
+
chainId,
|
|
149
|
+
operation: {
|
|
150
|
+
kind: 'executeCalls',
|
|
151
|
+
...(autoSwap ? {} : { calls: transferCalls }),
|
|
152
|
+
},
|
|
153
|
+
})) ?? defaultAccount
|
|
154
|
+
|
|
155
|
+
const mode = (() => {
|
|
156
|
+
const explicitMode = context?.mode ?? parameters.mode
|
|
157
|
+
if (explicitMode) {
|
|
158
|
+
if (!supportedModes.includes(explicitMode))
|
|
159
|
+
throw new Error(`Challenge does not support ${explicitMode} mode.`)
|
|
160
|
+
return explicitMode
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const preferredMode = account.type === 'json-rpc' ? 'push' : 'pull'
|
|
164
|
+
if (supportedModes.includes(preferredMode)) return preferredMode
|
|
165
|
+
return supportedModes[0]!
|
|
166
|
+
})()
|
|
167
|
+
|
|
144
168
|
const swapCalls = autoSwap
|
|
145
169
|
? await AutoSwap.findCalls(client, {
|
|
146
170
|
account: account.address,
|
|
@@ -198,6 +222,9 @@ export function charge(parameters: charge.Parameters = {}) {
|
|
|
198
222
|
|
|
199
223
|
export declare namespace charge {
|
|
200
224
|
type AutoSwap = AutoSwap.resolve.Value
|
|
225
|
+
type Context = ChargeContext
|
|
226
|
+
type ResolveAccount = AccountResolution.ResolveAccount
|
|
227
|
+
type ResolveAccountInfo = AccountResolution.ResolveAccountInfo
|
|
201
228
|
|
|
202
229
|
type Parameters = {
|
|
203
230
|
/**
|
|
@@ -232,6 +259,8 @@ export declare namespace charge {
|
|
|
232
259
|
* @default `'push'` for JSON-RPC accounts, `'pull'` for local accounts.
|
|
233
260
|
*/
|
|
234
261
|
mode?: Methods.ChargeMode | undefined
|
|
262
|
+
/** Selects the account that signs this charge after the challenge and chain are known. */
|
|
263
|
+
resolveAccount?: ResolveAccount | undefined
|
|
235
264
|
} & Account.getResolver.Parameters &
|
|
236
265
|
Client.getResolver.Parameters
|
|
237
266
|
}
|
|
@@ -14,14 +14,14 @@ const sessionLegacyClient = Object.assign(sessionLegacy_, { method: sessionLegac
|
|
|
14
14
|
export { sessionClient as session }
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Creates
|
|
17
|
+
* Creates the common Tempo `charge` and `session` client methods from shared parameters.
|
|
18
18
|
*
|
|
19
19
|
* @example
|
|
20
20
|
* ```ts
|
|
21
21
|
* import { Mppx, tempo } from 'mppx/client'
|
|
22
22
|
*
|
|
23
23
|
* const mppx = Mppx.create({
|
|
24
|
-
* methods: [tempo({ account })],
|
|
24
|
+
* methods: [tempo.common({ account })],
|
|
25
25
|
* })
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
@@ -34,6 +34,8 @@ export namespace tempo {
|
|
|
34
34
|
|
|
35
35
|
/** Creates a Tempo `charge` client method for one-time TIP-20 token transfers. */
|
|
36
36
|
export const charge = charge_
|
|
37
|
+
/** Creates the common Tempo `charge` and `session` client methods from shared parameters. */
|
|
38
|
+
export const common = tempo
|
|
37
39
|
/** Creates a TIP-1034 client method for Mppx registration. Use `tempo.session.manager()` for direct lifecycle control. */
|
|
38
40
|
export const session = sessionClient
|
|
39
41
|
/** @deprecated Use `tempo.session()` for the TIP-1034 session client method. */
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type * as Hex from 'ox/Hex'
|
|
2
|
+
import type { Account, Address } from 'viem'
|
|
3
|
+
|
|
4
|
+
import type { MaybePromise } from '../../internal/types.js'
|
|
5
|
+
|
|
6
|
+
/** Resolves the account that should satisfy an mppx account operation. */
|
|
7
|
+
export type ResolveAccount = (info: ResolveAccountInfo) => MaybePromise<Account | undefined>
|
|
8
|
+
|
|
9
|
+
/** Account-resolution details for a client credential operation. */
|
|
10
|
+
export type ResolveAccountInfo = {
|
|
11
|
+
/** Account mppx will use when the hook returns `undefined`. */
|
|
12
|
+
account: Account
|
|
13
|
+
/** EIP-155 chain ID used for the operation. */
|
|
14
|
+
chainId: number
|
|
15
|
+
/** Capability the selected account must satisfy. */
|
|
16
|
+
operation: ResolveAccountOperation
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Capability an mppx-selected account must satisfy. */
|
|
20
|
+
export type ResolveAccountOperation =
|
|
21
|
+
| {
|
|
22
|
+
kind: 'executeCalls'
|
|
23
|
+
/**
|
|
24
|
+
* Exact EVM calls the selected account will execute.
|
|
25
|
+
*
|
|
26
|
+
* Omitted when the calls depend on which account is selected, such as
|
|
27
|
+
* account-balance-dependent auto-swap routing.
|
|
28
|
+
*/
|
|
29
|
+
calls?: readonly ResolveAccountCall[] | undefined
|
|
30
|
+
}
|
|
31
|
+
| {
|
|
32
|
+
kind: 'authorizePaymentChannel'
|
|
33
|
+
/**
|
|
34
|
+
* Signer required by an existing reusable channel. Omitted when opening
|
|
35
|
+
* a new channel or when no existing channel has fixed a signer yet.
|
|
36
|
+
*/
|
|
37
|
+
authority?: Address | undefined
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** EVM call data used by account resolvers for scoped account selection. */
|
|
41
|
+
export type ResolveAccountCall = {
|
|
42
|
+
/** Contract address being called. */
|
|
43
|
+
to: Address
|
|
44
|
+
/** Calldata being sent. */
|
|
45
|
+
data: Hex.Hex
|
|
46
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Address, Secp256k1 } from 'ox'
|
|
2
|
+
import { TxEnvelopeTempo } from 'ox/tempo'
|
|
1
3
|
import { encodeFunctionData, maxUint256, toHex } from 'viem'
|
|
2
4
|
import { Abis, Addresses, Transaction } from 'viem/tempo'
|
|
3
5
|
import { afterEach, describe, expect, test, vi } from 'vp/test'
|
|
@@ -499,30 +501,82 @@ describe('fillHostedFeePayerTransaction', () => {
|
|
|
499
501
|
} as const
|
|
500
502
|
|
|
501
503
|
test('uses hosted fillTransaction and preserves sender-committed fields', async () => {
|
|
504
|
+
// Sign over the payload built from the actual RPC request body so this
|
|
505
|
+
// verifies recovery parity with the real request shape.
|
|
506
|
+
const sponsorPrivateKey =
|
|
507
|
+
'0x0000000000000000000000000000000000000000000000000000000000000042' as const
|
|
508
|
+
const sponsorAddress = Address.fromPublicKey(
|
|
509
|
+
Secp256k1.getPublicKey({ privateKey: sponsorPrivateKey }),
|
|
510
|
+
)
|
|
511
|
+
let realFeePayerSignature: ReturnType<typeof Secp256k1.sign> | undefined
|
|
512
|
+
|
|
502
513
|
const calls: { init?: RequestInit | undefined; input: RequestInfo | URL }[] = []
|
|
503
514
|
const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
504
515
|
calls.push({ init, input })
|
|
516
|
+
const rpc = JSON.parse(init!.body as string).params[0]
|
|
517
|
+
const quantity = (value: unknown) =>
|
|
518
|
+
value === undefined ? undefined : BigInt(value as string)
|
|
519
|
+
realFeePayerSignature = Secp256k1.sign({
|
|
520
|
+
payload: TxEnvelopeTempo.getFeePayerSignPayload(
|
|
521
|
+
TxEnvelopeTempo.from({
|
|
522
|
+
accessList: rpc.accessList,
|
|
523
|
+
calls: rpc.calls.map(({ value, ...call }: any) => ({
|
|
524
|
+
...call,
|
|
525
|
+
...(value && value !== '0x' ? { value: BigInt(value) } : {}),
|
|
526
|
+
})),
|
|
527
|
+
chainId: hostedTransaction.chainId,
|
|
528
|
+
feeToken: defaults.tokens.pathUsd,
|
|
529
|
+
from: rpc.from,
|
|
530
|
+
...(quantity(rpc.gas) !== undefined ? { gas: quantity(rpc.gas) } : {}),
|
|
531
|
+
...(rpc.keyAuthorization !== undefined
|
|
532
|
+
? { keyAuthorization: rpc.keyAuthorization }
|
|
533
|
+
: {}),
|
|
534
|
+
...(quantity(rpc.maxFeePerGas) !== undefined
|
|
535
|
+
? { maxFeePerGas: quantity(rpc.maxFeePerGas) }
|
|
536
|
+
: {}),
|
|
537
|
+
...(quantity(rpc.maxPriorityFeePerGas) !== undefined
|
|
538
|
+
? { maxPriorityFeePerGas: quantity(rpc.maxPriorityFeePerGas) }
|
|
539
|
+
: {}),
|
|
540
|
+
...(quantity(rpc.nonce) !== undefined ? { nonce: quantity(rpc.nonce) } : {}),
|
|
541
|
+
...(quantity(rpc.nonceKey) !== undefined ? { nonceKey: quantity(rpc.nonceKey) } : {}),
|
|
542
|
+
type: 'tempo',
|
|
543
|
+
...(rpc.validAfter !== undefined ? { validAfter: Number(BigInt(rpc.validAfter)) } : {}),
|
|
544
|
+
...(rpc.validBefore !== undefined
|
|
545
|
+
? { validBefore: Number(BigInt(rpc.validBefore)) }
|
|
546
|
+
: {}),
|
|
547
|
+
} as any) as any,
|
|
548
|
+
{ sender: rpc.from },
|
|
549
|
+
),
|
|
550
|
+
privateKey: sponsorPrivateKey,
|
|
551
|
+
})
|
|
505
552
|
return new Response(
|
|
506
|
-
JSON.stringify(
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
553
|
+
JSON.stringify(
|
|
554
|
+
{
|
|
555
|
+
result: {
|
|
556
|
+
tx: {
|
|
557
|
+
feePayerSignature: realFeePayerSignature,
|
|
558
|
+
feeToken: defaults.tokens.pathUsd,
|
|
559
|
+
gas: '0x1',
|
|
560
|
+
maxFeePerGas: '0x2',
|
|
561
|
+
},
|
|
513
562
|
},
|
|
514
563
|
},
|
|
515
|
-
|
|
564
|
+
(_key, value) => (typeof value === 'bigint' ? toHex(value) : value),
|
|
565
|
+
),
|
|
516
566
|
)
|
|
517
567
|
})
|
|
518
568
|
vi.stubGlobal('fetch', fetchMock)
|
|
519
569
|
|
|
520
|
-
const
|
|
570
|
+
const result = await fillHostedFeePayerTransaction({
|
|
521
571
|
allowedFeeTokens: defaultAllowedFeeTokens(defaults.chainId.mainnet),
|
|
522
572
|
transaction: hostedTransaction as any,
|
|
523
573
|
url: 'https://sponsor.example/tp_key',
|
|
524
574
|
})
|
|
525
575
|
|
|
576
|
+
expect(result.feeToken).toBe(defaults.tokens.pathUsd)
|
|
577
|
+
expect(result.feePayer.toLowerCase()).toBe(sponsorAddress.toLowerCase())
|
|
578
|
+
const serialized = result.serializedTransaction
|
|
579
|
+
|
|
526
580
|
expect(fetchMock).toHaveBeenCalledOnce()
|
|
527
581
|
expect(calls[0]!.input).toBe('https://sponsor.example/tp_key')
|
|
528
582
|
const body = JSON.parse(calls[0]!.init!.body as string)
|
|
@@ -554,7 +608,8 @@ describe('fillHostedFeePayerTransaction', () => {
|
|
|
554
608
|
expect(transaction.maxFeePerGas).toBe(hostedTransaction.maxFeePerGas)
|
|
555
609
|
expect(transaction.calls).toEqual(hostedTransaction.calls)
|
|
556
610
|
expect(transaction.feeToken).toBe(defaults.tokens.pathUsd)
|
|
557
|
-
expect(transaction.feePayerSignature).
|
|
611
|
+
expect(BigInt(transaction.feePayerSignature!.r)).toBe(realFeePayerSignature!.r)
|
|
612
|
+
expect(BigInt(transaction.feePayerSignature!.s)).toBe(realFeePayerSignature!.s)
|
|
558
613
|
})
|
|
559
614
|
|
|
560
615
|
test('error: requires hosted fee payer to return a feeToken', async () => {
|