mppx 0.5.12 → 0.5.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/server/internal/html/config.d.ts.map +1 -1
- package/dist/server/internal/html/config.js +8 -1
- package/dist/server/internal/html/config.js.map +1 -1
- package/dist/tempo/Methods.d.ts +8 -0
- package/dist/tempo/Methods.d.ts.map +1 -1
- package/dist/tempo/Methods.js +6 -2
- package/dist/tempo/Methods.js.map +1 -1
- package/dist/tempo/client/Charge.d.ts +11 -1
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +14 -2
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +6 -0
- package/dist/tempo/client/Methods.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.d.ts +8 -0
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js +31 -4
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/server/Charge.d.ts +17 -0
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +13 -1
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +6 -0
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Session.d.ts +4 -0
- package/dist/tempo/server/Session.d.ts.map +1 -1
- package/dist/tempo/server/Session.js +36 -28
- package/dist/tempo/server/Session.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/Chain.d.ts +5 -0
- package/dist/tempo/session/Chain.d.ts.map +1 -1
- package/dist/tempo/session/Chain.js +202 -63
- package/dist/tempo/session/Chain.js.map +1 -1
- package/dist/tempo/session/ChannelStore.d.ts +1 -0
- package/dist/tempo/session/ChannelStore.d.ts.map +1 -1
- package/dist/tempo/session/ChannelStore.js +38 -15
- package/dist/tempo/session/ChannelStore.js.map +1 -1
- package/package.json +2 -2
- package/src/server/Transport.test.ts +20 -0
- package/src/server/internal/html/config.ts +9 -1
- package/src/tempo/Methods.test.ts +25 -0
- package/src/tempo/Methods.ts +30 -22
- package/src/tempo/client/Charge.ts +20 -6
- package/src/tempo/internal/fee-payer.test.ts +122 -12
- package/src/tempo/internal/fee-payer.ts +49 -4
- package/src/tempo/server/Charge.test.ts +259 -1
- package/src/tempo/server/Charge.ts +31 -0
- package/src/tempo/server/Session.test.ts +130 -1
- package/src/tempo/server/Session.ts +41 -35
- package/src/tempo/server/internal/html/main.ts +2 -2
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/session/Chain.test.ts +225 -2
- package/src/tempo/session/Chain.ts +250 -65
- package/src/tempo/session/ChannelStore.test.ts +23 -0
- package/src/tempo/session/ChannelStore.ts +46 -13
|
@@ -37,6 +37,7 @@ import * as Store from '../../Store.js'
|
|
|
37
37
|
import * as Client from '../../viem/Client.js'
|
|
38
38
|
import * as Account from '../internal/account.js'
|
|
39
39
|
import * as defaults from '../internal/defaults.js'
|
|
40
|
+
import * as FeePayer from '../internal/fee-payer.js'
|
|
40
41
|
import type * as types from '../internal/types.js'
|
|
41
42
|
import * as Methods from '../Methods.js'
|
|
42
43
|
import {
|
|
@@ -92,6 +93,7 @@ export function session<const parameters extends session.Parameters>(
|
|
|
92
93
|
channelStateTtl = 5_000,
|
|
93
94
|
currency = defaults.resolveCurrency(parameters),
|
|
94
95
|
decimals = defaults.decimals,
|
|
96
|
+
feePayerPolicy,
|
|
95
97
|
store: rawStore = Store.memory(),
|
|
96
98
|
suggestedDeposit,
|
|
97
99
|
unitType,
|
|
@@ -203,9 +205,10 @@ export function session<const parameters extends session.Parameters>(
|
|
|
203
205
|
payload,
|
|
204
206
|
methodDetails,
|
|
205
207
|
resolvedFeePayer,
|
|
208
|
+
feePayerPolicy,
|
|
206
209
|
waitForConfirmation,
|
|
207
210
|
)
|
|
208
|
-
lastOnChainVerified.set(
|
|
211
|
+
lastOnChainVerified.set(sessionReceipt.channelId, Date.now())
|
|
209
212
|
break
|
|
210
213
|
|
|
211
214
|
case 'topUp':
|
|
@@ -216,8 +219,9 @@ export function session<const parameters extends session.Parameters>(
|
|
|
216
219
|
payload,
|
|
217
220
|
methodDetails,
|
|
218
221
|
resolvedFeePayer,
|
|
222
|
+
feePayerPolicy,
|
|
219
223
|
)
|
|
220
|
-
lastOnChainVerified.set(
|
|
224
|
+
lastOnChainVerified.set(sessionReceipt.channelId, Date.now())
|
|
221
225
|
break
|
|
222
226
|
|
|
223
227
|
case 'voucher':
|
|
@@ -293,9 +297,13 @@ export declare namespace session {
|
|
|
293
297
|
'feePayer' | 'recipient'
|
|
294
298
|
>
|
|
295
299
|
|
|
300
|
+
type FeePayerPolicy = Partial<FeePayer.Policy>
|
|
301
|
+
|
|
296
302
|
type Parameters = {
|
|
297
303
|
/** TTL in milliseconds for cached on-chain channel state. After this duration, the server re-queries on-chain state during voucher handling to detect forced close requests. @default 5_000 */
|
|
298
304
|
channelStateTtl?: number | undefined
|
|
305
|
+
/** Override the fee-sponsor policy used for sponsored open/topUp transactions. */
|
|
306
|
+
feePayerPolicy?: FeePayerPolicy | undefined
|
|
299
307
|
/** Minimum voucher delta to accept (numeric string, default: "0"). */
|
|
300
308
|
minVoucherDelta?: string | undefined
|
|
301
309
|
/**
|
|
@@ -561,13 +569,11 @@ async function handleOpen(
|
|
|
561
569
|
payload: SessionCredentialPayload & { action: 'open' },
|
|
562
570
|
methodDetails: SessionMethodDetails,
|
|
563
571
|
feePayer: viem_Account | undefined,
|
|
572
|
+
feePayerPolicy: session.FeePayerPolicy | undefined,
|
|
564
573
|
waitForConfirmation: boolean,
|
|
565
574
|
): Promise<SessionReceipt> {
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
payload.cumulativeAmount,
|
|
569
|
-
payload.signature,
|
|
570
|
-
)
|
|
575
|
+
const channelId = ChannelStore.normalizeChannelId(payload.channelId)
|
|
576
|
+
const voucher = parseVoucherFromPayload(channelId, payload.cumulativeAmount, payload.signature)
|
|
571
577
|
|
|
572
578
|
const recipient = challenge.request.recipient as Address
|
|
573
579
|
const currency = challenge.request.currency as Address
|
|
@@ -577,9 +583,11 @@ async function handleOpen(
|
|
|
577
583
|
client,
|
|
578
584
|
serializedTransaction: payload.transaction,
|
|
579
585
|
escrowContract: methodDetails.escrowContract,
|
|
580
|
-
channelId
|
|
586
|
+
channelId,
|
|
581
587
|
recipient,
|
|
582
588
|
currency,
|
|
589
|
+
challengeExpires: challenge.expires,
|
|
590
|
+
feePayerPolicy,
|
|
583
591
|
feePayer,
|
|
584
592
|
waitForConfirmation,
|
|
585
593
|
})
|
|
@@ -612,7 +620,7 @@ async function handleOpen(
|
|
|
612
620
|
throw new InvalidSignatureError({ reason: 'invalid voucher signature' })
|
|
613
621
|
}
|
|
614
622
|
|
|
615
|
-
const updated = await store.updateChannel(
|
|
623
|
+
const updated = await store.updateChannel(channelId, (existing) => {
|
|
616
624
|
if (existing) {
|
|
617
625
|
if (voucher.cumulativeAmount <= existing.settledOnChain) {
|
|
618
626
|
throw new VerificationFailedError({
|
|
@@ -644,7 +652,7 @@ async function handleOpen(
|
|
|
644
652
|
}
|
|
645
653
|
}
|
|
646
654
|
return {
|
|
647
|
-
channelId
|
|
655
|
+
channelId,
|
|
648
656
|
chainId: methodDetails.chainId,
|
|
649
657
|
escrowContract: methodDetails.escrowContract,
|
|
650
658
|
closeRequestedAt: onChain.closeRequestedAt,
|
|
@@ -667,7 +675,7 @@ async function handleOpen(
|
|
|
667
675
|
|
|
668
676
|
return createSessionReceipt({
|
|
669
677
|
challengeId: challenge.id,
|
|
670
|
-
channelId:
|
|
678
|
+
channelId: updated.channelId,
|
|
671
679
|
acceptedCumulative: updated.highestVoucherAmount,
|
|
672
680
|
spent: updated.spent,
|
|
673
681
|
units: updated.units,
|
|
@@ -689,8 +697,10 @@ async function handleTopUp(
|
|
|
689
697
|
payload: SessionCredentialPayload & { action: 'topUp' },
|
|
690
698
|
methodDetails: SessionMethodDetails,
|
|
691
699
|
feePayer: viem_Account | undefined,
|
|
700
|
+
feePayerPolicy: session.FeePayerPolicy | undefined,
|
|
692
701
|
): Promise<SessionReceipt> {
|
|
693
|
-
const
|
|
702
|
+
const channelId = ChannelStore.normalizeChannelId(payload.channelId)
|
|
703
|
+
const channel = await store.getChannel(channelId)
|
|
694
704
|
if (!channel) {
|
|
695
705
|
throw new ChannelNotFoundError({ reason: 'channel not found' })
|
|
696
706
|
}
|
|
@@ -701,21 +711,23 @@ async function handleTopUp(
|
|
|
701
711
|
client,
|
|
702
712
|
serializedTransaction: payload.transaction,
|
|
703
713
|
escrowContract: methodDetails.escrowContract,
|
|
704
|
-
channelId
|
|
714
|
+
channelId,
|
|
705
715
|
currency: challenge.request.currency as Address,
|
|
706
716
|
declaredDeposit,
|
|
707
717
|
previousDeposit: channel.deposit,
|
|
718
|
+
challengeExpires: challenge.expires,
|
|
719
|
+
feePayerPolicy,
|
|
708
720
|
feePayer,
|
|
709
721
|
})
|
|
710
722
|
|
|
711
|
-
const updated = await store.updateChannel(
|
|
723
|
+
const updated = await store.updateChannel(channelId, (current) => {
|
|
712
724
|
if (!current) throw new ChannelNotFoundError({ reason: 'channel not found' })
|
|
713
725
|
return { ...current, deposit: onChainDeposit }
|
|
714
726
|
})
|
|
715
727
|
|
|
716
728
|
return createSessionReceipt({
|
|
717
729
|
challengeId: challenge.id,
|
|
718
|
-
channelId:
|
|
730
|
+
channelId: updated?.channelId ?? channel.channelId,
|
|
719
731
|
acceptedCumulative: updated?.highestVoucherAmount ?? channel.highestVoucherAmount,
|
|
720
732
|
spent: updated?.spent ?? channel.spent,
|
|
721
733
|
units: updated?.units ?? channel.units,
|
|
@@ -735,7 +747,8 @@ async function handleVoucher(
|
|
|
735
747
|
channelStateTtl: number,
|
|
736
748
|
lastOnChainVerified: Map<Hex, number>,
|
|
737
749
|
): Promise<SessionReceipt> {
|
|
738
|
-
const
|
|
750
|
+
const channelId = ChannelStore.normalizeChannelId(payload.channelId)
|
|
751
|
+
const channel = await store.getChannel(channelId)
|
|
739
752
|
if (!channel) {
|
|
740
753
|
throw new ChannelNotFoundError({ reason: 'channel not found' })
|
|
741
754
|
}
|
|
@@ -743,11 +756,7 @@ async function handleVoucher(
|
|
|
743
756
|
throw new ChannelClosedError({ reason: 'channel is finalized' })
|
|
744
757
|
}
|
|
745
758
|
|
|
746
|
-
const voucher = parseVoucherFromPayload(
|
|
747
|
-
payload.channelId,
|
|
748
|
-
payload.cumulativeAmount,
|
|
749
|
-
payload.signature,
|
|
750
|
-
)
|
|
759
|
+
const voucher = parseVoucherFromPayload(channelId, payload.cumulativeAmount, payload.signature)
|
|
751
760
|
|
|
752
761
|
// Use locally-stored channel state as a trusted cache instead of
|
|
753
762
|
// reading on-chain for every voucher. The on-chain state is verified
|
|
@@ -759,7 +768,7 @@ async function handleVoucher(
|
|
|
759
768
|
// To guard against the payer initiating a forced close while vouchers
|
|
760
769
|
// are still being accepted, re-query on-chain state when the cache
|
|
761
770
|
// exceeds the configured staleness TTL (default: 5s).
|
|
762
|
-
const lastVerified = lastOnChainVerified.get(
|
|
771
|
+
const lastVerified = lastOnChainVerified.get(channelId) ?? 0
|
|
763
772
|
const isStale = Date.now() - lastVerified > channelStateTtl
|
|
764
773
|
|
|
765
774
|
const onChain = await (async () => {
|
|
@@ -767,13 +776,13 @@ async function handleVoucher(
|
|
|
767
776
|
const onChainChannel = await getOnChainChannel(
|
|
768
777
|
client,
|
|
769
778
|
methodDetails.escrowContract,
|
|
770
|
-
|
|
779
|
+
channelId,
|
|
771
780
|
)
|
|
772
|
-
lastOnChainVerified.set(
|
|
781
|
+
lastOnChainVerified.set(channelId, Date.now())
|
|
773
782
|
// Persist closeRequestedAt so the cached path detects force-close
|
|
774
783
|
// between re-queries.
|
|
775
784
|
if (onChainChannel.closeRequestedAt !== 0n) {
|
|
776
|
-
await store.updateChannel(
|
|
785
|
+
await store.updateChannel(channelId, (current) =>
|
|
777
786
|
current ? { ...current, closeRequestedAt: onChainChannel.closeRequestedAt } : current,
|
|
778
787
|
)
|
|
779
788
|
}
|
|
@@ -796,7 +805,7 @@ async function handleVoucher(
|
|
|
796
805
|
minVoucherDelta,
|
|
797
806
|
challenge,
|
|
798
807
|
channel,
|
|
799
|
-
channelId
|
|
808
|
+
channelId,
|
|
800
809
|
voucher,
|
|
801
810
|
onChain,
|
|
802
811
|
methodDetails,
|
|
@@ -815,7 +824,8 @@ async function handleClose(
|
|
|
815
824
|
account?: viem_Account,
|
|
816
825
|
feePayer?: viem_Account,
|
|
817
826
|
): Promise<SessionReceipt> {
|
|
818
|
-
const
|
|
827
|
+
const channelId = ChannelStore.normalizeChannelId(payload.channelId)
|
|
828
|
+
const channel = await store.getChannel(channelId)
|
|
819
829
|
if (!channel) {
|
|
820
830
|
throw new ChannelNotFoundError({ reason: 'channel not found' })
|
|
821
831
|
}
|
|
@@ -823,13 +833,9 @@ async function handleClose(
|
|
|
823
833
|
throw new ChannelClosedError({ reason: 'channel is already finalized' })
|
|
824
834
|
}
|
|
825
835
|
|
|
826
|
-
const voucher = parseVoucherFromPayload(
|
|
827
|
-
payload.channelId,
|
|
828
|
-
payload.cumulativeAmount,
|
|
829
|
-
payload.signature,
|
|
830
|
-
)
|
|
836
|
+
const voucher = parseVoucherFromPayload(channelId, payload.cumulativeAmount, payload.signature)
|
|
831
837
|
|
|
832
|
-
const onChain = await getOnChainChannel(client, methodDetails.escrowContract,
|
|
838
|
+
const onChain = await getOnChainChannel(client, methodDetails.escrowContract, channelId)
|
|
833
839
|
|
|
834
840
|
if (onChain.finalized) {
|
|
835
841
|
throw new ChannelClosedError({ reason: 'channel is finalized on-chain' })
|
|
@@ -867,7 +873,7 @@ async function handleClose(
|
|
|
867
873
|
...(feePayer && account ? { feePayer, account } : { account }),
|
|
868
874
|
})
|
|
869
875
|
|
|
870
|
-
const updated = await store.updateChannel(
|
|
876
|
+
const updated = await store.updateChannel(channelId, (current) => {
|
|
871
877
|
if (!current) return null
|
|
872
878
|
const updateVoucher = voucher.cumulativeAmount > current.highestVoucherAmount
|
|
873
879
|
return {
|
|
@@ -883,7 +889,7 @@ async function handleClose(
|
|
|
883
889
|
|
|
884
890
|
return createSessionReceipt({
|
|
885
891
|
challengeId: challenge.id,
|
|
886
|
-
channelId:
|
|
892
|
+
channelId: updated?.channelId ?? channel.channelId,
|
|
887
893
|
acceptedCumulative: voucher.cumulativeAmount,
|
|
888
894
|
spent: updated?.spent ?? channel.spent,
|
|
889
895
|
units: updated?.units ?? channel.units,
|
|
@@ -73,8 +73,8 @@ const provider = Provider.create({
|
|
|
73
73
|
})
|
|
74
74
|
|
|
75
75
|
const button = document.createElement('button')
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
const buttonLabel = c.text.pay === 'Pay' ? 'Continue with' : c.text.pay
|
|
77
|
+
button.innerHTML = `${buttonLabel} <svg aria-label="Tempo" viewBox="0 0 107 25" role="img"><path d="M8.10464 23.7163H1.82475L7.64513 5.79356H0.201172L1.82475 0.540352H22.5637L20.9401 5.79356H13.8944L8.10464 23.7163Z"></path><path d="M31.474 23.7163H16.5861L24.0607 0.540352H38.8873L37.4782 4.95923H28.8701L27.3078 9.93433H35.6402L34.231 14.2914H25.8681L24.3057 19.2974H32.8525L31.474 23.7163Z"></path><path d="M38.2124 23.7163H33.2192L40.7244 0.540352H49.0567L48.781 13.0245L56.8989 0.540352H66.0277L58.5531 23.7163H52.3039L57.3584 7.86395L46.9736 23.7163H43.267L43.4201 7.80214L38.2124 23.7163Z"></path><path d="M73.057 4.83563L70.6369 12.3137H71.3108C72.8425 12.3137 74.1189 11.9532 75.14 11.2322C76.1612 10.4906 76.8249 9.43991 77.1312 8.08025C77.3967 6.90601 77.2538 6.07167 76.7023 5.57725C76.1509 5.08284 75.2319 4.83563 73.9453 4.83563H73.057ZM66.9915 23.7163H60.7116L68.1862 0.540352H75.814C77.5703 0.540352 79.0816 0.828764 80.3478 1.40559C81.6344 1.96181 82.5738 2.76524 83.166 3.81588C83.7787 4.84592 83.9829 6.05107 83.7787 7.43133C83.5132 9.2442 82.8189 10.8408 81.6956 12.221C80.5724 13.6013 79.1122 14.6725 77.315 15.4347C75.5383 16.1764 73.5471 16.5472 71.3415 16.5472H69.289L66.9915 23.7163Z"></path><path d="M98.747 22.233C96.664 23.4691 94.4481 24.0871 92.0996 24.0871H92.0383C89.9552 24.0871 88.1989 23.6236 86.7693 22.6965C85.3602 21.7489 84.3493 20.4717 83.7366 18.8648C83.1443 17.2579 83.0014 15.4966 83.3077 13.5807C83.6957 11.1704 84.5841 8.94549 85.9728 6.90601C87.3616 4.86653 89.0975 3.23906 91.1805 2.02361C93.2636 0.808164 95.4897 0.200439 97.8587 0.200439H97.9199C100.085 0.200439 101.872 0.663958 103.281 1.591C104.71 2.51803 105.701 3.78498 106.252 5.39185C106.824 6.97811 106.947 8.76008 106.62 10.7378C106.232 13.0657 105.343 15.2596 103.955 17.3197C102.566 19.3592 100.83 20.997 98.747 22.233ZM90.0777 18.2468C90.6292 19.2974 91.589 19.8227 92.9573 19.8227H93.0186C94.1418 19.8227 95.1833 19.4004 96.1432 18.5558C97.1235 17.6905 97.9506 16.5369 98.6245 15.0948C99.3189 13.6528 99.8294 12.0459 100.156 10.2742C100.463 8.54377 100.34 7.15322 99.7886 6.10257C99.2372 5.03133 98.2875 4.49571 96.9397 4.49571H96.8784C95.8369 4.49571 94.826 4.92833 93.8457 5.79356C92.8858 6.6588 92.0485 7.82274 91.3337 9.2854C90.6189 10.7481 90.0982 12.3343 89.7714 14.0442C89.4446 15.7747 89.5468 17.1755 90.0777 18.2468Z"></path></svg>`
|
|
78
78
|
button.onclick = async () => {
|
|
79
79
|
try {
|
|
80
80
|
c.error()
|