mppx 0.8.13 → 0.8.14
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 +16 -0
- package/dist/Errors.d.ts +10 -0
- package/dist/Errors.d.ts.map +1 -1
- package/dist/Errors.js +11 -2
- package/dist/Errors.js.map +1 -1
- package/dist/Method.d.ts +105 -3
- package/dist/Method.d.ts.map +1 -1
- package/dist/Method.js +102 -2
- package/dist/Method.js.map +1 -1
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +12 -2
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/client/internal/MethodChallenge.d.ts +17 -0
- package/dist/client/internal/MethodChallenge.d.ts.map +1 -0
- package/dist/client/internal/MethodChallenge.js +11 -0
- package/dist/client/internal/MethodChallenge.js.map +1 -0
- package/dist/server/Mppx.d.ts +23 -4
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +91 -82
- package/dist/server/Mppx.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +1 -0
- package/dist/tempo/client/Methods.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.d.ts +1 -0
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js +7 -2
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/legacy/client/ChannelOps.d.ts.map +1 -1
- package/dist/tempo/legacy/client/ChannelOps.js +2 -1
- package/dist/tempo/legacy/client/ChannelOps.js.map +1 -1
- package/dist/tempo/server/Charge.d.ts +38 -77
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +289 -209
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +12 -1
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Methods.js +4 -0
- package/dist/tempo/server/Methods.js.map +1 -1
- package/dist/tempo/server/Relay.d.ts +48 -0
- package/dist/tempo/server/Relay.d.ts.map +1 -0
- package/dist/tempo/server/Relay.js +177 -0
- package/dist/tempo/server/Relay.js.map +1 -0
- package/dist/tempo/server/SponsorBudget.d.ts +59 -0
- package/dist/tempo/server/SponsorBudget.d.ts.map +1 -0
- package/dist/tempo/server/SponsorBudget.js +144 -0
- package/dist/tempo/server/SponsorBudget.js.map +1 -0
- 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 +7 -1
- package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -1
- package/dist/tempo/session/client/ChannelOps.js +9 -4
- package/dist/tempo/session/client/ChannelOps.js.map +1 -1
- package/dist/tempo/session/client/CredentialState.d.ts +5 -1
- package/dist/tempo/session/client/CredentialState.d.ts.map +1 -1
- package/dist/tempo/session/client/CredentialState.js +37 -19
- package/dist/tempo/session/client/CredentialState.js.map +1 -1
- package/dist/tempo/session/client/Runtime.d.ts +8 -0
- package/dist/tempo/session/client/Runtime.d.ts.map +1 -1
- package/dist/tempo/session/client/Runtime.js +17 -0
- package/dist/tempo/session/client/Runtime.js.map +1 -1
- package/dist/tempo/session/client/Session.d.ts +15 -5
- package/dist/tempo/session/client/Session.d.ts.map +1 -1
- package/dist/tempo/session/client/Session.js +93 -36
- package/dist/tempo/session/client/Session.js.map +1 -1
- package/dist/tempo/session/client/SessionManager.d.ts +8 -1
- package/dist/tempo/session/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/session/client/SessionManager.js +54 -26
- package/dist/tempo/session/client/SessionManager.js.map +1 -1
- package/dist/tempo/session/client/Transports.d.ts +7 -5
- package/dist/tempo/session/client/Transports.d.ts.map +1 -1
- package/dist/tempo/session/client/Transports.js +48 -24
- package/dist/tempo/session/client/Transports.js.map +1 -1
- package/dist/tempo/session/precompile/Chain.d.ts.map +1 -1
- package/dist/tempo/session/precompile/Chain.js +96 -8
- package/dist/tempo/session/precompile/Chain.js.map +1 -1
- package/dist/tempo/session/precompile/Channel.d.ts +2 -0
- package/dist/tempo/session/precompile/Channel.d.ts.map +1 -1
- package/dist/tempo/session/precompile/Channel.js +4 -0
- package/dist/tempo/session/precompile/Channel.js.map +1 -1
- package/dist/tempo/session/server/RequestState.js +3 -1
- package/dist/tempo/session/server/RequestState.js.map +1 -1
- package/package.json +1 -1
- package/src/Errors.test.ts +23 -0
- package/src/Errors.ts +21 -2
- package/src/Method.test.ts +102 -1
- package/src/Method.ts +241 -5
- package/src/client/Mppx.test-d.ts +3 -3
- package/src/client/internal/Fetch.ts +16 -7
- package/src/client/internal/MethodChallenge.ts +30 -0
- package/src/server/Mppx.test-d.ts +2 -0
- package/src/server/Mppx.test.ts +205 -0
- package/src/server/Mppx.ts +167 -103
- package/src/tempo/PublicExports.test-d.ts +20 -0
- package/src/tempo/internal/fee-payer.ts +7 -3
- package/src/tempo/legacy/client/ChannelOps.test.ts +7 -0
- package/src/tempo/legacy/client/ChannelOps.ts +2 -1
- package/src/tempo/server/Charge.test.ts +337 -63
- package/src/tempo/server/Charge.ts +435 -267
- package/src/tempo/server/Methods.ts +11 -0
- package/src/tempo/server/Relay.test.ts +523 -0
- package/src/tempo/server/Relay.ts +288 -0
- package/src/tempo/server/SponsorBudget.test.ts +125 -0
- package/src/tempo/server/SponsorBudget.ts +213 -0
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/session/client/ChannelOps.test.ts +17 -2
- package/src/tempo/session/client/ChannelOps.ts +12 -4
- package/src/tempo/session/client/CredentialState.ts +61 -20
- package/src/tempo/session/client/Runtime.test.ts +12 -0
- package/src/tempo/session/client/Runtime.ts +22 -0
- package/src/tempo/session/client/Session.test.ts +235 -2
- package/src/tempo/session/client/Session.ts +130 -41
- package/src/tempo/session/client/SessionManager.test.ts +102 -4
- package/src/tempo/session/client/SessionManager.ts +71 -27
- package/src/tempo/session/client/Transports.test.ts +32 -1
- package/src/tempo/session/client/Transports.ts +79 -35
- package/src/tempo/session/precompile/Chain.integration.test.ts +19 -1
- package/src/tempo/session/precompile/Chain.test.ts +197 -10
- package/src/tempo/session/precompile/Chain.ts +102 -8
- package/src/tempo/session/precompile/Channel.test.ts +9 -0
- package/src/tempo/session/precompile/Channel.ts +5 -0
- package/src/tempo/session/server/RequestState.test.ts +8 -1
- package/src/tempo/session/server/RequestState.ts +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Account, Address, Client, Hex } from 'viem'
|
|
2
|
-
import { encodeFunctionData, isAddressEqual, parseEventLogs } from 'viem'
|
|
2
|
+
import { decodeFunctionData, encodeFunctionData, isAddressEqual, parseEventLogs } from 'viem'
|
|
3
3
|
import {
|
|
4
4
|
call,
|
|
5
5
|
prepareTransactionRequest,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
signTransaction,
|
|
11
11
|
waitForTransactionReceipt,
|
|
12
12
|
} from 'viem/actions'
|
|
13
|
-
import { Transaction } from 'viem/tempo'
|
|
13
|
+
import { Abis, Addresses, Transaction } from 'viem/tempo'
|
|
14
14
|
|
|
15
15
|
import { BadRequestError, VerificationFailedError } from '../../../Errors.js'
|
|
16
16
|
import * as FeePayer from '../../internal/fee-payer.js'
|
|
@@ -401,6 +401,7 @@ export type ChannelTransactionOptions = {
|
|
|
401
401
|
|
|
402
402
|
type ParsedPrecompileCredentialTransaction = {
|
|
403
403
|
call: Transaction.TransactionTempo['calls'][number] & { data: Hex; to: Address }
|
|
404
|
+
prefixCalls: readonly Transaction.TransactionTempo['calls'][number][]
|
|
404
405
|
transaction: ReturnType<(typeof Transaction)['deserialize']>
|
|
405
406
|
}
|
|
406
407
|
|
|
@@ -418,11 +419,11 @@ function parsePrecompileCredentialTransaction(parameters: {
|
|
|
418
419
|
serializedTransaction as Transaction.TransactionSerializedTempo,
|
|
419
420
|
)
|
|
420
421
|
const calls = transaction.calls
|
|
421
|
-
if (calls.length !== 1)
|
|
422
|
+
if (calls.length !== 1 && calls.length !== 3)
|
|
422
423
|
throw new VerificationFailedError({
|
|
423
|
-
reason: `TIP-1034 ${label} transaction must contain
|
|
424
|
+
reason: `TIP-1034 ${label} transaction must contain one management call, optionally preceded by an auto-swap`,
|
|
424
425
|
})
|
|
425
|
-
const call = calls[
|
|
426
|
+
const call = calls[calls.length - 1]!
|
|
426
427
|
if (!call.to || !isAddressEqual(call.to, escrowContract))
|
|
427
428
|
throw new VerificationFailedError({
|
|
428
429
|
reason: `TIP-1034 ${label} transaction targets the wrong address`,
|
|
@@ -431,7 +432,88 @@ function parsePrecompileCredentialTransaction(parameters: {
|
|
|
431
432
|
throw new VerificationFailedError({
|
|
432
433
|
reason: `TIP-1034 ${label} transaction is missing calldata`,
|
|
433
434
|
})
|
|
434
|
-
return {
|
|
435
|
+
return {
|
|
436
|
+
transaction,
|
|
437
|
+
call: { ...call, data: call.data, to: call.to },
|
|
438
|
+
prefixCalls: calls.slice(0, -1),
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function validateAutoSwapPrefix(parameters: {
|
|
443
|
+
amountOut: bigint
|
|
444
|
+
currency: Address
|
|
445
|
+
label: 'open' | 'topUp'
|
|
446
|
+
prefixCalls: readonly Transaction.TransactionTempo['calls'][number][]
|
|
447
|
+
}) {
|
|
448
|
+
const { amountOut, currency, label, prefixCalls } = parameters
|
|
449
|
+
if (prefixCalls.length === 0) return
|
|
450
|
+
|
|
451
|
+
const fail = (reason: string): never => {
|
|
452
|
+
throw new VerificationFailedError({ reason: `TIP-1034 ${label} auto-swap ${reason}` })
|
|
453
|
+
}
|
|
454
|
+
if (prefixCalls.length !== 2) fail('must contain exactly approve and swap calls')
|
|
455
|
+
|
|
456
|
+
const approveCall = prefixCalls[0]!
|
|
457
|
+
const swapCall = prefixCalls[1]!
|
|
458
|
+
const approveTo = approveCall.to
|
|
459
|
+
const approveData = approveCall.data
|
|
460
|
+
const swapTo = swapCall.to
|
|
461
|
+
const swapData = swapCall.data
|
|
462
|
+
if (!approveTo || !approveData || !swapTo || !swapData)
|
|
463
|
+
fail('call is missing a target or calldata')
|
|
464
|
+
const checkedApproveTo = approveTo as Address
|
|
465
|
+
const checkedApproveData = approveData as Hex
|
|
466
|
+
const checkedSwapTo = swapTo as Address
|
|
467
|
+
const checkedSwapData = swapData as Hex
|
|
468
|
+
if ((approveCall.value ?? 0n) !== 0n || (swapCall.value ?? 0n) !== 0n)
|
|
469
|
+
fail('calls must not transfer native value')
|
|
470
|
+
if (!isAddressEqual(checkedSwapTo, Addresses.stablecoinDex)) fail('targets the wrong DEX')
|
|
471
|
+
|
|
472
|
+
const approve = (() => {
|
|
473
|
+
try {
|
|
474
|
+
return decodeFunctionData({ abi: Abis.tip20, data: checkedApproveData })
|
|
475
|
+
} catch {
|
|
476
|
+
return fail('approval calldata is invalid')
|
|
477
|
+
}
|
|
478
|
+
})()
|
|
479
|
+
const swap = (() => {
|
|
480
|
+
try {
|
|
481
|
+
return decodeFunctionData({ abi: Abis.stablecoinDex, data: checkedSwapData })
|
|
482
|
+
} catch {
|
|
483
|
+
return fail('swap calldata is invalid')
|
|
484
|
+
}
|
|
485
|
+
})()
|
|
486
|
+
if (approve.functionName !== 'approve' || swap.functionName !== 'swapExactAmountOut')
|
|
487
|
+
fail('must contain approve followed by swapExactAmountOut')
|
|
488
|
+
|
|
489
|
+
const [spender, approvedAmount] = approve.args as readonly [Address, bigint]
|
|
490
|
+
const [tokenIn, tokenOut, swapAmountOut, maxAmountIn] = swap.args as readonly [
|
|
491
|
+
Address,
|
|
492
|
+
Address,
|
|
493
|
+
bigint,
|
|
494
|
+
bigint,
|
|
495
|
+
]
|
|
496
|
+
if (!isAddressEqual(checkedApproveTo, tokenIn)) fail('approval token does not match swap input')
|
|
497
|
+
if (!isAddressEqual(spender, Addresses.stablecoinDex)) fail('approval spender is not the DEX')
|
|
498
|
+
if (approvedAmount !== maxAmountIn) fail('approval amount does not match swap maximum input')
|
|
499
|
+
if (!isAddressEqual(tokenOut, currency)) fail('output token does not match channel currency')
|
|
500
|
+
if (swapAmountOut !== amountOut) fail('output amount does not match channel deposit')
|
|
501
|
+
if (isAddressEqual(tokenIn, tokenOut)) fail('input and output tokens must differ')
|
|
502
|
+
|
|
503
|
+
const canonicalApprove = encodeFunctionData({
|
|
504
|
+
abi: Abis.tip20,
|
|
505
|
+
functionName: 'approve',
|
|
506
|
+
args: [spender, approvedAmount],
|
|
507
|
+
})
|
|
508
|
+
const canonicalSwap = encodeFunctionData({
|
|
509
|
+
abi: Abis.stablecoinDex,
|
|
510
|
+
functionName: 'swapExactAmountOut',
|
|
511
|
+
args: [tokenIn, tokenOut, swapAmountOut, maxAmountIn],
|
|
512
|
+
})
|
|
513
|
+
if (checkedApproveData.toLowerCase() !== canonicalApprove.toLowerCase())
|
|
514
|
+
fail('approval calldata is not canonical')
|
|
515
|
+
if (checkedSwapData.toLowerCase() !== canonicalSwap.toLowerCase())
|
|
516
|
+
fail('swap calldata is not canonical')
|
|
435
517
|
}
|
|
436
518
|
|
|
437
519
|
async function simulateTempoTransaction(client: Client, request: unknown) {
|
|
@@ -796,7 +878,7 @@ export type BroadcastOpenTransactionParameters = {
|
|
|
796
878
|
export async function broadcastOpenTransaction(
|
|
797
879
|
parameters: BroadcastOpenTransactionParameters,
|
|
798
880
|
): Promise<BroadcastOpenTransactionResult> {
|
|
799
|
-
const { transaction, call } = parsePrecompileCredentialTransaction({
|
|
881
|
+
const { transaction, call, prefixCalls } = parsePrecompileCredentialTransaction({
|
|
800
882
|
escrowContract: parameters.escrowContract,
|
|
801
883
|
feePayer: parameters.feePayer,
|
|
802
884
|
label: 'open',
|
|
@@ -812,6 +894,12 @@ export async function broadcastOpenTransaction(
|
|
|
812
894
|
authorizedSigner: parameters.expectedAuthorizedSigner,
|
|
813
895
|
},
|
|
814
896
|
})
|
|
897
|
+
validateAutoSwapPrefix({
|
|
898
|
+
amountOut: open.deposit,
|
|
899
|
+
currency: parameters.expectedCurrency,
|
|
900
|
+
label: 'open',
|
|
901
|
+
prefixCalls,
|
|
902
|
+
})
|
|
815
903
|
const descriptor = ChannelOps.descriptorFromOpen({
|
|
816
904
|
chainId: parameters.chainId,
|
|
817
905
|
escrow: parameters.escrowContract,
|
|
@@ -920,7 +1008,7 @@ export type BroadcastTopUpTransactionParameters = {
|
|
|
920
1008
|
export async function broadcastTopUpTransaction(
|
|
921
1009
|
parameters: BroadcastTopUpTransactionParameters,
|
|
922
1010
|
): Promise<BroadcastTopUpTransactionResult> {
|
|
923
|
-
const { transaction, call } = parsePrecompileCredentialTransaction({
|
|
1011
|
+
const { transaction, call, prefixCalls } = parsePrecompileCredentialTransaction({
|
|
924
1012
|
escrowContract: parameters.escrowContract,
|
|
925
1013
|
feePayer: parameters.feePayer,
|
|
926
1014
|
label: 'topUp',
|
|
@@ -933,6 +1021,12 @@ export async function broadcastTopUpTransaction(
|
|
|
933
1021
|
additionalDeposit: parameters.additionalDeposit,
|
|
934
1022
|
},
|
|
935
1023
|
})
|
|
1024
|
+
validateAutoSwapPrefix({
|
|
1025
|
+
amountOut: parameters.additionalDeposit,
|
|
1026
|
+
currency: parameters.expectedCurrency,
|
|
1027
|
+
label: 'topUp',
|
|
1028
|
+
prefixCalls,
|
|
1029
|
+
})
|
|
936
1030
|
const receipt = await sendCredentialTransaction({
|
|
937
1031
|
challengeExpires: parameters.challengeExpires,
|
|
938
1032
|
chainId: parameters.chainId,
|
|
@@ -27,6 +27,15 @@ const transaction = {
|
|
|
27
27
|
} as const satisfies Channel.ExpiringNonceTransaction
|
|
28
28
|
|
|
29
29
|
describe('precompile Channel.computeId', () => {
|
|
30
|
+
test('uses the payer when the descriptor delegates to the zero address', () => {
|
|
31
|
+
expect(
|
|
32
|
+
Channel.resolveAuthorizedSigner({
|
|
33
|
+
...descriptor,
|
|
34
|
+
authorizedSigner: '0x0000000000000000000000000000000000000000',
|
|
35
|
+
}),
|
|
36
|
+
).toBe(descriptor.payer)
|
|
37
|
+
})
|
|
38
|
+
|
|
30
39
|
test('returns deterministic 32-byte hash for fixed inputs', () => {
|
|
31
40
|
const id = Channel.computeId({ ...descriptor, chainId })
|
|
32
41
|
expect(Channel.computeId({ ...descriptor, chainId })).toBe(id)
|
|
@@ -9,6 +9,11 @@ import type { ChannelDescriptor } from './Protocol.js'
|
|
|
9
9
|
/** Re-export of the TIP-1034 channel descriptor shape. */
|
|
10
10
|
export type { ChannelDescriptor } from './Protocol.js'
|
|
11
11
|
|
|
12
|
+
/** Resolves the descriptor's effective voucher signer; zero delegates to the payer. */
|
|
13
|
+
export function resolveAuthorizedSigner(descriptor: ChannelDescriptor): Address {
|
|
14
|
+
return BigInt(descriptor.authorizedSigner) === 0n ? descriptor.payer : descriptor.authorizedSigner
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
/** Tempo transaction shape used to derive the TIP-1034 `expiringNonceHash`. */
|
|
13
18
|
export type ExpiringNonceTransaction = (
|
|
14
19
|
| z_TransactionSerializableTempo
|
|
@@ -544,10 +544,17 @@ describe('SessionSnapshotHints', () => {
|
|
|
544
544
|
}
|
|
545
545
|
})
|
|
546
546
|
|
|
547
|
-
test('omits hints for missing, non-precompile, or finalized channels', async () => {
|
|
547
|
+
test('omits hints for missing, non-precompile, closing, or finalized channels', async () => {
|
|
548
548
|
await expect(
|
|
549
549
|
resolveSessionSnapshot({ amount: 1n, channelId, store: store(null) }),
|
|
550
550
|
).resolves.toBe(undefined)
|
|
551
|
+
await expect(
|
|
552
|
+
resolveSessionSnapshot({
|
|
553
|
+
amount: 1n,
|
|
554
|
+
channelId,
|
|
555
|
+
store: store(channel({ closeRequestedAt: 1n })),
|
|
556
|
+
}),
|
|
557
|
+
).resolves.toBe(undefined)
|
|
551
558
|
await expect(
|
|
552
559
|
resolveSessionSnapshot({
|
|
553
560
|
amount: 1n,
|
|
@@ -143,6 +143,7 @@ export async function resolveSessionSnapshot(
|
|
|
143
143
|
const channel = await store.getChannel(ChannelStore.normalizeChannelId(channelId))
|
|
144
144
|
if (!channel || !ChannelStore.isPrecompileState(channel)) return undefined
|
|
145
145
|
if (channel.finalized) return undefined
|
|
146
|
+
if (channel.closeRequestedAt !== 0n) return undefined
|
|
146
147
|
if (!channel.highestVoucher) return undefined
|
|
147
148
|
if (channel.highestVoucher.cumulativeAmount !== channel.highestVoucherAmount) return undefined
|
|
148
149
|
if (expected && !matchesSnapshotPaymentFields(channel, expected)) return undefined
|
|
@@ -151,8 +152,7 @@ export async function resolveSessionSnapshot(
|
|
|
151
152
|
acceptedCumulative: channel.highestVoucherAmount.toString(),
|
|
152
153
|
chainId: channel.chainId,
|
|
153
154
|
channelId: channel.channelId,
|
|
154
|
-
closeRequestedAt:
|
|
155
|
-
channel.closeRequestedAt === 0n ? undefined : channel.closeRequestedAt.toString(),
|
|
155
|
+
closeRequestedAt: undefined,
|
|
156
156
|
deposit: channel.deposit.toString(),
|
|
157
157
|
descriptor: channel.descriptor,
|
|
158
158
|
escrow: channel.escrowContract,
|